diff --git a/oauth2-framework-impl/oauth2-authorization-server/pom.xml b/oauth2-framework-impl/oauth2-authorization-server/pom.xml new file mode 100644 index 0000000000..8db2150558 --- /dev/null +++ b/oauth2-framework-impl/oauth2-authorization-server/pom.xml @@ -0,0 +1,72 @@ + + + 4.0.0 + + oauth2-authorization-server + war + + + com.baeldung.oauth2 + oauth2-framework-impl + 1.0-SNAPSHOT + + + + 1.4.199 + 9080 + 9443 + + + + + com.nimbusds + nimbus-jose-jwt + 7.3 + + + org.bouncycastle + bcprov-jdk15on + 1.62 + + + org.bouncycastle + bcpkix-jdk15on + 1.62 + + + + + + net.wasdev.wlp.maven.plugins + liberty-maven-plugin + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy-h2-dependency + package + + copy + + + + + + + com.h2database + h2 + ${h2.version} + jar + ${project.build.directory}/liberty/wlp/usr/shared/resources/ + + + + + + + + diff --git a/oauth2-framework-impl/oauth2-authorization-server/src/main/java/com/baeldung/oauth2/authorization/server/OAuth2ServerApplication.java b/oauth2-framework-impl/oauth2-authorization-server/src/main/java/com/baeldung/oauth2/authorization/server/OAuth2ServerApplication.java new file mode 100644 index 0000000000..62d6b04464 --- /dev/null +++ b/oauth2-framework-impl/oauth2-authorization-server/src/main/java/com/baeldung/oauth2/authorization/server/OAuth2ServerApplication.java @@ -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 { +} diff --git a/oauth2-framework-impl/oauth2-authorization-server/src/main/java/com/baeldung/oauth2/authorization/server/PEMKeyUtils.java b/oauth2-framework-impl/oauth2-authorization-server/src/main/java/com/baeldung/oauth2/authorization/server/PEMKeyUtils.java new file mode 100644 index 0000000000..dab57b91a7 --- /dev/null +++ b/oauth2-framework-impl/oauth2-authorization-server/src/main/java/com/baeldung/oauth2/authorization/server/PEMKeyUtils.java @@ -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); + } +} diff --git a/oauth2-framework-impl/oauth2-authorization-server/src/main/java/com/baeldung/oauth2/authorization/server/api/AuthorizationEndpoint.java b/oauth2-framework-impl/oauth2-authorization-server/src/main/java/com/baeldung/oauth2/authorization/server/api/AuthorizationEndpoint.java new file mode 100644 index 0000000000..10e78a2dfd --- /dev/null +++ b/oauth2-framework-impl/oauth2-authorization-server/src/main/java/com/baeldung/oauth2/authorization/server/api/AuthorizationEndpoint.java @@ -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 authorizationGrantTypeHandlers; + + @GET + @Produces(MediaType.TEXT_HTML) + public Response doGet(@Context HttpServletRequest request, + @Context HttpServletResponse response, + @Context UriInfo uriInfo) throws ServletException, IOException { + MultivaluedMap 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 params) throws Exception { + MultivaluedMap originalParams = (MultivaluedMap) 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 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 allowedScopes = new LinkedHashSet<>(); + Set rScopes = new HashSet(Arrays.asList(requestedScope.split(" "))); + Set 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; + } +} diff --git a/oauth2-framework-impl/oauth2-authorization-server/src/main/java/com/baeldung/oauth2/authorization/server/api/JWKEndpoint.java b/oauth2-framework-impl/oauth2-authorization-server/src/main/java/com/baeldung/oauth2/authorization/server/api/JWKEndpoint.java new file mode 100644 index 0000000000..9d38c823b9 --- /dev/null +++ b/oauth2-framework-impl/oauth2-authorization-server/src/main/java/com/baeldung/oauth2/authorization/server/api/JWKEndpoint.java @@ -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; + } +} diff --git a/oauth2-framework-impl/oauth2-authorization-server/src/main/java/com/baeldung/oauth2/authorization/server/api/TokenEndpoint.java b/oauth2-framework-impl/oauth2-authorization-server/src/main/java/com/baeldung/oauth2/authorization/server/api/TokenEndpoint.java new file mode 100644 index 0000000000..f39bb2ea2d --- /dev/null +++ b/oauth2-framework-impl/oauth2-authorization-server/src/main/java/com/baeldung/oauth2/authorization/server/api/TokenEndpoint.java @@ -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 supportedGrantTypes = Collections.singletonList("authorization_code"); + + @Inject + private AppDataRepository appDataRepository; + + @Inject + Instance authorizationGrantTypeHandlers; + + @POST + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) + public Response token(MultivaluedMap 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; + } +} diff --git a/oauth2-framework-impl/oauth2-authorization-server/src/main/java/com/baeldung/oauth2/authorization/server/handler/AuthorizationCodeGrantTypeHandler.java b/oauth2-framework-impl/oauth2-authorization-server/src/main/java/com/baeldung/oauth2/authorization/server/handler/AuthorizationCodeGrantTypeHandler.java new file mode 100644 index 0000000000..889c7fcea2 --- /dev/null +++ b/oauth2-framework-impl/oauth2-authorization-server/src/main/java/com/baeldung/oauth2/authorization/server/handler/AuthorizationCodeGrantTypeHandler.java @@ -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 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(); + } +} diff --git a/oauth2-framework-impl/oauth2-authorization-server/src/main/java/com/baeldung/oauth2/authorization/server/handler/AuthorizationGrantTypeHandler.java b/oauth2-framework-impl/oauth2-authorization-server/src/main/java/com/baeldung/oauth2/authorization/server/handler/AuthorizationGrantTypeHandler.java new file mode 100644 index 0000000000..a5afe293ef --- /dev/null +++ b/oauth2-framework-impl/oauth2-authorization-server/src/main/java/com/baeldung/oauth2/authorization/server/handler/AuthorizationGrantTypeHandler.java @@ -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 params) throws Exception; +} diff --git a/oauth2-framework-impl/oauth2-authorization-server/src/main/java/com/baeldung/oauth2/authorization/server/model/AppDataRepository.java b/oauth2-framework-impl/oauth2-authorization-server/src/main/java/com/baeldung/oauth2/authorization/server/model/AppDataRepository.java new file mode 100644 index 0000000000..6b827d6a3d --- /dev/null +++ b/oauth2-framework-impl/oauth2-authorization-server/src/main/java/com/baeldung/oauth2/authorization/server/model/AppDataRepository.java @@ -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; + } +} diff --git a/oauth2-framework-impl/oauth2-authorization-server/src/main/java/com/baeldung/oauth2/authorization/server/model/AuthorizationCode.java b/oauth2-framework-impl/oauth2-authorization-server/src/main/java/com/baeldung/oauth2/authorization/server/model/AuthorizationCode.java new file mode 100644 index 0000000000..a2ec088eb9 --- /dev/null +++ b/oauth2-framework-impl/oauth2-authorization-server/src/main/java/com/baeldung/oauth2/authorization/server/model/AuthorizationCode.java @@ -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; + } +} diff --git a/oauth2-framework-impl/oauth2-authorization-server/src/main/java/com/baeldung/oauth2/authorization/server/model/Client.java b/oauth2-framework-impl/oauth2-authorization-server/src/main/java/com/baeldung/oauth2/authorization/server/model/Client.java new file mode 100644 index 0000000000..9b5ad2f904 --- /dev/null +++ b/oauth2-framework-impl/oauth2-authorization-server/src/main/java/com/baeldung/oauth2/authorization/server/model/Client.java @@ -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; + } +} diff --git a/oauth2-framework-impl/oauth2-authorization-server/src/main/java/com/baeldung/oauth2/authorization/server/model/User.java b/oauth2-framework-impl/oauth2-authorization-server/src/main/java/com/baeldung/oauth2/authorization/server/model/User.java new file mode 100644 index 0000000000..b3821715f4 --- /dev/null +++ b/oauth2-framework-impl/oauth2-authorization-server/src/main/java/com/baeldung/oauth2/authorization/server/model/User.java @@ -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(); + } +} diff --git a/oauth2-framework-impl/oauth2-authorization-server/src/main/java/com/baeldung/oauth2/authorization/server/security/UserIdentityStore.java b/oauth2-framework-impl/oauth2-authorization-server/src/main/java/com/baeldung/oauth2/authorization/server/security/UserIdentityStore.java new file mode 100644 index 0000000000..a32ab32cd4 --- /dev/null +++ b/oauth2-framework-impl/oauth2-authorization-server/src/main/java/com/baeldung/oauth2/authorization/server/security/UserIdentityStore.java @@ -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; + } +} \ No newline at end of file diff --git a/oauth2-framework-impl/oauth2-authorization-server/src/main/java/com/baeldung/oauth2/authorization/server/web/AuthorizationEndpoint.java b/oauth2-framework-impl/oauth2-authorization-server/src/main/java/com/baeldung/oauth2/authorization/server/web/AuthorizationEndpoint.java new file mode 100644 index 0000000000..84b9a89c54 --- /dev/null +++ b/oauth2-framework-impl/oauth2-authorization-server/src/main/java/com/baeldung/oauth2/authorization/server/web/AuthorizationEndpoint.java @@ -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: +// +// +// ==> 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 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 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); +// } +//} diff --git a/oauth2-framework-impl/oauth2-authorization-server/src/main/java/com/baeldung/oauth2/authorization/server/web/TokenEndpoint.java b/oauth2-framework-impl/oauth2-authorization-server/src/main/java/com/baeldung/oauth2/authorization/server/web/TokenEndpoint.java new file mode 100644 index 0000000000..73085a68d1 --- /dev/null +++ b/oauth2-framework-impl/oauth2-authorization-server/src/main/java/com/baeldung/oauth2/authorization/server/web/TokenEndpoint.java @@ -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 supportedGrantTypes = Arrays.asList("authorization_code", "password", "refresh_token", "client_credentials"); +// +// @Inject +// private SecurityContext securityContext; +// +// @Inject +// Instance authorizationGrantTypeHandlers; +// +// @POST +// @Produces(MediaType.APPLICATION_JSON) +// @Consumes(MediaType.APPLICATION_FORM_URLENCODED) +// @Authenticated +// public Response token(MultivaluedMap 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; +// } +//} diff --git a/oauth2-framework-impl/oauth2-authorization-server/src/main/liberty/config/server.xml b/oauth2-framework-impl/oauth2-authorization-server/src/main/liberty/config/server.xml new file mode 100644 index 0000000000..97d9ec4a28 --- /dev/null +++ b/oauth2-framework-impl/oauth2-authorization-server/src/main/liberty/config/server.xml @@ -0,0 +1,32 @@ + + + + + localConnector-1.0 + cdi-2.0 + jaxrs-2.1 + jpa-2.2 + appSecurity-3.0 + jsp-2.3 + mpConfig-1.3 + + + + + + + + + + + + + + + + + diff --git a/oauth2-framework-impl/oauth2-authorization-server/src/main/resources/META-INF/microprofile-config.properties b/oauth2-framework-impl/oauth2-authorization-server/src/main/resources/META-INF/microprofile-config.properties new file mode 100644 index 0000000000..d9c68bc111 --- /dev/null +++ b/oauth2-framework-impl/oauth2-authorization-server/src/main/resources/META-INF/microprofile-config.properties @@ -0,0 +1,2 @@ +signingkey=/META-INF/private-key.pem +verificationkey=/META-INF/public-key.pem diff --git a/oauth2-framework-impl/oauth2-authorization-server/src/main/resources/META-INF/persistence.xml b/oauth2-framework-impl/oauth2-authorization-server/src/main/resources/META-INF/persistence.xml new file mode 100644 index 0000000000..a10ad3c886 --- /dev/null +++ b/oauth2-framework-impl/oauth2-authorization-server/src/main/resources/META-INF/persistence.xml @@ -0,0 +1,14 @@ + + + + jdbc/OAuth2_DS + + + + + + \ No newline at end of file diff --git a/oauth2-framework-impl/oauth2-authorization-server/src/main/resources/META-INF/private-key.pem b/oauth2-framework-impl/oauth2-authorization-server/src/main/resources/META-INF/private-key.pem new file mode 100644 index 0000000000..6e36cb4d78 --- /dev/null +++ b/oauth2-framework-impl/oauth2-authorization-server/src/main/resources/META-INF/private-key.pem @@ -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----- diff --git a/oauth2-framework-impl/oauth2-authorization-server/src/main/resources/META-INF/public-key.pem b/oauth2-framework-impl/oauth2-authorization-server/src/main/resources/META-INF/public-key.pem new file mode 100644 index 0000000000..f53ceca446 --- /dev/null +++ b/oauth2-framework-impl/oauth2-authorization-server/src/main/resources/META-INF/public-key.pem @@ -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----- diff --git a/oauth2-framework-impl/oauth2-authorization-server/src/main/resources/data.sql b/oauth2-framework-impl/oauth2-authorization-server/src/main/resources/data.sql new file mode 100644 index 0000000000..ecda0fa2ad --- /dev/null +++ b/oauth2-framework-impl/oauth2-authorization-server/src/main/resources/data.sql @@ -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'); \ No newline at end of file diff --git a/oauth2-framework-impl/oauth2-authorization-server/src/main/webapp/WEB-INF/beans.xml b/oauth2-framework-impl/oauth2-authorization-server/src/main/webapp/WEB-INF/beans.xml new file mode 100644 index 0000000000..29595ff490 --- /dev/null +++ b/oauth2-framework-impl/oauth2-authorization-server/src/main/webapp/WEB-INF/beans.xml @@ -0,0 +1,6 @@ + + + diff --git a/oauth2-framework-impl/oauth2-authorization-server/src/main/webapp/authorize.jsp b/oauth2-framework-impl/oauth2-authorization-server/src/main/webapp/authorize.jsp new file mode 100644 index 0000000000..1004b5b8b7 --- /dev/null +++ b/oauth2-framework-impl/oauth2-authorization-server/src/main/webapp/authorize.jsp @@ -0,0 +1,54 @@ +<%@ page contentType="text/html;charset=UTF-8" language="java" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> + + + + Authorization + + + + +
+

Want to Authorize scopes for client : ${client.clientId} ?

+
+ +
+ + + + + + + + + +
Scopes : + + ${scope}
+
+
+ + +
+
+ +
+ + + diff --git a/oauth2-framework-impl/oauth2-authorization-server/src/main/webapp/error.jsp b/oauth2-framework-impl/oauth2-authorization-server/src/main/webapp/error.jsp new file mode 100644 index 0000000000..edb0bf28b7 --- /dev/null +++ b/oauth2-framework-impl/oauth2-authorization-server/src/main/webapp/error.jsp @@ -0,0 +1,25 @@ +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1" %> + + + + + + Error + + + + + + +
+

${error}

+
+ + + \ No newline at end of file diff --git a/oauth2-framework-impl/oauth2-authorization-server/src/main/webapp/login-error.jsp b/oauth2-framework-impl/oauth2-authorization-server/src/main/webapp/login-error.jsp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/oauth2-framework-impl/oauth2-authorization-server/src/main/webapp/login.jsp b/oauth2-framework-impl/oauth2-authorization-server/src/main/webapp/login.jsp new file mode 100644 index 0000000000..1d2dd93fd0 --- /dev/null +++ b/oauth2-framework-impl/oauth2-authorization-server/src/main/webapp/login.jsp @@ -0,0 +1,49 @@ +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1" %> + + + + + Login Form + + + + +
+

Login Form

+
+
+ + + + + + + + + + + + + +
Username
Password
+
+ +
+ + + \ No newline at end of file diff --git a/oauth2-framework-impl/oauth2-client/pom.xml b/oauth2-framework-impl/oauth2-client/pom.xml new file mode 100644 index 0000000000..e46a44268e --- /dev/null +++ b/oauth2-framework-impl/oauth2-client/pom.xml @@ -0,0 +1,28 @@ + + + 4.0.0 + oauth2-client + war + + + com.baeldung.oauth2 + oauth2-framework-impl + 1.0-SNAPSHOT + + + + 9180 + 9543 + + + + + + net.wasdev.wlp.maven.plugins + liberty-maven-plugin + + + + diff --git a/oauth2-framework-impl/oauth2-client/src/main/java/com/baeldung/oauth2/client/AuthorizationCodeServlet.java b/oauth2-framework-impl/oauth2-client/src/main/java/com/baeldung/oauth2/client/AuthorizationCodeServlet.java new file mode 100644 index 0000000000..a5fdaf07f2 --- /dev/null +++ b/oauth2-framework-impl/oauth2-client/src/main/java/com/baeldung/oauth2/client/AuthorizationCodeServlet.java @@ -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); + } +} diff --git a/oauth2-framework-impl/oauth2-client/src/main/java/com/baeldung/oauth2/client/CallbackServlet.java b/oauth2-framework-impl/oauth2-client/src/main/java/com/baeldung/oauth2/client/CallbackServlet.java new file mode 100644 index 0000000000..87aa8bc668 --- /dev/null +++ b/oauth2-framework-impl/oauth2-client/src/main/java/com/baeldung/oauth2/client/CallbackServlet.java @@ -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; + } +} diff --git a/oauth2-framework-impl/oauth2-client/src/main/java/com/baeldung/oauth2/client/DownstreamCallServlet.java b/oauth2-framework-impl/oauth2-client/src/main/java/com/baeldung/oauth2/client/DownstreamCallServlet.java new file mode 100644 index 0000000000..bbe850917b --- /dev/null +++ b/oauth2-framework-impl/oauth2-client/src/main/java/com/baeldung/oauth2/client/DownstreamCallServlet.java @@ -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(); + } +} diff --git a/oauth2-framework-impl/oauth2-client/src/main/liberty/config/server.xml b/oauth2-framework-impl/oauth2-client/src/main/liberty/config/server.xml new file mode 100644 index 0000000000..26dc730361 --- /dev/null +++ b/oauth2-framework-impl/oauth2-client/src/main/liberty/config/server.xml @@ -0,0 +1,19 @@ + + + + + localConnector-1.0 + cdi-2.0 + jsp-2.3 + mpConfig-1.3 + jaxrsClient-2.1 + jsonp-1.1 + + + + + + + + + diff --git a/oauth2-framework-impl/oauth2-client/src/main/resources/META-INF/microprofile-config.properties b/oauth2-framework-impl/oauth2-client/src/main/resources/META-INF/microprofile-config.properties new file mode 100644 index 0000000000..d66f4f6a74 --- /dev/null +++ b/oauth2-framework-impl/oauth2-client/src/main/resources/META-INF/microprofile-config.properties @@ -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 \ No newline at end of file diff --git a/oauth2-framework-impl/oauth2-client/src/main/webapp/WEB-INF/beans.xml b/oauth2-framework-impl/oauth2-client/src/main/webapp/WEB-INF/beans.xml new file mode 100644 index 0000000000..29595ff490 --- /dev/null +++ b/oauth2-framework-impl/oauth2-client/src/main/webapp/WEB-INF/beans.xml @@ -0,0 +1,6 @@ + + + diff --git a/oauth2-framework-impl/oauth2-client/src/main/webapp/WEB-INF/web.xml b/oauth2-framework-impl/oauth2-client/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000000..0203894c1b --- /dev/null +++ b/oauth2-framework-impl/oauth2-client/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,5 @@ + + + index.jsp + + \ No newline at end of file diff --git a/oauth2-framework-impl/oauth2-client/src/main/webapp/index.jsp b/oauth2-framework-impl/oauth2-client/src/main/webapp/index.jsp new file mode 100644 index 0000000000..23fec70f33 --- /dev/null +++ b/oauth2-framework-impl/oauth2-client/src/main/webapp/index.jsp @@ -0,0 +1,97 @@ +<%@ page import="org.eclipse.microprofile.config.Config" %> +<%@ page import="org.eclipse.microprofile.config.ConfigProvider" %> + + + + + + OAuth2 Client + + + + + +<% + Config config1 = ConfigProvider.getConfig(); +%> + +
"> + Error : ${error} +
+ +
+

OAuth2 Authorization Server

+
+

Client Registration :

+
    +
  • client_id: <%=config1.getValue("client.clientId", String.class)%> +
  • +
  • client_secret: <%=config1.getValue("client.clientSecret", String.class)%> +
  • +
  • redirect_uri: <%=config1.getValue("client.redirectUri", String.class)%> +
  • +
  • scope: <%=config1.getValue("client.scope", String.class)%> +
  • +
+

Authorization Server :

+
    +
  • authorization_uri: <%=config1.getValue("provider.authorizationUri", String.class)%> +
  • +
  • token_uri: <%=config1.getValue("provider.tokenUri", String.class)%> +
  • +
+
+
+

OAuth2 Client

+
+

Token Request :

+ + +

Token Response:

+
    +
  • access_token: ${tokenResponse.getString("access_token")}
  • +
  • scope: ${tokenResponse.getString("scope")}
  • +
  • Expires in (s): ${tokenResponse.getInt("expires_in")}
  • +
+
+
+

OAuth2 Resource Server Call

+
+ + +
+ + + \ No newline at end of file diff --git a/oauth2-framework-impl/oauth2-resource-server/pom.xml b/oauth2-framework-impl/oauth2-resource-server/pom.xml new file mode 100644 index 0000000000..9b58c33472 --- /dev/null +++ b/oauth2-framework-impl/oauth2-resource-server/pom.xml @@ -0,0 +1,40 @@ + + + 4.0.0 + oauth2-resource-server + war + + + com.baeldung.oauth2 + oauth2-framework-impl + 1.0-SNAPSHOT + + + + 9280 + 8643 + http://localhost:9080 + http://localhost:9280 + + + + + org.eclipse.microprofile.jwt + microprofile-jwt-auth-api + 1.1 + provided + + + + + + + net.wasdev.wlp.maven.plugins + liberty-maven-plugin + + + + + diff --git a/oauth2-framework-impl/oauth2-resource-server/src/main/java/com/baeldung/oauth2/resource/server/OAuth2ResourceServerApplication.java b/oauth2-framework-impl/oauth2-resource-server/src/main/java/com/baeldung/oauth2/resource/server/OAuth2ResourceServerApplication.java new file mode 100644 index 0000000000..835e80a416 --- /dev/null +++ b/oauth2-framework-impl/oauth2-resource-server/src/main/java/com/baeldung/oauth2/resource/server/OAuth2ResourceServerApplication.java @@ -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 { +} diff --git a/oauth2-framework-impl/oauth2-resource-server/src/main/java/com/baeldung/oauth2/resource/server/secure/ProtectedResource.java b/oauth2-framework-impl/oauth2-resource-server/src/main/java/com/baeldung/oauth2/resource/server/secure/ProtectedResource.java new file mode 100644 index 0000000000..300de83c6d --- /dev/null +++ b/oauth2-framework-impl/oauth2-resource-server/src/main/java/com/baeldung/oauth2/resource/server/secure/ProtectedResource.java @@ -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(); + } +} diff --git a/oauth2-framework-impl/oauth2-resource-server/src/main/liberty/config/server.xml b/oauth2-framework-impl/oauth2-resource-server/src/main/liberty/config/server.xml new file mode 100644 index 0000000000..c54d698359 --- /dev/null +++ b/oauth2-framework-impl/oauth2-resource-server/src/main/liberty/config/server.xml @@ -0,0 +1,20 @@ + + + + + localConnector-1.0 + cdi-2.0 + jaxrs-2.1 + jsonp-1.1 + mpConfig-1.3 + mpJwt-1.1 + + + + + + + + + + diff --git a/oauth2-framework-impl/oauth2-resource-server/src/main/resources/META-INF/microprofile-config.properties b/oauth2-framework-impl/oauth2-resource-server/src/main/resources/META-INF/microprofile-config.properties new file mode 100644 index 0000000000..be6919ec50 --- /dev/null +++ b/oauth2-framework-impl/oauth2-resource-server/src/main/resources/META-INF/microprofile-config.properties @@ -0,0 +1,2 @@ +mp.jwt.verify.publickey.location=/META-INF/public-key.pem +mp.jwt.verify.issuer=http://localhost:9080 \ No newline at end of file diff --git a/oauth2-framework-impl/oauth2-resource-server/src/main/resources/META-INF/public-key.pem b/oauth2-framework-impl/oauth2-resource-server/src/main/resources/META-INF/public-key.pem new file mode 100644 index 0000000000..f53ceca446 --- /dev/null +++ b/oauth2-framework-impl/oauth2-resource-server/src/main/resources/META-INF/public-key.pem @@ -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----- diff --git a/oauth2-framework-impl/oauth2-resource-server/src/main/webapp/WEB-INF/beans.xml b/oauth2-framework-impl/oauth2-resource-server/src/main/webapp/WEB-INF/beans.xml new file mode 100644 index 0000000000..2777559c20 --- /dev/null +++ b/oauth2-framework-impl/oauth2-resource-server/src/main/webapp/WEB-INF/beans.xml @@ -0,0 +1,6 @@ + + + diff --git a/oauth2-framework-impl/oauth2-resource-server/src/main/webapp/index.html b/oauth2-framework-impl/oauth2-resource-server/src/main/webapp/index.html new file mode 100644 index 0000000000..7ea29c1097 --- /dev/null +++ b/oauth2-framework-impl/oauth2-resource-server/src/main/webapp/index.html @@ -0,0 +1,37 @@ + + + + + Eclipse MicroProfile demo + + + +

MicroProfile

+ +Hello JAX-RS endpoint
+ + +

Config

+Injected config values
+Config values by lookup
+ + + + + + + + + +

JWT Auth

+Look at readme.md on how to test protected endpoint. + + + + + + + + + + \ No newline at end of file diff --git a/oauth2-framework-impl/pom.xml b/oauth2-framework-impl/pom.xml new file mode 100644 index 0000000000..281103660b --- /dev/null +++ b/oauth2-framework-impl/pom.xml @@ -0,0 +1,93 @@ + + + 4.0.0 + com.baeldung.oauth2 + oauth2-framework-impl + 1.0-SNAPSHOT + pom + + + 1.8 + 1.8 + false + RELEASE + 2.6.4 + + + + + javax + javaee-web-api + 8.0 + provided + + + org.eclipse.microprofile.config + microprofile-config-api + 1.3 + provided + + + + + ${project.artifactId} + + + ${basedir}/src/main/resources + + **/*.* + + + + true + ${basedir}/src/main/liberty + + **/*.* + + + + + + + net.wasdev.wlp.maven.plugins + liberty-maven-plugin + ${openliberty.maven.version} + + + package-server + package + + create-server + install-apps + + + target/wlp-package + + + + + + io.openliberty + openliberty-webProfile8 + ${openliberty.version} + zip + + target/classes/config/server.xml + ${project.build.directory}/${project.build.finalName}.war + project + ${project.basedir}/src/main/liberty/server + apps + true + + + + + + + oauth2-authorization-server + oauth2-resource-server + oauth2-client + +