Merge pull request #7223 from eelhazati/master
BAEL-2887 - OAuth2 framework implementation
This commit is contained in:
commit
4cf05a5965
|
@ -0,0 +1,72 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>oauth2-authorization-server</artifactId>
|
||||
<packaging>war</packaging>
|
||||
|
||||
<parent>
|
||||
<groupId>com.baeldung.oauth2</groupId>
|
||||
<artifactId>oauth2-framework-impl</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<h2.version>1.4.199</h2.version>
|
||||
<httpPort>9080</httpPort>
|
||||
<httpsPort>9443</httpsPort>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.nimbusds</groupId>
|
||||
<artifactId>nimbus-jose-jwt</artifactId>
|
||||
<version>7.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15on</artifactId>
|
||||
<version>1.62</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcpkix-jdk15on</artifactId>
|
||||
<version>1.62</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>net.wasdev.wlp.maven.plugins</groupId>
|
||||
<artifactId>liberty-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-h2-dependency</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>copy</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<version>${h2.version}</version>
|
||||
<type>jar</type>
|
||||
<outputDirectory>${project.build.directory}/liberty/wlp/usr/shared/resources/
|
||||
</outputDirectory>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
|
@ -0,0 +1,8 @@
|
|||
package com.baeldung.oauth2.authorization.server;
|
||||
|
||||
import javax.ws.rs.ApplicationPath;
|
||||
import javax.ws.rs.core.Application;
|
||||
|
||||
@ApplicationPath("/")
|
||||
public class OAuth2ServerApplication extends Application {
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package com.baeldung.oauth2.authorization.server;
|
||||
|
||||
import java.net.URI;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import static java.lang.Thread.currentThread;
|
||||
|
||||
|
||||
public class PEMKeyUtils {
|
||||
|
||||
public static String readKeyAsString(String keyLocation) throws Exception {
|
||||
URI uri = currentThread().getContextClassLoader().getResource(keyLocation).toURI();
|
||||
byte[] byteArray = Files.readAllBytes(Paths.get(uri));
|
||||
return new String(byteArray);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,185 @@
|
|||
package com.baeldung.oauth2.authorization.server.api;
|
||||
|
||||
import com.baeldung.oauth2.authorization.server.handler.AuthorizationGrantTypeHandler;
|
||||
import com.baeldung.oauth2.authorization.server.model.AppDataRepository;
|
||||
import com.baeldung.oauth2.authorization.server.model.AuthorizationCode;
|
||||
import com.baeldung.oauth2.authorization.server.model.Client;
|
||||
import com.baeldung.oauth2.authorization.server.model.User;
|
||||
|
||||
import javax.annotation.security.RolesAllowed;
|
||||
import javax.enterprise.context.RequestScoped;
|
||||
import javax.enterprise.inject.Instance;
|
||||
import javax.enterprise.inject.literal.NamedLiteral;
|
||||
import javax.inject.Inject;
|
||||
import javax.json.JsonObject;
|
||||
import javax.security.enterprise.SecurityContext;
|
||||
import javax.security.enterprise.authentication.mechanism.http.FormAuthenticationMechanismDefinition;
|
||||
import javax.security.enterprise.authentication.mechanism.http.LoginToContinue;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.ws.rs.*;
|
||||
import javax.ws.rs.core.*;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.security.Principal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
|
||||
@FormAuthenticationMechanismDefinition(
|
||||
loginToContinue = @LoginToContinue(loginPage = "/login.jsp", errorPage = "/login.jsp")
|
||||
)
|
||||
@RolesAllowed("USER")
|
||||
@RequestScoped
|
||||
@Path("authorize")
|
||||
public class AuthorizationEndpoint {
|
||||
|
||||
@Inject
|
||||
private SecurityContext securityContext;
|
||||
|
||||
@Inject
|
||||
private AppDataRepository appDataRepository;
|
||||
|
||||
@Inject
|
||||
Instance<AuthorizationGrantTypeHandler> authorizationGrantTypeHandlers;
|
||||
|
||||
@GET
|
||||
@Produces(MediaType.TEXT_HTML)
|
||||
public Response doGet(@Context HttpServletRequest request,
|
||||
@Context HttpServletResponse response,
|
||||
@Context UriInfo uriInfo) throws ServletException, IOException {
|
||||
MultivaluedMap<String, String> params = uriInfo.getQueryParameters();
|
||||
Principal principal = securityContext.getCallerPrincipal();
|
||||
|
||||
//error about redirect_uri && client_id ==> forward user, thus to error.jsp.
|
||||
//otherwise ==> sendRedirect redirect_uri?error=error&error_description=error_description
|
||||
//1. client_id
|
||||
String clientId = params.getFirst("client_id");
|
||||
if (clientId == null || clientId.isEmpty()) {
|
||||
return informUserAboutError(request, response, "Invalid client_id :" + clientId);
|
||||
}
|
||||
Client client = appDataRepository.getClient(clientId);
|
||||
if (client == null) {
|
||||
return informUserAboutError(request, response, "Invalid client_id :" + clientId);
|
||||
}
|
||||
//2. Client Authorized Grant Type
|
||||
String clientError = "";
|
||||
if (client.getAuthorizedGrantTypes() != null && !client.getAuthorizedGrantTypes().contains("authorization_code")) {
|
||||
return informUserAboutError(request, response, "Authorization Grant type, authorization_code, is not allowed for this client :" + clientId);
|
||||
}
|
||||
|
||||
//3. redirectUri
|
||||
String redirectUri = params.getFirst("redirect_uri");
|
||||
if (client.getRedirectUri() != null && !client.getRedirectUri().isEmpty()) {
|
||||
if (redirectUri != null && !redirectUri.isEmpty() && !client.getRedirectUri().equals(redirectUri)) {
|
||||
//sould be in the client.redirectUri
|
||||
return informUserAboutError(request, response, "redirect_uri is pre-registred and should match");
|
||||
}
|
||||
redirectUri = client.getRedirectUri();
|
||||
params.putSingle("resolved_redirect_uri", redirectUri);
|
||||
} else {
|
||||
if (redirectUri == null || redirectUri.isEmpty()) {
|
||||
return informUserAboutError(request, response, "redirect_uri is not pre-registred and should be provided");
|
||||
}
|
||||
params.putSingle("resolved_redirect_uri", redirectUri);
|
||||
}
|
||||
request.setAttribute("client", client);
|
||||
|
||||
//4. response_type
|
||||
String responseType = params.getFirst("response_type");
|
||||
if (!"code".equals(responseType) && !"token".equals(responseType)) {
|
||||
//error = "invalid_grant :" + responseType + ", response_type params should be code or token:";
|
||||
//return informUserAboutError(error);
|
||||
}
|
||||
|
||||
//Save params in session
|
||||
request.getSession().setAttribute("ORIGINAL_PARAMS", params);
|
||||
|
||||
//4.scope: Optional
|
||||
String requestedScope = request.getParameter("scope");
|
||||
if (requestedScope == null || requestedScope.isEmpty()) {
|
||||
requestedScope = client.getScope();
|
||||
}
|
||||
User user = appDataRepository.getUser(principal.getName());
|
||||
String allowedScopes = checkUserScopes(user.getScopes(), requestedScope);
|
||||
request.setAttribute("scopes", allowedScopes);
|
||||
|
||||
request.getRequestDispatcher("/authorize.jsp").forward(request, response);
|
||||
return null;
|
||||
}
|
||||
|
||||
@POST
|
||||
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
|
||||
@Produces(MediaType.TEXT_HTML)
|
||||
public Response doPost(@Context HttpServletRequest request,
|
||||
@Context HttpServletResponse response,
|
||||
MultivaluedMap<String, String> params) throws Exception {
|
||||
MultivaluedMap<String, String> originalParams = (MultivaluedMap<String, String>) request.getSession().getAttribute("ORIGINAL_PARAMS");
|
||||
if (originalParams == null) {
|
||||
return informUserAboutError(request, response, "No pending authorization request.");
|
||||
}
|
||||
String redirectUri = originalParams.getFirst("resolved_redirect_uri");
|
||||
StringBuilder sb = new StringBuilder(redirectUri);
|
||||
|
||||
String approbationStatus = params.getFirst("approbation_status");
|
||||
if ("NO".equals(approbationStatus)) {
|
||||
URI location = UriBuilder.fromUri(sb.toString())
|
||||
.queryParam("error", "User doesn't approved the request.")
|
||||
.queryParam("error_description", "User doesn't approved the request.")
|
||||
.build();
|
||||
return Response.seeOther(location).build();
|
||||
}
|
||||
//==> YES
|
||||
List<String> approvedScopes = params.get("scope");
|
||||
if (approvedScopes == null || approvedScopes.isEmpty()) {
|
||||
URI location = UriBuilder.fromUri(sb.toString())
|
||||
.queryParam("error", "User doesn't approved the request.")
|
||||
.queryParam("error_description", "User doesn't approved the request.")
|
||||
.build();
|
||||
return Response.seeOther(location).build();
|
||||
}
|
||||
|
||||
String responseType = originalParams.getFirst("response_type");
|
||||
String clientId = originalParams.getFirst("client_id");
|
||||
if ("code".equals(responseType)) {
|
||||
String userId = securityContext.getCallerPrincipal().getName();
|
||||
AuthorizationCode authorizationCode = new AuthorizationCode();
|
||||
authorizationCode.setClientId(clientId);
|
||||
authorizationCode.setUserId(userId);
|
||||
authorizationCode.setApprovedScopes(String.join(" ", approvedScopes));
|
||||
authorizationCode.setExpirationDate(LocalDateTime.now().plusMinutes(10));
|
||||
authorizationCode.setRedirectUri(redirectUri);
|
||||
appDataRepository.save(authorizationCode);
|
||||
String code = authorizationCode.getCode();
|
||||
sb.append("?code=").append(code);
|
||||
} else {
|
||||
//Implicit: responseType=token
|
||||
AuthorizationGrantTypeHandler authorizationGrantTypeHandler = authorizationGrantTypeHandlers.select(NamedLiteral.of("implicit")).get();
|
||||
JsonObject tokenResponse = authorizationGrantTypeHandler.createAccessToken(clientId, params);
|
||||
sb.append("#access_token=").append(tokenResponse.getString("access_token"))
|
||||
.append("&token_type=").append(tokenResponse.getString("token_type"))
|
||||
.append("&scope=").append(tokenResponse.getString("scope"));
|
||||
}
|
||||
String state = originalParams.getFirst("state");
|
||||
if (state != null) {
|
||||
sb.append("&state=").append(state);
|
||||
}
|
||||
return Response.seeOther(UriBuilder.fromUri(sb.toString()).build()).build();
|
||||
}
|
||||
|
||||
private String checkUserScopes(String userScopes, String requestedScope) {
|
||||
Set<String> allowedScopes = new LinkedHashSet<>();
|
||||
Set<String> rScopes = new HashSet(Arrays.asList(requestedScope.split(" ")));
|
||||
Set<String> uScopes = new HashSet(Arrays.asList(userScopes.split(" ")));
|
||||
for (String scope : uScopes) {
|
||||
if (rScopes.contains(scope)) allowedScopes.add(scope);
|
||||
}
|
||||
return String.join(" ", allowedScopes);
|
||||
}
|
||||
|
||||
private Response informUserAboutError(HttpServletRequest request, HttpServletResponse response, String error) throws ServletException, IOException {
|
||||
request.setAttribute("error", error);
|
||||
request.getRequestDispatcher("/error.jsp").forward(request, response);
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package com.baeldung.oauth2.authorization.server.api;
|
||||
|
||||
import com.baeldung.oauth2.authorization.server.PEMKeyUtils;
|
||||
import com.nimbusds.jose.jwk.JWK;
|
||||
import org.eclipse.microprofile.config.Config;
|
||||
|
||||
import javax.enterprise.context.ApplicationScoped;
|
||||
import javax.inject.Inject;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.util.Arrays;
|
||||
|
||||
@Path("jwk")
|
||||
@ApplicationScoped
|
||||
public class JWKEndpoint {
|
||||
|
||||
@Inject
|
||||
private Config config;
|
||||
|
||||
@GET
|
||||
public Response getKey(@QueryParam("format") String format) throws Exception {
|
||||
if (format != null && !Arrays.asList("jwk", "pem").contains(format)) {
|
||||
return Response.status(Response.Status.BAD_REQUEST).entity("Public Key Format should be : jwk or pem").build();
|
||||
}
|
||||
String verificationkey = config.getValue("verificationkey", String.class);
|
||||
String pemEncodedRSAPublicKey = PEMKeyUtils.readKeyAsString(verificationkey);
|
||||
if (format == null || format.equals("jwk")) {
|
||||
JWK jwk = JWK.parseFromPEMEncodedObjects(pemEncodedRSAPublicKey);
|
||||
return Response.ok(jwk.toJSONString()).type(MediaType.APPLICATION_JSON).build();
|
||||
} else if (format.equals("pem")) {
|
||||
return Response.ok(pemEncodedRSAPublicKey).build();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
package com.baeldung.oauth2.authorization.server.api;
|
||||
|
||||
import com.baeldung.oauth2.authorization.server.handler.AuthorizationGrantTypeHandler;
|
||||
import com.baeldung.oauth2.authorization.server.model.AppDataRepository;
|
||||
import com.baeldung.oauth2.authorization.server.model.Client;
|
||||
import com.nimbusds.jose.JOSEException;
|
||||
|
||||
import javax.enterprise.inject.Instance;
|
||||
import javax.enterprise.inject.literal.NamedLiteral;
|
||||
import javax.inject.Inject;
|
||||
import javax.json.Json;
|
||||
import javax.json.JsonObject;
|
||||
import javax.ws.rs.*;
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.util.Base64;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@Path("token")
|
||||
public class TokenEndpoint {
|
||||
|
||||
List<String> supportedGrantTypes = Collections.singletonList("authorization_code");
|
||||
|
||||
@Inject
|
||||
private AppDataRepository appDataRepository;
|
||||
|
||||
@Inject
|
||||
Instance<AuthorizationGrantTypeHandler> authorizationGrantTypeHandlers;
|
||||
|
||||
@POST
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
|
||||
public Response token(MultivaluedMap<String, String> params,
|
||||
@HeaderParam(HttpHeaders.AUTHORIZATION) String authHeader) throws JOSEException {
|
||||
|
||||
//Check grant_type params
|
||||
String grantType = params.getFirst("grant_type");
|
||||
Objects.requireNonNull(grantType, "grant_type params is required");
|
||||
if (!supportedGrantTypes.contains(grantType)) {
|
||||
JsonObject error = Json.createObjectBuilder()
|
||||
.add("error", "unsupported_grant_type")
|
||||
.add("error_description", "grant type should be one of :" + supportedGrantTypes)
|
||||
.build();
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(error).build();
|
||||
|
||||
}
|
||||
|
||||
//Client Authentication
|
||||
String[] clientCredentials = extract(authHeader);
|
||||
String clientId = clientCredentials[0];
|
||||
String clientSecret = clientCredentials[1];
|
||||
Client client = appDataRepository.getClient(clientId);
|
||||
if (client == null || clientSecret == null || !clientSecret.equals(client.getClientSecret())) {
|
||||
JsonObject error = Json.createObjectBuilder()
|
||||
.add("error", "invalid_client")
|
||||
.build();
|
||||
return Response.status(Response.Status.UNAUTHORIZED)
|
||||
.entity(error).build();
|
||||
}
|
||||
|
||||
AuthorizationGrantTypeHandler authorizationGrantTypeHandler = authorizationGrantTypeHandlers.select(NamedLiteral.of(grantType)).get();
|
||||
JsonObject tokenResponse = null;
|
||||
try {
|
||||
tokenResponse = authorizationGrantTypeHandler.createAccessToken(clientId, params);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return Response.ok(tokenResponse)
|
||||
.header("Cache-Control", "no-store")
|
||||
.header("Pragma", "no-cache")
|
||||
.build();
|
||||
}
|
||||
|
||||
private String[] extract(String authHeader) {
|
||||
if (authHeader != null && authHeader.startsWith("Basic ")) {
|
||||
return new String(Base64.getDecoder().decode(authHeader.substring(6))).split(":");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
package com.baeldung.oauth2.authorization.server.handler;
|
||||
|
||||
import com.baeldung.oauth2.authorization.server.PEMKeyUtils;
|
||||
import com.baeldung.oauth2.authorization.server.model.AuthorizationCode;
|
||||
import com.nimbusds.jose.JOSEObjectType;
|
||||
import com.nimbusds.jose.JWSAlgorithm;
|
||||
import com.nimbusds.jose.JWSHeader;
|
||||
import com.nimbusds.jose.crypto.RSASSASigner;
|
||||
import com.nimbusds.jose.jwk.JWK;
|
||||
import com.nimbusds.jose.jwk.RSAKey;
|
||||
import com.nimbusds.jwt.JWTClaimsSet;
|
||||
import com.nimbusds.jwt.SignedJWT;
|
||||
import org.eclipse.microprofile.config.Config;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.json.Json;
|
||||
import javax.json.JsonObject;
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceContext;
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
@Named("authorization_code")
|
||||
public class AuthorizationCodeGrantTypeHandler implements AuthorizationGrantTypeHandler {
|
||||
|
||||
@PersistenceContext
|
||||
private EntityManager entityManager;
|
||||
|
||||
@Inject
|
||||
private Config config;
|
||||
|
||||
@Override
|
||||
public JsonObject createAccessToken(String clientId, MultivaluedMap<String, String> params) throws Exception {
|
||||
//1. code is required
|
||||
String code = params.getFirst("code");
|
||||
if (code == null || "".equals(code)) {
|
||||
throw new WebApplicationException("invalid_grant");
|
||||
}
|
||||
AuthorizationCode authorizationCode = entityManager.find(AuthorizationCode.class, code);
|
||||
if (!authorizationCode.getExpirationDate().isAfter(LocalDateTime.now())) {
|
||||
throw new WebApplicationException("code Expired !");
|
||||
}
|
||||
String redirectUri = params.getFirst("redirect_uri");
|
||||
//redirecturi match
|
||||
if (authorizationCode.getRedirectUri() != null && !authorizationCode.getRedirectUri().equals(redirectUri)) {
|
||||
//redirectUri params should be the same as the requested redirectUri.
|
||||
throw new WebApplicationException("invalid_grant");
|
||||
}
|
||||
//client match
|
||||
if (!clientId.equals(authorizationCode.getClientId())) {
|
||||
throw new WebApplicationException("invalid_grant");
|
||||
}
|
||||
|
||||
//JWT Header
|
||||
JWSHeader jwsHeader = new JWSHeader.Builder(JWSAlgorithm.RS256).type(JOSEObjectType.JWT).build();
|
||||
|
||||
Instant now = Instant.now();
|
||||
Long expiresInMin = 30L;
|
||||
Date expiresIn = Date.from(now.plus(expiresInMin, ChronoUnit.MINUTES));
|
||||
|
||||
//3. JWT Payload or claims
|
||||
JWTClaimsSet jwtClaims = new JWTClaimsSet.Builder()
|
||||
.issuer("http://localhost:9080")
|
||||
.subject(authorizationCode.getUserId())
|
||||
.claim("upn", authorizationCode.getUserId())
|
||||
.audience("http://localhost:9280")
|
||||
.claim("scope", authorizationCode.getApprovedScopes())
|
||||
.claim("groups", Arrays.asList(authorizationCode.getApprovedScopes().split(" ")))
|
||||
.expirationTime(expiresIn) // expires in 30 minutes
|
||||
.notBeforeTime(Date.from(now))
|
||||
.issueTime(Date.from(now))
|
||||
.jwtID(UUID.randomUUID().toString())
|
||||
.build();
|
||||
SignedJWT signedJWT = new SignedJWT(jwsHeader, jwtClaims);
|
||||
|
||||
//4. Signing
|
||||
String signingkey = config.getValue("signingkey", String.class);
|
||||
String pemEncodedRSAPrivateKey = PEMKeyUtils.readKeyAsString(signingkey);
|
||||
RSAKey rsaKey = (RSAKey) JWK.parseFromPEMEncodedObjects(pemEncodedRSAPrivateKey);
|
||||
signedJWT.sign(new RSASSASigner(rsaKey.toRSAPrivateKey()));
|
||||
|
||||
//5. Finally the JWT access token
|
||||
String accessToken = signedJWT.serialize();
|
||||
|
||||
return Json.createObjectBuilder()
|
||||
.add("token_type", "Bearer")
|
||||
.add("access_token", accessToken)
|
||||
.add("expires_in", expiresInMin * 60)
|
||||
.add("scope", authorizationCode.getApprovedScopes())
|
||||
.build();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package com.baeldung.oauth2.authorization.server.handler;
|
||||
|
||||
import javax.json.JsonObject;
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
|
||||
public interface AuthorizationGrantTypeHandler {
|
||||
JsonObject createAccessToken(String clientId, MultivaluedMap<String, String> params) throws Exception;
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package com.baeldung.oauth2.authorization.server.model;
|
||||
|
||||
import javax.enterprise.context.ApplicationScoped;
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceContext;
|
||||
import javax.transaction.Transactional;
|
||||
|
||||
@ApplicationScoped
|
||||
public class AppDataRepository {
|
||||
|
||||
@PersistenceContext
|
||||
private EntityManager entityManager;
|
||||
|
||||
public Client getClient(String clientId) {
|
||||
return entityManager.find(Client.class, clientId);
|
||||
}
|
||||
|
||||
public User getUser(String userId) {
|
||||
return entityManager.find(User.class, userId);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public AuthorizationCode save(AuthorizationCode authorizationCode) {
|
||||
entityManager.persist(authorizationCode);
|
||||
return authorizationCode;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
package com.baeldung.oauth2.authorization.server.model;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Entity
|
||||
@Table(name = "authorization_code")
|
||||
public class AuthorizationCode {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
@Column(name = "code")
|
||||
private String code;
|
||||
@Column(name = "client_id")
|
||||
private String clientId;
|
||||
@Column(name = "user_id")
|
||||
private String userId;
|
||||
@Column(name = "approved_scopes")
|
||||
private String approvedScopes;
|
||||
|
||||
@Column(name = "redirect_uri")
|
||||
private String redirectUri;
|
||||
|
||||
@Column(name = "expiration_date")
|
||||
private LocalDateTime expirationDate;
|
||||
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(String username) {
|
||||
this.userId = username;
|
||||
}
|
||||
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public void setClientId(String clientId) {
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getApprovedScopes() {
|
||||
return approvedScopes;
|
||||
}
|
||||
|
||||
public void setApprovedScopes(String approvedScopes) {
|
||||
this.approvedScopes = approvedScopes;
|
||||
}
|
||||
|
||||
public String getRedirectUri() {
|
||||
return redirectUri;
|
||||
}
|
||||
|
||||
public void setRedirectUri(String redirectUri) {
|
||||
this.redirectUri = redirectUri;
|
||||
}
|
||||
|
||||
public LocalDateTime getExpirationDate() {
|
||||
return expirationDate;
|
||||
}
|
||||
|
||||
public void setExpirationDate(LocalDateTime expirationDate) {
|
||||
this.expirationDate = expirationDate;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
package com.baeldung.oauth2.authorization.server.model;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "clients")
|
||||
public class Client {
|
||||
@Id
|
||||
@Column(name = "client_id")
|
||||
private String clientId;
|
||||
@Column(name = "client_secret")
|
||||
private String clientSecret;
|
||||
@Column(name = "redirect_uri")
|
||||
private String redirectUri;
|
||||
@Column(name = "scope")
|
||||
private String scope;
|
||||
@Column(name = "authorized_grant_types")
|
||||
private String authorizedGrantTypes;
|
||||
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public void setClientId(String clientId) {
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
public String getClientSecret() {
|
||||
return clientSecret;
|
||||
}
|
||||
|
||||
public void setClientSecret(String clientSecret) {
|
||||
this.clientSecret = clientSecret;
|
||||
}
|
||||
|
||||
public String getRedirectUri() {
|
||||
return redirectUri;
|
||||
}
|
||||
|
||||
public void setRedirectUri(String redirectUri) {
|
||||
this.redirectUri = redirectUri;
|
||||
}
|
||||
|
||||
public String getScope() {
|
||||
return scope;
|
||||
}
|
||||
|
||||
public void setScope(String scope) {
|
||||
this.scope = scope;
|
||||
}
|
||||
|
||||
public String getAuthorizedGrantTypes() {
|
||||
return authorizedGrantTypes;
|
||||
}
|
||||
|
||||
public void setAuthorizedGrantTypes(String authorizedGrantTypes) {
|
||||
this.authorizedGrantTypes = authorizedGrantTypes;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package com.baeldung.oauth2.authorization.server.model;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
import java.security.Principal;
|
||||
|
||||
@Entity
|
||||
@Table(name = "users")
|
||||
public class User implements Principal {
|
||||
@Id
|
||||
@Column(name = "user_id")
|
||||
private String userId;
|
||||
@Column(name = "password")
|
||||
private String password;
|
||||
@Column(name = "roles")
|
||||
private String roles;
|
||||
@Column(name = "scopes")
|
||||
private String scopes;
|
||||
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(String username) {
|
||||
this.userId = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getRoles() {
|
||||
return roles;
|
||||
}
|
||||
|
||||
public void setRoles(String roles) {
|
||||
this.roles = roles;
|
||||
}
|
||||
|
||||
public String getScopes() {
|
||||
return scopes;
|
||||
}
|
||||
|
||||
public void setScopes(String scopes) {
|
||||
this.scopes = scopes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return getUserId();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package com.baeldung.oauth2.authorization.server.security;
|
||||
|
||||
import com.baeldung.oauth2.authorization.server.model.AppDataRepository;
|
||||
import com.baeldung.oauth2.authorization.server.model.User;
|
||||
|
||||
import javax.enterprise.context.ApplicationScoped;
|
||||
import javax.inject.Inject;
|
||||
import javax.security.enterprise.credential.Credential;
|
||||
import javax.security.enterprise.credential.UsernamePasswordCredential;
|
||||
import javax.security.enterprise.identitystore.CredentialValidationResult;
|
||||
import javax.security.enterprise.identitystore.IdentityStore;
|
||||
import javax.transaction.Transactional;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
|
||||
import static javax.security.enterprise.identitystore.CredentialValidationResult.INVALID_RESULT;
|
||||
|
||||
@ApplicationScoped
|
||||
@Transactional
|
||||
public class UserIdentityStore implements IdentityStore {
|
||||
|
||||
@Inject
|
||||
private AppDataRepository appDataRepository;
|
||||
|
||||
@Override
|
||||
public CredentialValidationResult validate(Credential credential) {
|
||||
UsernamePasswordCredential usernamePasswordCredential = (UsernamePasswordCredential) credential;
|
||||
String userId = usernamePasswordCredential.getCaller();
|
||||
User user = appDataRepository.getUser(userId);
|
||||
Objects.requireNonNull(user, "User should be not null");
|
||||
if (usernamePasswordCredential.getPasswordAsString().equals(user.getPassword())) {
|
||||
return new CredentialValidationResult(userId, new HashSet<>(Arrays.asList(user.getRoles().split(","))));
|
||||
}
|
||||
return INVALID_RESULT;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,209 @@
|
|||
//package com.baeldung.security.oauth2.server.web;
|
||||
//
|
||||
//import AuthorizationCode;
|
||||
//import Client;
|
||||
//import User;
|
||||
//import com.baeldung.security.oauth2.server.service.AuthCodeService;
|
||||
//
|
||||
//import javax.ejb.EJB;
|
||||
//import javax.enterprise.context.RequestScoped;
|
||||
//import javax.inject.Inject;
|
||||
//import javax.persistence.EntityManager;
|
||||
//import javax.persistence.PersistenceContext;
|
||||
//import javax.security.enterprise.SecurityContext;
|
||||
//import javax.security.enterprise.authentication.mechanism.http.FormAuthenticationMechanismDefinition;
|
||||
//import javax.security.enterprise.authentication.mechanism.http.LoginToContinue;
|
||||
//import javax.servlet.ServletException;
|
||||
//import javax.servlet.annotation.HttpConstraint;
|
||||
//import javax.servlet.annotation.ServletSecurity;
|
||||
//import javax.servlet.annotation.WebServlet;
|
||||
//import javax.servlet.http.HttpServlet;
|
||||
//import javax.servlet.http.HttpServletRequest;
|
||||
//import javax.servlet.http.HttpServletResponse;
|
||||
//import java.io.IOException;
|
||||
//import java.security.Principal;
|
||||
//import java.util.*;
|
||||
//
|
||||
///**
|
||||
// * 1. GET http://localhost:8080/app/ (302)
|
||||
// * 2. GET http://localhost:8080/uaa/authorize?client_id=app&redirect_uri=http://localhost:8080/app/&response_type=code&state=A123 (302)
|
||||
// * 3. GET http://localhost:8080/uaa/login (200) with initial request as hidden input
|
||||
// * 4. POST http://localhost:8080/uaa/login (username, password, initial client request) (302)
|
||||
// * 5. GET http://localhost:8080/uaa/authorize?client_id=app&redirect_uri=http://localhost:8080/app/&response_type=code&state=A123 (200)
|
||||
// * 7. POST http://localhost:8080/uaa/authorize?client_id=app&redirect_uri=http://localhost:8080/app/&response_type=code&state=A123 (302)
|
||||
// * 8. GET http://localhost:8080/app/?code=rkWijq06mL&state=A123 (200)
|
||||
// */
|
||||
///*
|
||||
//
|
||||
//Query Params:
|
||||
// client_id: app
|
||||
// redirect_uri: http://localhost:8080/app/
|
||||
// response_type: code
|
||||
// state: A123
|
||||
//
|
||||
// ==> GET user login WITH client request as hidden input:
|
||||
// <input name="form_redirect_uri"
|
||||
// type="hidden"
|
||||
// value="http://localhost:8080/uaa/oauth/authorize?client_id=app&redirect_uri=http://localhost:8080/app/&response_type=code&state=A123"/>
|
||||
//
|
||||
// ==> After user login ==> Initial client request
|
||||
// ==> gen code
|
||||
// == redirect to redirect uri + params code & state : 302, location : http://localhost:8080/app/?code=w6A0YQFzzg&state=A123
|
||||
//*/
|
||||
//
|
||||
////authorize?client_id=app&redirect_uri=http://localhost:8080/app/&response_type=code&state=A123
|
||||
////http://localhost:9080/authorize?response_type=code&client_id=client_id_1&redirect_uri=http://localhost:9080/app&state=A123
|
||||
//
|
||||
////@RequestScoped
|
||||
//@FormAuthenticationMechanismDefinition(
|
||||
// loginToContinue = @LoginToContinue(
|
||||
// loginPage = "/login-servlet",
|
||||
// errorPage = "/login-error-servlet"
|
||||
// )
|
||||
//)
|
||||
//@WebServlet({"/authorize"})
|
||||
//@ServletSecurity(@HttpConstraint(rolesAllowed = "user"))
|
||||
////@Stateless
|
||||
//@RequestScoped
|
||||
//public class AuthorizationEndpoint extends HttpServlet {
|
||||
//
|
||||
// private static final List<String> authorizedResponseTypes = Arrays.asList("code", "token");
|
||||
//
|
||||
// @Inject
|
||||
// private SecurityContext securityContext;
|
||||
//
|
||||
// @PersistenceContext(name = "jpa-oauth2-pu")
|
||||
// private EntityManager entityManager;
|
||||
//
|
||||
// @EJB
|
||||
// private AuthCodeService authCodeService;
|
||||
//
|
||||
// //HTTP GET IS A MUST, POST IS OPTIONAL
|
||||
// @Override
|
||||
// protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
//
|
||||
// String error = "";
|
||||
//
|
||||
// //1. User Authentication
|
||||
// Principal principal = securityContext.getCallerPrincipal();
|
||||
//
|
||||
// //2. Check for a valid client_id
|
||||
// String clientId = request.getParameter("client_id");
|
||||
// if (clientId == null) {
|
||||
// request.setAttribute("error", "The client " + clientId + " doesn't exist.");
|
||||
// }
|
||||
// request.setAttribute("clientId", clientId);
|
||||
// Client client = entityManager.find(Client.class, clientId);
|
||||
// if (client == null) {
|
||||
// request.setAttribute("error", "The client " + clientId + " doesn't exist.");
|
||||
// }
|
||||
//
|
||||
// //3. check for a valid response_type
|
||||
// String responseType = request.getParameter("response_type");
|
||||
// if (!authorizedResponseTypes.contains(responseType)) {
|
||||
// error = "invalid_grant :" + responseType + ", response_type params should be one of :" + authorizedResponseTypes;
|
||||
// request.setAttribute("error", error);
|
||||
// request.getRequestDispatcher("/error.jsp")
|
||||
// .forward(request, response);
|
||||
// }
|
||||
//
|
||||
// //4. Optional redirect_uri, if provided should match
|
||||
// String redirectUri = request.getParameter("redirect_uri");
|
||||
// checkRedirectUri(client, redirectUri);
|
||||
//
|
||||
// //save params
|
||||
// String currentUri = request.getRequestURI();
|
||||
// request.setAttribute("post_redirect_uri", currentUri);
|
||||
//
|
||||
// String state = request.getParameter("state");
|
||||
// Map<String, String> requestMap = new HashMap<>();
|
||||
// requestMap.put("response_type", responseType);
|
||||
// requestMap.put("client_id", clientId);
|
||||
// requestMap.put("redirect_uri", redirectUri);
|
||||
// requestMap.put("state", state);
|
||||
// request.setAttribute("requestMap", requestMap);
|
||||
//
|
||||
// //5.scope: Optional
|
||||
// String requestedScope = request.getParameter("scope");
|
||||
// if (requestedScope.isEmpty()) {
|
||||
// requestedScope = client.getScope();
|
||||
// }
|
||||
// //requestedScope should be a subset of the client scope: clientScopes.containsAll(requestedScopes)
|
||||
// //checkRequestedScope(requestedScope, client.getScope());
|
||||
//
|
||||
// //sub set of user scope
|
||||
// //allowed scope by the user
|
||||
//
|
||||
// User user = entityManager.find(User.class, principal.getName());
|
||||
// request.setAttribute("scopes", requestedScope);
|
||||
//
|
||||
//
|
||||
// forward("/authorize.jsp", request, response);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
// String clientId = request.getParameter("client_id");
|
||||
//
|
||||
// String responseType = request.getParameter("response_type");
|
||||
// if (!authorizedResponseTypes.contains(responseType)) {
|
||||
// String error = "invalid_grant :" + responseType + ", response_type params should be one of :" + authorizedResponseTypes;
|
||||
// request.setAttribute("error", error);
|
||||
// forward("/error.jsp", request, response);
|
||||
// }
|
||||
//
|
||||
// Client client = entityManager.find(Client.class, clientId);
|
||||
// Objects.requireNonNull(client);
|
||||
//
|
||||
// String userId = securityContext.getCallerPrincipal().getName();
|
||||
// AuthorizationCode authorizationCode = new AuthorizationCode();
|
||||
// authorizationCode.setClientId(clientId);
|
||||
// authorizationCode.setUserId(userId);
|
||||
// String redirectUri = request.getParameter("redirect_uri");
|
||||
// authorizationCode.setRedirectUri(redirectUri);
|
||||
//
|
||||
// redirectUri = checkRedirectUri(client, redirectUri);
|
||||
//
|
||||
// String[] scope = request.getParameterValues("scope");
|
||||
// if (scope == null) {
|
||||
// request.setAttribute("error", "User doesn't approved any scope");
|
||||
// forward("/error.jsp", request, response);
|
||||
// }
|
||||
//
|
||||
// String approvedScopes = String.join(" ", scope);
|
||||
// authorizationCode.setApprovedScopes(approvedScopes);
|
||||
//
|
||||
// //entityManager.persist(authorizationCode);
|
||||
// authCodeService.save(authorizationCode);
|
||||
// String code = authorizationCode.getCode();
|
||||
//
|
||||
// StringBuilder sb = new StringBuilder(redirectUri);
|
||||
// sb.append("?code=").append(code);
|
||||
//
|
||||
// //If the client send a state, Send it back
|
||||
// String state = request.getParameter("state");
|
||||
// if (state != null) {
|
||||
// sb.append("&state=").append(state);
|
||||
// }
|
||||
// response.sendRedirect(sb.toString());
|
||||
// }
|
||||
//
|
||||
// private String checkRedirectUri(Client client, String redirectUri) {
|
||||
// //redirect uri
|
||||
// if (redirectUri == null) {
|
||||
// //erreur: param redirect_uri && client redirect_uri don't match.
|
||||
// redirectUri = client.getRedirectUri();
|
||||
// if (redirectUri == null) {
|
||||
// throw new IllegalStateException("redirectUri shloud be not null, unless a registred client have a redirect_uri.");
|
||||
// }
|
||||
// } else if (!redirectUri.equals(client.getRedirectUri())) {
|
||||
// throw new IllegalStateException("request redirectUri and client registred redirect_uri should match.");
|
||||
// }
|
||||
// return redirectUri;
|
||||
// }
|
||||
//
|
||||
// private void forward(String path, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
// request.getRequestDispatcher(path)
|
||||
// .forward(request, response);
|
||||
// }
|
||||
//}
|
|
@ -0,0 +1,70 @@
|
|||
//package com.baeldung.security.oauth2.server.web;
|
||||
//
|
||||
//import AuthorizationGrantTypeHandler;
|
||||
//import TokenResponse;
|
||||
//import com.baeldung.security.oauth2.server.security.Authenticated;
|
||||
//import com.nimbusds.jose.JOSEException;
|
||||
//
|
||||
//import javax.enterprise.inject.Instance;
|
||||
//import javax.enterprise.inject.literal.NamedLiteral;
|
||||
//import javax.inject.Inject;
|
||||
//import javax.security.enterprise.SecurityContext;
|
||||
//import javax.ws.rs.Consumes;
|
||||
//import javax.ws.rs.POST;
|
||||
//import javax.ws.rs.Path;
|
||||
//import javax.ws.rs.Produces;
|
||||
//import javax.ws.rs.core.MediaType;
|
||||
//import javax.ws.rs.core.MultivaluedMap;
|
||||
//import javax.ws.rs.core.Response;
|
||||
//import java.security.Principal;
|
||||
//import java.util.Arrays;
|
||||
//import java.util.List;
|
||||
//import java.util.Objects;
|
||||
//
|
||||
///**
|
||||
// * {
|
||||
// * "access_token" : "acb6803a48114d9fb4761e403c17f812",
|
||||
// * "token_type" : "bearer",
|
||||
// * "id_token" : "eyJhbGciOiJIUzI1NiIsImprdSI6Imh0dHBzOi8vbG9jYWxob3N0OjgwODAvdWFhL3Rva2VuX2tleXMiLCJraWQiOiJsZWdhY3ktdG9rZW4ta2V5IiwidHlwIjoiSldUIn0.eyJzdWIiOiIwNzYzZTM2MS02ODUwLTQ3N2ItYjk1Ny1iMmExZjU3MjczMTQiLCJhdWQiOlsibG9naW4iXSwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL3VhYS9vYXV0aC90b2tlbiIsImV4cCI6MTU1NzgzMDM4NSwiaWF0IjoxNTU3Nzg3MTg1LCJhenAiOiJsb2dpbiIsInNjb3BlIjpbIm9wZW5pZCJdLCJlbWFpbCI6IndyaHBONUB0ZXN0Lm9yZyIsInppZCI6InVhYSIsIm9yaWdpbiI6InVhYSIsImp0aSI6ImFjYjY4MDNhNDgxMTRkOWZiNDc2MWU0MDNjMTdmODEyIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImNsaWVudF9pZCI6ImxvZ2luIiwiY2lkIjoibG9naW4iLCJncmFudF90eXBlIjoiYXV0aG9yaXphdGlvbl9jb2RlIiwidXNlcl9uYW1lIjoid3JocE41QHRlc3Qub3JnIiwicmV2X3NpZyI6ImI3MjE5ZGYxIiwidXNlcl9pZCI6IjA3NjNlMzYxLTY4NTAtNDc3Yi1iOTU3LWIyYTFmNTcyNzMxNCIsImF1dGhfdGltZSI6MTU1Nzc4NzE4NX0.Fo8wZ_Zq9mwFks3LfXQ1PfJ4ugppjWvioZM6jSqAAQQ",
|
||||
// * "refresh_token" : "f59dcb5dcbca45f981f16ce519d61486-r",
|
||||
// * "expires_in" : 43199,
|
||||
// * "scope" : "openid oauth.approvals",
|
||||
// * "jti" : "acb6803a48114d9fb4761e403c17f812"
|
||||
// * }
|
||||
// */
|
||||
//@Path("token")
|
||||
//public class TokenEndpoint {
|
||||
//
|
||||
// List<String> supportedGrantTypes = Arrays.asList("authorization_code", "password", "refresh_token", "client_credentials");
|
||||
//
|
||||
// @Inject
|
||||
// private SecurityContext securityContext;
|
||||
//
|
||||
// @Inject
|
||||
// Instance<AuthorizationGrantTypeHandler> authorizationGrantTypeHandlers;
|
||||
//
|
||||
// @POST
|
||||
// @Produces(MediaType.APPLICATION_JSON)
|
||||
// @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
|
||||
// @Authenticated
|
||||
// public Response token(MultivaluedMap<String, String> params) throws JOSEException {
|
||||
// //Authenticate client with [basic] http authentication mechanism
|
||||
// Principal principal = securityContext.getCallerPrincipal();
|
||||
// Objects.requireNonNull(principal, "Client not authenticated!");
|
||||
//
|
||||
// //Check grant_type params
|
||||
// String grantType = params.getFirst("grant_type");
|
||||
// Objects.requireNonNull(grantType, "grant_type params is required");
|
||||
// //authorization_code, password, refresh, client_credentials
|
||||
// if (!supportedGrantTypes.contains(grantType)) {
|
||||
// throw new RuntimeException("grant_type parameter should be one of the following :" + supportedGrantTypes);
|
||||
// }
|
||||
// AuthorizationGrantTypeHandler authorizationGrantTypeHandler = authorizationGrantTypeHandlers.select(NamedLiteral.of(grantType)).get();
|
||||
// TokenResponse tokenResponse = authorizationGrantTypeHandler.createAccessToken(principal.getName(), params);
|
||||
// Response response = Response.ok(tokenResponse)
|
||||
// .header("Cache-Control", "no-store")
|
||||
// .header("Pragma", "no-cache")
|
||||
// .build();
|
||||
// return response;
|
||||
// }
|
||||
//}
|
|
@ -0,0 +1,32 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<server description="${project.artifactId}">
|
||||
|
||||
<featureManager>
|
||||
<feature>localConnector-1.0</feature>
|
||||
<feature>cdi-2.0</feature>
|
||||
<feature>jaxrs-2.1</feature>
|
||||
<feature>jpa-2.2</feature>
|
||||
<feature>appSecurity-3.0</feature>
|
||||
<feature>jsp-2.3</feature>
|
||||
<feature>mpConfig-1.3</feature>
|
||||
</featureManager>
|
||||
|
||||
<httpEndpoint id="defaultHttpEndpoint" httpPort="${httpPort}" httpsPort="${httpsPort}"/>
|
||||
|
||||
<library id="H2_JDBC_Lib">
|
||||
<fileset dir="${shared.resource.dir}" includes="h2*.jar"/>
|
||||
</library>
|
||||
<dataSource id="oauth2datasource" jndiName="jdbc/OAuth2_DS">
|
||||
<jdbcDriver
|
||||
javax.sql.XADataSource="org.h2.jdbcx.JdbcDataSource"
|
||||
javax.sql.ConnectionPoolDataSource="org.h2.jdbcx.JdbcDataSource"
|
||||
javax.sql.DataSource="org.h2.jdbcx.JdbcDataSource"
|
||||
libraryRef="H2_JDBC_Lib"/>
|
||||
<properties url="jdbc:h2:mem:OAuth2_DB" user="sa" password=""/>
|
||||
</dataSource>
|
||||
|
||||
<applicationManager autoExpand="true"/>
|
||||
<applicationMonitor updateTrigger="mbean"/>
|
||||
<application type="war" location="${project.build.finalName}.war" context-root="/"/>
|
||||
|
||||
</server>
|
|
@ -0,0 +1,2 @@
|
|||
signingkey=/META-INF/private-key.pem
|
||||
verificationkey=/META-INF/public-key.pem
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<persistence version="2.2"
|
||||
xmlns="http://xmlns.jcp.org/xml/ns/persistence"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
|
||||
http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd">
|
||||
<persistence-unit name="jpa-oauth2-pu" transaction-type="JTA">
|
||||
<jta-data-source>jdbc/OAuth2_DS</jta-data-source>
|
||||
<properties>
|
||||
<property name="javax.persistence.schema-generation.database.action" value="create"/>
|
||||
<property name="javax.persistence.sql-load-script-source" value="data.sql"/>
|
||||
</properties>
|
||||
</persistence-unit>
|
||||
</persistence>
|
|
@ -0,0 +1,28 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDYizUyvwY6qMd0
|
||||
ZkYPzP9mLTJOSczW8WtAbjhrgxk5LORIjcdv/aB1BAzDEOCX4UC+Rbs9YwaekIvW
|
||||
7dL/t6xPzsA7+9tMt27TqUxXigWEFQG73w/WbEVzCG09IHqG7Ztjb6aGL+8pfcu+
|
||||
SLvOtXWrctHNbr35BYC5yBQaos9bH+nLYbh2Ff+emrpg7Kwoeis6MY6RgZIL/lxt
|
||||
3beZt1NQhLEvB9t6XoSkyLQKSjT/OC8XOa4OYI4OfOSFqEUEp+8dKe3svZbQUNJf
|
||||
KKehx78xx0rfwH+iBsFKkHdvt1ZHtINvYedAfGrc5rWnesunzOW7OKMAiIXAJSRA
|
||||
rn9Iv5/fAgMBAAECggEAYHKwgSfAGIRwQhIDhqoh31qmG2SXjez9fjcZfhloNKUg
|
||||
EIjFmcX3n+br4D42Kq+zbIwWd6MRobJz9oj6/9bJMsq9qHnnFWZmQHQZgqwBBPFu
|
||||
UkVqAnE7BZ9tOFqs+EgAe+uQ2hejiHF1PA2dSNZd0L1VYRDAIJgo25aYDb0Sal0c
|
||||
qaFmK1XzPnSIhgnopKP/TmUfALjeshtGvh6WkiXHTY2VKJlyLEiGd3/1tBZnzwSQ
|
||||
QiS/2zfXL7lO0SYRA10m1BmtqGi3wSKIXUA6tNulmpXEMnYk4RtsqBClOanzQIm7
|
||||
f4Ie59AtUUlDdHzRmhxBaDprgAI/FdCxXkseLzk5sQKBgQDyptOhJ/VQ5tkgffJW
|
||||
X2QwGIaNdvNCEL4GlgXKAWNkCzT4h+Q/2wFoKhaN2JH2YZwKCRwG/UZh4Hq22fdx
|
||||
fa/DP8PK3zlX0c9dWQOtmBOe0/nG28kq9iqziP3ILLNuelfaMhxtRkUmR652VCzx
|
||||
qazZmjoo2MxtA6BMiK3Vx5aikwKBgQDkdLalVveHKTVkrplQYNapQAtAwjlsNnMt
|
||||
VJh6nLo3eOg1xBNA7JrYqx7sXRQInUGAyNKvgQL/lCQdMkTzyetLjJGsiFSEgeot
|
||||
RMsDXBrJFYZiErbpQungpaevwAymaGi++nbCzhw1/n3AvVUhRKNknX8Ms0UKlLRZ
|
||||
TCktCTChBQKBgGA7ZzzHgxPFqaCoMl6s0Cf+4gXigdDWoPYtszgM2uUHSMez5QKq
|
||||
EWHFJ1Kz7BdBWMfmGvZupeYVR7WStf6NcRJHDJg9dRlt/QYxUjMbV9Sqjqmd6qce
|
||||
H4s6LiOgDr0mygafzwRLVQs8bGVDNtvUhdd6wcwHRvOI957CqeZZlFT/AoGBAN2P
|
||||
j79EW6U6wuyVJF0+vZDBauhwNQ6MtCEnZQWs0DCSUuop8d5KWVZ+huwGzTIZiPhk
|
||||
S2goP4cs3eVu5k5k6oyHlJP2V7l24WzrxdPJVLTl6kFdEwWgfn//SGR7ZglRQxzM
|
||||
fbcp+1QmL0FonZI5JhmjYR8pEXFUjJ/57AkgW4gdAoGASV3TlaNd+reb2l0kETqp
|
||||
HrzzMIYkM2faZ8LmpcEO0wz606SK468bnl6SqVBYT2J0bkqCpEmPBtWs/OFCWd6x
|
||||
93NndSfJk57/7AqNxfVyZWp2sjRzt5vZ4KPlRVvMHIGeJNbIV7RqUr6XGu+u3ZrS
|
||||
B7SqwKZqdHVSULG9nMK7Iz8=
|
||||
-----END PRIVATE KEY-----
|
|
@ -0,0 +1,9 @@
|
|||
-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2Is1Mr8GOqjHdGZGD8z/
|
||||
Zi0yTknM1vFrQG44a4MZOSzkSI3Hb/2gdQQMwxDgl+FAvkW7PWMGnpCL1u3S/7es
|
||||
T87AO/vbTLdu06lMV4oFhBUBu98P1mxFcwhtPSB6hu2bY2+mhi/vKX3Lvki7zrV1
|
||||
q3LRzW69+QWAucgUGqLPWx/py2G4dhX/npq6YOysKHorOjGOkYGSC/5cbd23mbdT
|
||||
UISxLwfbel6EpMi0Cko0/zgvFzmuDmCODnzkhahFBKfvHSnt7L2W0FDSXyinoce/
|
||||
McdK38B/ogbBSpB3b7dWR7SDb2HnQHxq3Oa1p3rLp8zluzijAIiFwCUkQK5/SL+f
|
||||
3wIDAQAB
|
||||
-----END PUBLIC KEY-----
|
|
@ -0,0 +1,3 @@
|
|||
INSERT INTO `users` (`user_id`, `password`, `roles`, `scopes`) VALUES ('appuser', 'appusersecret', 'USER', 'resource.read resource.write');
|
||||
|
||||
INSERT INTO `clients` (`client_id`, `client_secret`, `redirect_uri`,`scope`,`authorized_grant_types`) VALUES ('webappclient', 'webappclientsecret', 'http://localhost:9180/callback', 'resource.read resource.write', 'authorization_code refresh_token');
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd"
|
||||
bean-discovery-mode="all">
|
||||
</beans>
|
|
@ -0,0 +1,54 @@
|
|||
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
|
||||
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>Authorization</title>
|
||||
<style>
|
||||
input[type=submit]{
|
||||
width: 25%;
|
||||
padding: 4px 0px;
|
||||
display: inline-block;
|
||||
border: 1px solid #ccc;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.container {
|
||||
width: 75%;
|
||||
padding: 25px;
|
||||
border: 1px solid #ccc;
|
||||
background: beige;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="container">
|
||||
<p><h3>Want to Authorize scopes for client : ${client.clientId} ?</h3></p>
|
||||
<hr>
|
||||
|
||||
<form method="post" action="authorize">
|
||||
<table>
|
||||
<tr>
|
||||
<td valign="top">Scopes :</td>
|
||||
<td>
|
||||
<c:forTokens items="${scopes}" delims=" " var="scope">
|
||||
<input type="checkbox" name="scope" value="${scope}">${scope}</input><br/>
|
||||
</c:forTokens>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<input type="submit" name="approbation_status" value="YES"/>
|
||||
<input type="submit" name="approbation_status" value="NO"/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,25 @@
|
|||
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1" %>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
||||
<title>Error</title>
|
||||
</head>
|
||||
|
||||
<style>
|
||||
.container {
|
||||
padding: 16px;
|
||||
border: 1px solid #ccc;
|
||||
background: #f57007;
|
||||
}
|
||||
</style>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="container">
|
||||
<p>${error}</p>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,49 @@
|
|||
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1" %>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
||||
<title>Login Form</title>
|
||||
</head>
|
||||
<style>
|
||||
input[type=text], input[type=password] {
|
||||
width: 75%;
|
||||
padding: 4px 0px;
|
||||
display: inline-block;
|
||||
border: 1px solid #ccc;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.container {
|
||||
width: 50%;
|
||||
padding: 16px;
|
||||
border: 1px solid #ccc;
|
||||
background: beige;
|
||||
}
|
||||
|
||||
</style>
|
||||
<body>
|
||||
|
||||
<div class="container">
|
||||
<p>Login Form</p>
|
||||
<hr>
|
||||
<form id="loginform" action="j_security_check" method="post">
|
||||
<table style="with: 50%">
|
||||
<tr>
|
||||
<td width="20%">Username</td>
|
||||
<td><input type="text" name="j_username"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="20%">Password</td>
|
||||
<td><input type="password" name="j_password"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="20%"></td>
|
||||
<td><input type="submit" value="Login"/></td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>oauth2-client</artifactId>
|
||||
<packaging>war</packaging>
|
||||
|
||||
<parent>
|
||||
<groupId>com.baeldung.oauth2</groupId>
|
||||
<artifactId>oauth2-framework-impl</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<httpPort>9180</httpPort>
|
||||
<httpsPort>9543</httpsPort>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>net.wasdev.wlp.maven.plugins</groupId>
|
||||
<artifactId>liberty-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
|
@ -0,0 +1,39 @@
|
|||
package com.baeldung.oauth2.client;
|
||||
|
||||
import org.eclipse.microprofile.config.Config;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.annotation.WebServlet;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.UUID;
|
||||
|
||||
@WebServlet(urlPatterns = "/authorize")
|
||||
public class AuthorizationCodeServlet extends HttpServlet {
|
||||
|
||||
@Inject
|
||||
private Config config;
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
//...
|
||||
request.getSession().removeAttribute("tokenResponse");
|
||||
String state = UUID.randomUUID().toString();
|
||||
request.getSession().setAttribute("CLIENT_LOCAL_STATE", state);
|
||||
|
||||
String authorizationUri = config.getValue("provider.authorizationUri", String.class);
|
||||
String clientId = config.getValue("client.clientId", String.class);
|
||||
String redirectUri = config.getValue("client.redirectUri", String.class);
|
||||
String scope = config.getValue("client.scope", String.class);
|
||||
|
||||
String authorizationLocation = authorizationUri + "?response_type=code"
|
||||
+ "&client_id=" + clientId
|
||||
+ "&redirect_uri=" + redirectUri
|
||||
+ "&scope=" + scope
|
||||
+ "&state=" + state;
|
||||
response.sendRedirect(authorizationLocation);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
package com.baeldung.oauth2.client;
|
||||
|
||||
import org.eclipse.microprofile.config.Config;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.json.JsonObject;
|
||||
import javax.servlet.RequestDispatcher;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.annotation.WebServlet;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.ws.rs.client.Client;
|
||||
import javax.ws.rs.client.ClientBuilder;
|
||||
import javax.ws.rs.client.Entity;
|
||||
import javax.ws.rs.client.WebTarget;
|
||||
import javax.ws.rs.core.Form;
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import java.io.IOException;
|
||||
import java.util.Base64;
|
||||
|
||||
@WebServlet(urlPatterns = "/callback")
|
||||
public class CallbackServlet extends HttpServlet {
|
||||
|
||||
@Inject
|
||||
private Config config;
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
|
||||
//Error:
|
||||
String error = request.getParameter("error");
|
||||
if (error != null) {
|
||||
request.setAttribute("error", error);
|
||||
dispatch("/", request, response);
|
||||
return;
|
||||
}
|
||||
String localState = (String) request.getSession().getAttribute("CLIENT_LOCAL_STATE");
|
||||
if (!localState.equals(request.getParameter("state"))) {
|
||||
request.setAttribute("error", "The state attribute doesn't match !!");
|
||||
dispatch("/", request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
String code = request.getParameter("code");
|
||||
|
||||
Client client = ClientBuilder.newClient();
|
||||
WebTarget target = client.target(config.getValue("provider.tokenUri", String.class));
|
||||
|
||||
Form form = new Form();
|
||||
form.param("grant_type", "authorization_code");
|
||||
form.param("code", code);
|
||||
form.param("redirect_uri", config.getValue("client.redirectUri", String.class));
|
||||
|
||||
JsonObject tokenResponse = target.request(MediaType.APPLICATION_JSON_TYPE)
|
||||
.header(HttpHeaders.AUTHORIZATION, getAuthorizationHeaderValue())
|
||||
.post(Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE), JsonObject.class);
|
||||
|
||||
request.getSession().setAttribute("tokenResponse", tokenResponse);
|
||||
dispatch("/", request, response);
|
||||
}
|
||||
|
||||
private void dispatch(String location, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
RequestDispatcher requestDispatcher = request.getRequestDispatcher(location);
|
||||
requestDispatcher.forward(request, response);
|
||||
}
|
||||
|
||||
private String getAuthorizationHeaderValue() {
|
||||
String clientId = config.getValue("client.clientId", String.class);
|
||||
String clientSecret = config.getValue("client.clientSecret", String.class);
|
||||
String token = clientId + ":" + clientSecret;
|
||||
String encodedString = Base64.getEncoder().encodeToString(token.getBytes());
|
||||
return "Basic " + encodedString;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
package com.baeldung.oauth2.client;
|
||||
|
||||
import org.eclipse.microprofile.config.Config;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.json.JsonObject;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.annotation.WebServlet;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.ws.rs.client.*;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
@WebServlet(urlPatterns = "/downstream")
|
||||
public class DownstreamCallServlet extends HttpServlet {
|
||||
|
||||
@Inject
|
||||
private Config config;
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse resp) throws ServletException, IOException {
|
||||
resp.setContentType("text/html;charset=UTF-8");
|
||||
String action = request.getParameter("action");
|
||||
Client client = ClientBuilder.newClient();
|
||||
WebTarget webTarget = client.target(config.getValue("resourceServerUri", String.class));
|
||||
WebTarget resourceWebTarget;
|
||||
String response = null;
|
||||
JsonObject tokenResponse = (JsonObject) request.getSession().getAttribute("tokenResponse");
|
||||
if ("read".equals(action)) {
|
||||
resourceWebTarget = webTarget.path("resource/read");
|
||||
Invocation.Builder invocationBuilder = resourceWebTarget.request();
|
||||
response = invocationBuilder
|
||||
.header("authorization", tokenResponse.getString("access_token"))
|
||||
.get(String.class);
|
||||
} else if ("write".equals(action)) {
|
||||
resourceWebTarget = webTarget.path("resource/write");
|
||||
Invocation.Builder invocationBuilder = resourceWebTarget.request();
|
||||
response = invocationBuilder
|
||||
.header("authorization", tokenResponse.getString("access_token"))
|
||||
.post(Entity.text("body string"), String.class);
|
||||
}
|
||||
PrintWriter out = resp.getWriter();
|
||||
out.println(response);
|
||||
out.flush();
|
||||
out.close();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<server description="${project.artifactId}">
|
||||
|
||||
<featureManager>
|
||||
<feature>localConnector-1.0</feature>
|
||||
<feature>cdi-2.0</feature>
|
||||
<feature>jsp-2.3</feature>
|
||||
<feature>mpConfig-1.3</feature>
|
||||
<feature>jaxrsClient-2.1</feature>
|
||||
<feature>jsonp-1.1</feature>
|
||||
</featureManager>
|
||||
|
||||
<httpEndpoint id="defaultHttpEndpoint" httpPort="${httpPort}" httpsPort="${httpsPort}"/>
|
||||
|
||||
<applicationManager autoExpand="true"/>
|
||||
<applicationMonitor updateTrigger="mbean"/>
|
||||
<application type="war" location="${project.build.finalName}.war" context-root="/"/>
|
||||
|
||||
</server>
|
|
@ -0,0 +1,12 @@
|
|||
#Client registration
|
||||
client.clientId=webappclient
|
||||
client.clientSecret=webappclientsecret
|
||||
client.redirectUri=http://localhost:9180/callback
|
||||
client.scope=resource.read resource.write
|
||||
|
||||
#Provider
|
||||
provider.authorizationUri=http://127.0.0.1:9080/authorize
|
||||
provider.tokenUri=http://127.0.0.1:9080/token
|
||||
|
||||
#Resource Server
|
||||
resourceServerUri=http://localhost:9280/api
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd"
|
||||
bean-discovery-mode="all">
|
||||
</beans>
|
|
@ -0,0 +1,5 @@
|
|||
<web-app xmlns="http://java.sun.com/xml/ns/javaee" version="3.0">
|
||||
<welcome-file-list>
|
||||
<welcome-file>index.jsp</welcome-file>
|
||||
</welcome-file-list>
|
||||
</web-app>
|
|
@ -0,0 +1,97 @@
|
|||
<%@ page import="org.eclipse.microprofile.config.Config" %>
|
||||
<%@ page import="org.eclipse.microprofile.config.ConfigProvider" %>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>OAuth2 Client</title>
|
||||
<style>
|
||||
body {
|
||||
margin: 0px;
|
||||
}
|
||||
input[type=text], input[type=password] {
|
||||
width: 75%;
|
||||
padding: 4px 0px;
|
||||
display: inline-block;
|
||||
border: 1px solid #502bcc;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.container-error {
|
||||
padding: 16px;
|
||||
border: 1px solid #cc102a;
|
||||
background: #f50307;
|
||||
width: 75%;
|
||||
margin-left: 25px;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
.container {
|
||||
padding: 16px;
|
||||
border: 1px solid #130ecc;
|
||||
background: #eaecf5;
|
||||
width: 75%;
|
||||
margin-left: 25px;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<%
|
||||
Config config1 = ConfigProvider.getConfig();
|
||||
%>
|
||||
|
||||
<div class="container-error" style="visibility: <%=request.getAttribute("error")!=null?"visible":"hidden"%>">
|
||||
<span>Error : ${error}</span>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<span><h4>OAuth2 Authorization Server</h4></span>
|
||||
<hr>
|
||||
<p>Client Registration :</p>
|
||||
<ul>
|
||||
<li>client_id: <%=config1.getValue("client.clientId", String.class)%>
|
||||
</li>
|
||||
<li>client_secret: <%=config1.getValue("client.clientSecret", String.class)%>
|
||||
</li>
|
||||
<li>redirect_uri: <%=config1.getValue("client.redirectUri", String.class)%>
|
||||
</li>
|
||||
<li>scope: <%=config1.getValue("client.scope", String.class)%>
|
||||
</li>
|
||||
</ul>
|
||||
<p>Authorization Server :</p>
|
||||
<ul>
|
||||
<li>authorization_uri: <%=config1.getValue("provider.authorizationUri", String.class)%>
|
||||
</li>
|
||||
<li>token_uri: <%=config1.getValue("provider.tokenUri", String.class)%>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="container">
|
||||
<span><h4>OAuth2 Client</h4></span>
|
||||
<hr>
|
||||
<p>Token Request :</p>
|
||||
<ul>
|
||||
<li><a class="btn btn-primary" href="/authorize">Get Access Token</a></li>
|
||||
</ul>
|
||||
|
||||
<p>Token Response:</p>
|
||||
<ul>
|
||||
<li>access_token: ${tokenResponse.getString("access_token")}</li>
|
||||
<li>scope: ${tokenResponse.getString("scope")}</li>
|
||||
<li>Expires in (s): ${tokenResponse.getInt("expires_in")}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="container">
|
||||
<span><h4>OAuth2 Resource Server Call</h4></span>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="downstream?action=read">Read Protected Resource</a></li>
|
||||
<li><a href="downstream?action=write">Write Protected Resource</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,40 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>oauth2-resource-server</artifactId>
|
||||
<packaging>war</packaging>
|
||||
|
||||
<parent>
|
||||
<groupId>com.baeldung.oauth2</groupId>
|
||||
<artifactId>oauth2-framework-impl</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<httpPort>9280</httpPort>
|
||||
<httpsPort>8643</httpsPort>
|
||||
<jwt.issuer>http://localhost:9080</jwt.issuer>
|
||||
<jwt.resourceId>http://localhost:9280</jwt.resourceId>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.microprofile.jwt</groupId>
|
||||
<artifactId>microprofile-jwt-auth-api</artifactId>
|
||||
<version>1.1</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>net.wasdev.wlp.maven.plugins</groupId>
|
||||
<artifactId>liberty-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,13 @@
|
|||
package com.baeldung.oauth2.resource.server;
|
||||
|
||||
import org.eclipse.microprofile.auth.LoginConfig;
|
||||
|
||||
import javax.annotation.security.DeclareRoles;
|
||||
import javax.ws.rs.ApplicationPath;
|
||||
import javax.ws.rs.core.Application;
|
||||
|
||||
@ApplicationPath("/api")
|
||||
@DeclareRoles({"resource.read", "resource.write"})
|
||||
@LoginConfig(authMethod = "MP-JWT")
|
||||
public class OAuth2ResourceServerApplication extends Application {
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package com.baeldung.oauth2.resource.server.secure;
|
||||
|
||||
import org.eclipse.microprofile.jwt.JsonWebToken;
|
||||
|
||||
import javax.annotation.security.RolesAllowed;
|
||||
import javax.enterprise.context.RequestScoped;
|
||||
import javax.inject.Inject;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.util.UUID;
|
||||
|
||||
@Path("/resource")
|
||||
@RequestScoped
|
||||
public class ProtectedResource {
|
||||
|
||||
@Inject
|
||||
private JsonWebToken principal;
|
||||
|
||||
@GET
|
||||
@RolesAllowed("resource.read")
|
||||
@Path("/read")
|
||||
public Response read() {
|
||||
//DoStaff
|
||||
return Response.ok("Hello, " + principal.getName()).build();
|
||||
}
|
||||
|
||||
@POST
|
||||
@RolesAllowed("resource.write")
|
||||
@Path("/write")
|
||||
public Response write() {
|
||||
//DoStaff
|
||||
return Response.ok("Hello, " + principal.getName())
|
||||
.header("location", UUID.randomUUID().toString())
|
||||
.build();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<server description="${project.artifactId}">
|
||||
|
||||
<featureManager>
|
||||
<feature>localConnector-1.0</feature>
|
||||
<feature>cdi-2.0</feature>
|
||||
<feature>jaxrs-2.1</feature>
|
||||
<feature>jsonp-1.1</feature>
|
||||
<feature>mpConfig-1.3</feature>
|
||||
<feature>mpJwt-1.1</feature>
|
||||
</featureManager>
|
||||
|
||||
<httpEndpoint id="defaultHttpEndpoint" httpPort="${httpPort}" httpsPort="${httpsPort}"/>
|
||||
<mpJwt id="mpJwt123" audiences="${jwt.resourceId}"/>
|
||||
|
||||
<applicationManager autoExpand="true"/>
|
||||
<applicationMonitor updateTrigger="mbean"/>
|
||||
<application type="war" location="${project.build.finalName}.war" context-root="/"/>
|
||||
|
||||
</server>
|
|
@ -0,0 +1,2 @@
|
|||
mp.jwt.verify.publickey.location=/META-INF/public-key.pem
|
||||
mp.jwt.verify.issuer=http://localhost:9080
|
|
@ -0,0 +1,9 @@
|
|||
-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2Is1Mr8GOqjHdGZGD8z/
|
||||
Zi0yTknM1vFrQG44a4MZOSzkSI3Hb/2gdQQMwxDgl+FAvkW7PWMGnpCL1u3S/7es
|
||||
T87AO/vbTLdu06lMV4oFhBUBu98P1mxFcwhtPSB6hu2bY2+mhi/vKX3Lvki7zrV1
|
||||
q3LRzW69+QWAucgUGqLPWx/py2G4dhX/npq6YOysKHorOjGOkYGSC/5cbd23mbdT
|
||||
UISxLwfbel6EpMi0Cko0/zgvFzmuDmCODnzkhahFBKfvHSnt7L2W0FDSXyinoce/
|
||||
McdK38B/ogbBSpB3b7dWR7SDb2HnQHxq3Oa1p3rLp8zluzijAIiFwCUkQK5/SL+f
|
||||
3wIDAQAB
|
||||
-----END PUBLIC KEY-----
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
|
||||
bean-discovery-mode="all">
|
||||
</beans>
|
|
@ -0,0 +1,37 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Eclipse MicroProfile demo</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h2>MicroProfile</h2>
|
||||
|
||||
<a href="data/hello" target="_blank" >Hello JAX-RS endpoint</a> <br/>
|
||||
|
||||
|
||||
<h3>Config</h3>
|
||||
<a href="data/config/injected" target="_blank" >Injected config values</a> <br/>
|
||||
<a href="data/config/lookup" target="_blank" >Config values by lookup</a> <br/>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>JWT Auth</h3>
|
||||
Look at readme.md on how to test protected endpoint.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,93 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.baeldung.oauth2</groupId>
|
||||
<artifactId>oauth2-framework-impl</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
<failOnMissingWebXml>false</failOnMissingWebXml>
|
||||
<openliberty.version>RELEASE</openliberty.version>
|
||||
<openliberty.maven.version>2.6.4</openliberty.maven.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>javax</groupId>
|
||||
<artifactId>javaee-web-api</artifactId>
|
||||
<version>8.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.microprofile.config</groupId>
|
||||
<artifactId>microprofile-config-api</artifactId>
|
||||
<version>1.3</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>${basedir}/src/main/resources</directory>
|
||||
<includes>
|
||||
<include>**/*.*</include>
|
||||
</includes>
|
||||
</resource>
|
||||
<resource>
|
||||
<filtering>true</filtering>
|
||||
<directory>${basedir}/src/main/liberty</directory>
|
||||
<includes>
|
||||
<include>**/*.*</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>net.wasdev.wlp.maven.plugins</groupId>
|
||||
<artifactId>liberty-maven-plugin</artifactId>
|
||||
<version>${openliberty.maven.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>package-server</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>create-server</goal>
|
||||
<goal>install-apps</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>target/wlp-package</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<assemblyArtifact>
|
||||
<groupId>io.openliberty</groupId>
|
||||
<artifactId>openliberty-webProfile8</artifactId>
|
||||
<version>${openliberty.version}</version>
|
||||
<type>zip</type>
|
||||
</assemblyArtifact>
|
||||
<configFile>target/classes/config/server.xml</configFile>
|
||||
<appArchive>${project.build.directory}/${project.build.finalName}.war</appArchive>
|
||||
<installAppPackages>project</installAppPackages>
|
||||
<configDirectory>${project.basedir}/src/main/liberty/server</configDirectory>
|
||||
<appsDirectory>apps</appsDirectory>
|
||||
<looseApplication>true</looseApplication>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
<modules>
|
||||
<module>oauth2-authorization-server</module>
|
||||
<module>oauth2-resource-server</module>
|
||||
<module>oauth2-client</module>
|
||||
</modules>
|
||||
</project>
|
Loading…
Reference in New Issue