Merge pull request #7397 from eelhazati/master
BAEL-2887: add resfresh token feature
This commit is contained in:
commit
f9281a1011
@ -15,15 +15,14 @@ import javax.ws.rs.core.HttpHeaders;
|
|||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
import javax.ws.rs.core.MultivaluedMap;
|
import javax.ws.rs.core.MultivaluedMap;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
@Path("token")
|
@Path("token")
|
||||||
public class TokenEndpoint {
|
public class TokenEndpoint {
|
||||||
|
|
||||||
List<String> supportedGrantTypes = Collections.singletonList("authorization_code");
|
List<String> supportedGrantTypes = Arrays.asList("authorization_code", "refresh_token");
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private AppDataRepository appDataRepository;
|
private AppDataRepository appDataRepository;
|
||||||
@ -39,36 +38,36 @@ public class TokenEndpoint {
|
|||||||
|
|
||||||
//Check grant_type params
|
//Check grant_type params
|
||||||
String grantType = params.getFirst("grant_type");
|
String grantType = params.getFirst("grant_type");
|
||||||
Objects.requireNonNull(grantType, "grant_type params is required");
|
if (grantType == null || grantType.isEmpty())
|
||||||
if (!supportedGrantTypes.contains(grantType)) {
|
return responseError("Invalid_request", "grant_type is required", Response.Status.BAD_REQUEST);
|
||||||
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();
|
|
||||||
|
|
||||||
|
if (!supportedGrantTypes.contains(grantType)) {
|
||||||
|
return responseError("unsupported_grant_type", "grant_type should be one of :" + supportedGrantTypes, Response.Status.BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Client Authentication
|
//Client Authentication
|
||||||
String[] clientCredentials = extract(authHeader);
|
String[] clientCredentials = extract(authHeader);
|
||||||
|
if (clientCredentials.length != 2) {
|
||||||
|
return responseError("Invalid_request", "Bad Credentials client_id/client_secret", Response.Status.BAD_REQUEST);
|
||||||
|
}
|
||||||
String clientId = clientCredentials[0];
|
String clientId = clientCredentials[0];
|
||||||
String clientSecret = clientCredentials[1];
|
|
||||||
Client client = appDataRepository.getClient(clientId);
|
Client client = appDataRepository.getClient(clientId);
|
||||||
if (client == null || clientSecret == null || !clientSecret.equals(client.getClientSecret())) {
|
if (client == null) {
|
||||||
JsonObject error = Json.createObjectBuilder()
|
return responseError("Invalid_request", "Invalid client_id", Response.Status.BAD_REQUEST);
|
||||||
.add("error", "invalid_client")
|
}
|
||||||
.build();
|
String clientSecret = clientCredentials[1];
|
||||||
return Response.status(Response.Status.UNAUTHORIZED)
|
if (!clientSecret.equals(client.getClientSecret())) {
|
||||||
.entity(error).build();
|
return responseError("Invalid_request", "Invalid client_secret", Response.Status.UNAUTHORIZED);
|
||||||
}
|
}
|
||||||
|
|
||||||
AuthorizationGrantTypeHandler authorizationGrantTypeHandler = authorizationGrantTypeHandlers.select(NamedLiteral.of(grantType)).get();
|
AuthorizationGrantTypeHandler authorizationGrantTypeHandler = authorizationGrantTypeHandlers.select(NamedLiteral.of(grantType)).get();
|
||||||
JsonObject tokenResponse = null;
|
JsonObject tokenResponse = null;
|
||||||
try {
|
try {
|
||||||
tokenResponse = authorizationGrantTypeHandler.createAccessToken(clientId, params);
|
tokenResponse = authorizationGrantTypeHandler.createAccessToken(clientId, params);
|
||||||
|
} catch (WebApplicationException e) {
|
||||||
|
return e.getResponse();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
return responseError("Invalid_request", "Can't get token", Response.Status.INTERNAL_SERVER_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Response.ok(tokenResponse)
|
return Response.ok(tokenResponse)
|
||||||
@ -81,6 +80,15 @@ public class TokenEndpoint {
|
|||||||
if (authHeader != null && authHeader.startsWith("Basic ")) {
|
if (authHeader != null && authHeader.startsWith("Basic ")) {
|
||||||
return new String(Base64.getDecoder().decode(authHeader.substring(6))).split(":");
|
return new String(Base64.getDecoder().decode(authHeader.substring(6))).split(":");
|
||||||
}
|
}
|
||||||
return null;
|
return new String[]{};
|
||||||
|
}
|
||||||
|
|
||||||
|
private Response responseError(String error, String errorDescription, Response.Status status) {
|
||||||
|
JsonObject errorResponse = Json.createObjectBuilder()
|
||||||
|
.add("error", error)
|
||||||
|
.add("error_description", errorDescription)
|
||||||
|
.build();
|
||||||
|
return Response.status(status)
|
||||||
|
.entity(errorResponse).build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,87 @@
|
|||||||
|
package com.baeldung.oauth2.authorization.server.handler;
|
||||||
|
|
||||||
|
import com.baeldung.oauth2.authorization.server.PEMKeyUtils;
|
||||||
|
import com.nimbusds.jose.*;
|
||||||
|
import com.nimbusds.jose.crypto.RSASSASigner;
|
||||||
|
import com.nimbusds.jose.crypto.RSASSAVerifier;
|
||||||
|
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 java.time.Instant;
|
||||||
|
import java.time.temporal.ChronoUnit;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public abstract class AbstractGrantTypeHandler implements AuthorizationGrantTypeHandler {
|
||||||
|
|
||||||
|
//Always RSA 256, but could be parametrized
|
||||||
|
protected JWSHeader jwsHeader = new JWSHeader.Builder(JWSAlgorithm.RS256).type(JOSEObjectType.JWT).build();
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
protected Config config;
|
||||||
|
|
||||||
|
//30 min
|
||||||
|
protected Long expiresInMin = 30L;
|
||||||
|
|
||||||
|
protected JWSVerifier getJWSVerifier() throws Exception {
|
||||||
|
String verificationkey = config.getValue("verificationkey", String.class);
|
||||||
|
String pemEncodedRSAPublicKey = PEMKeyUtils.readKeyAsString(verificationkey);
|
||||||
|
RSAKey rsaPublicKey = (RSAKey) JWK.parseFromPEMEncodedObjects(pemEncodedRSAPublicKey);
|
||||||
|
return new RSASSAVerifier(rsaPublicKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected JWSSigner getJwsSigner() throws Exception {
|
||||||
|
String signingkey = config.getValue("signingkey", String.class);
|
||||||
|
String pemEncodedRSAPrivateKey = PEMKeyUtils.readKeyAsString(signingkey);
|
||||||
|
RSAKey rsaKey = (RSAKey) JWK.parseFromPEMEncodedObjects(pemEncodedRSAPrivateKey);
|
||||||
|
return new RSASSASigner(rsaKey.toRSAPrivateKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getAccessToken(String clientId, String subject, String approvedScope) throws Exception {
|
||||||
|
//4. Signing
|
||||||
|
JWSSigner jwsSigner = getJwsSigner();
|
||||||
|
|
||||||
|
Instant now = Instant.now();
|
||||||
|
//Long expiresInMin = 30L;
|
||||||
|
Date expirationTime = Date.from(now.plus(expiresInMin, ChronoUnit.MINUTES));
|
||||||
|
|
||||||
|
//3. JWT Payload or claims
|
||||||
|
JWTClaimsSet jwtClaims = new JWTClaimsSet.Builder()
|
||||||
|
.issuer("http://localhost:9080")
|
||||||
|
.subject(subject)
|
||||||
|
.claim("upn", subject)
|
||||||
|
.claim("client_id", clientId)
|
||||||
|
.audience("http://localhost:9280")
|
||||||
|
.claim("scope", approvedScope)
|
||||||
|
.claim("groups", Arrays.asList(approvedScope.split(" ")))
|
||||||
|
.expirationTime(expirationTime) // expires in 30 minutes
|
||||||
|
.notBeforeTime(Date.from(now))
|
||||||
|
.issueTime(Date.from(now))
|
||||||
|
.jwtID(UUID.randomUUID().toString())
|
||||||
|
.build();
|
||||||
|
SignedJWT signedJWT = new SignedJWT(jwsHeader, jwtClaims);
|
||||||
|
signedJWT.sign(jwsSigner);
|
||||||
|
return signedJWT.serialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getRefreshToken(String clientId, String subject, String approvedScope) throws Exception {
|
||||||
|
JWSSigner jwsSigner = getJwsSigner();
|
||||||
|
Instant now = Instant.now();
|
||||||
|
//6.Build refresh token
|
||||||
|
JWTClaimsSet refreshTokenClaims = new JWTClaimsSet.Builder()
|
||||||
|
.subject(subject)
|
||||||
|
.claim("client_id", clientId)
|
||||||
|
.claim("scope", approvedScope)
|
||||||
|
//refresh token for 1 day.
|
||||||
|
.expirationTime(Date.from(now.plus(1, ChronoUnit.DAYS)))
|
||||||
|
.build();
|
||||||
|
SignedJWT signedRefreshToken = new SignedJWT(jwsHeader, refreshTokenClaims);
|
||||||
|
signedRefreshToken.sign(jwsSigner);
|
||||||
|
return signedRefreshToken.serialize();
|
||||||
|
}
|
||||||
|
}
|
@ -1,18 +1,7 @@
|
|||||||
package com.baeldung.oauth2.authorization.server.handler;
|
package com.baeldung.oauth2.authorization.server.handler;
|
||||||
|
|
||||||
import com.baeldung.oauth2.authorization.server.PEMKeyUtils;
|
|
||||||
import com.baeldung.oauth2.authorization.server.model.AuthorizationCode;
|
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.inject.Named;
|
||||||
import javax.json.Json;
|
import javax.json.Json;
|
||||||
import javax.json.JsonObject;
|
import javax.json.JsonObject;
|
||||||
@ -20,22 +9,14 @@ import javax.persistence.EntityManager;
|
|||||||
import javax.persistence.PersistenceContext;
|
import javax.persistence.PersistenceContext;
|
||||||
import javax.ws.rs.WebApplicationException;
|
import javax.ws.rs.WebApplicationException;
|
||||||
import javax.ws.rs.core.MultivaluedMap;
|
import javax.ws.rs.core.MultivaluedMap;
|
||||||
import java.time.Instant;
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.temporal.ChronoUnit;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
@Named("authorization_code")
|
@Named("authorization_code")
|
||||||
public class AuthorizationCodeGrantTypeHandler implements AuthorizationGrantTypeHandler {
|
public class AuthorizationCodeGrantTypeHandler extends AbstractGrantTypeHandler {
|
||||||
|
|
||||||
@PersistenceContext
|
@PersistenceContext
|
||||||
private EntityManager entityManager;
|
private EntityManager entityManager;
|
||||||
|
|
||||||
@Inject
|
|
||||||
private Config config;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JsonObject createAccessToken(String clientId, MultivaluedMap<String, String> params) throws Exception {
|
public JsonObject createAccessToken(String clientId, MultivaluedMap<String, String> params) throws Exception {
|
||||||
//1. code is required
|
//1. code is required
|
||||||
@ -58,42 +39,16 @@ public class AuthorizationCodeGrantTypeHandler implements AuthorizationGrantType
|
|||||||
throw new WebApplicationException("invalid_grant");
|
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
|
//3. JWT Payload or claims
|
||||||
JWTClaimsSet jwtClaims = new JWTClaimsSet.Builder()
|
String accessToken = getAccessToken(clientId, authorizationCode.getUserId(), authorizationCode.getApprovedScopes());
|
||||||
.issuer("http://localhost:9080")
|
String refreshToken = getRefreshToken(clientId, authorizationCode.getUserId(), authorizationCode.getApprovedScopes());
|
||||||
.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()
|
return Json.createObjectBuilder()
|
||||||
.add("token_type", "Bearer")
|
.add("token_type", "Bearer")
|
||||||
.add("access_token", accessToken)
|
.add("access_token", accessToken)
|
||||||
.add("expires_in", expiresInMin * 60)
|
.add("expires_in", expiresInMin * 60)
|
||||||
.add("scope", authorizationCode.getApprovedScopes())
|
.add("scope", authorizationCode.getApprovedScopes())
|
||||||
|
.add("refresh_token", refreshToken)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,72 @@
|
|||||||
|
package com.baeldung.oauth2.authorization.server.handler;
|
||||||
|
|
||||||
|
import com.nimbusds.jose.JWSVerifier;
|
||||||
|
import com.nimbusds.jwt.SignedJWT;
|
||||||
|
|
||||||
|
import javax.inject.Named;
|
||||||
|
import javax.json.Json;
|
||||||
|
import javax.json.JsonObject;
|
||||||
|
import javax.ws.rs.WebApplicationException;
|
||||||
|
import javax.ws.rs.core.MultivaluedMap;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Named("refresh_token")
|
||||||
|
public class RefreshTokenGrantTypeHandler extends AbstractGrantTypeHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonObject createAccessToken(String clientId, MultivaluedMap<String, String> params) throws Exception {
|
||||||
|
String refreshToken = params.getFirst("refresh_token");
|
||||||
|
if (refreshToken == null || "".equals(refreshToken)) {
|
||||||
|
throw new WebApplicationException("invalid_grant");
|
||||||
|
}
|
||||||
|
|
||||||
|
//Decode refresh token
|
||||||
|
SignedJWT signedRefreshToken = SignedJWT.parse(refreshToken);
|
||||||
|
JWSVerifier verifier = getJWSVerifier();
|
||||||
|
|
||||||
|
if (!signedRefreshToken.verify(verifier)) {
|
||||||
|
throw new WebApplicationException("Invalid refresh token.");
|
||||||
|
}
|
||||||
|
if (!(new Date().before(signedRefreshToken.getJWTClaimsSet().getExpirationTime()))) {
|
||||||
|
throw new WebApplicationException("Refresh token expired.");
|
||||||
|
}
|
||||||
|
String refreshTokenClientId = signedRefreshToken.getJWTClaimsSet().getStringClaim("client_id");
|
||||||
|
if (!clientId.equals(refreshTokenClientId)) {
|
||||||
|
throw new WebApplicationException("Invalid client_id.");
|
||||||
|
}
|
||||||
|
|
||||||
|
//At this point, the refresh token is valid and not yet expired
|
||||||
|
//So create a new access token from it.
|
||||||
|
String subject = signedRefreshToken.getJWTClaimsSet().getSubject();
|
||||||
|
String approvedScopes = signedRefreshToken.getJWTClaimsSet().getStringClaim("scope");
|
||||||
|
|
||||||
|
String requestedScopes = params.getFirst("scope");
|
||||||
|
if (requestedScopes != null && !requestedScopes.isEmpty()) {
|
||||||
|
Set<String> rScopes = new HashSet(Arrays.asList(requestedScopes.split(" ")));
|
||||||
|
Set<String> aScopes = new HashSet(Arrays.asList(approvedScopes.split(" ")));
|
||||||
|
if (!aScopes.containsAll(rScopes)) {
|
||||||
|
JsonObject error = Json.createObjectBuilder()
|
||||||
|
.add("error", "Invalid_request")
|
||||||
|
.add("error_description", "Requested scopes should be a subset of the original scopes.")
|
||||||
|
.build();
|
||||||
|
Response response = Response.status(Response.Status.BAD_REQUEST).entity(error).build();
|
||||||
|
throw new WebApplicationException(response);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
requestedScopes = approvedScopes;
|
||||||
|
}
|
||||||
|
|
||||||
|
String accessToken = getAccessToken(clientId, subject, requestedScopes);
|
||||||
|
return Json.createObjectBuilder()
|
||||||
|
.add("token_type", "Bearer")
|
||||||
|
.add("access_token", accessToken)
|
||||||
|
.add("expires_in", expiresInMin * 60)
|
||||||
|
.add("scope", requestedScopes)
|
||||||
|
.add("refresh_token", refreshToken)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package com.baeldung.oauth2.client;
|
||||||
|
|
||||||
|
import javax.servlet.RequestDispatcher;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServlet;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
public abstract class AbstractServlet extends HttpServlet {
|
||||||
|
|
||||||
|
protected void dispatch(String location, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||||
|
RequestDispatcher requestDispatcher = request.getRequestDispatcher(location);
|
||||||
|
requestDispatcher.forward(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getAuthorizationHeaderValue(String clientId, String clientSecret) {
|
||||||
|
String token = clientId + ":" + clientSecret;
|
||||||
|
String encodedString = Base64.getEncoder().encodeToString(token.getBytes());
|
||||||
|
return "Basic " + encodedString;
|
||||||
|
}
|
||||||
|
}
|
@ -4,10 +4,8 @@ import org.eclipse.microprofile.config.Config;
|
|||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.json.JsonObject;
|
import javax.json.JsonObject;
|
||||||
import javax.servlet.RequestDispatcher;
|
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
import javax.servlet.annotation.WebServlet;
|
import javax.servlet.annotation.WebServlet;
|
||||||
import javax.servlet.http.HttpServlet;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import javax.ws.rs.client.Client;
|
import javax.ws.rs.client.Client;
|
||||||
@ -18,10 +16,9 @@ import javax.ws.rs.core.Form;
|
|||||||
import javax.ws.rs.core.HttpHeaders;
|
import javax.ws.rs.core.HttpHeaders;
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Base64;
|
|
||||||
|
|
||||||
@WebServlet(urlPatterns = "/callback")
|
@WebServlet(urlPatterns = "/callback")
|
||||||
public class CallbackServlet extends HttpServlet {
|
public class CallbackServlet extends AbstractServlet {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private Config config;
|
private Config config;
|
||||||
@ -29,6 +26,9 @@ public class CallbackServlet extends HttpServlet {
|
|||||||
@Override
|
@Override
|
||||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||||
|
|
||||||
|
String clientId = config.getValue("client.clientId", String.class);
|
||||||
|
String clientSecret = config.getValue("client.clientSecret", String.class);
|
||||||
|
|
||||||
//Error:
|
//Error:
|
||||||
String error = request.getParameter("error");
|
String error = request.getParameter("error");
|
||||||
if (error != null) {
|
if (error != null) {
|
||||||
@ -53,24 +53,15 @@ public class CallbackServlet extends HttpServlet {
|
|||||||
form.param("code", code);
|
form.param("code", code);
|
||||||
form.param("redirect_uri", config.getValue("client.redirectUri", String.class));
|
form.param("redirect_uri", config.getValue("client.redirectUri", String.class));
|
||||||
|
|
||||||
JsonObject tokenResponse = target.request(MediaType.APPLICATION_JSON_TYPE)
|
try {
|
||||||
.header(HttpHeaders.AUTHORIZATION, getAuthorizationHeaderValue())
|
JsonObject tokenResponse = target.request(MediaType.APPLICATION_JSON_TYPE)
|
||||||
.post(Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE), JsonObject.class);
|
.header(HttpHeaders.AUTHORIZATION, getAuthorizationHeaderValue(clientId, clientSecret))
|
||||||
|
.post(Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE), JsonObject.class);
|
||||||
request.getSession().setAttribute("tokenResponse", tokenResponse);
|
request.getSession().setAttribute("tokenResponse", tokenResponse);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
System.out.println(ex.getMessage());
|
||||||
|
request.setAttribute("error", ex.getMessage());
|
||||||
|
}
|
||||||
dispatch("/", request, response);
|
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,57 @@
|
|||||||
|
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.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 javax.ws.rs.core.Response;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@WebServlet(urlPatterns = "/refreshtoken")
|
||||||
|
public class RefreshTokenServlet extends AbstractServlet {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private Config config;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||||
|
|
||||||
|
String clientId = config.getValue("client.clientId", String.class);
|
||||||
|
String clientSecret = config.getValue("client.clientSecret", String.class);
|
||||||
|
|
||||||
|
JsonObject actualTokenResponse = (JsonObject) request.getSession().getAttribute("tokenResponse");
|
||||||
|
Client client = ClientBuilder.newClient();
|
||||||
|
WebTarget target = client.target(config.getValue("provider.tokenUri", String.class));
|
||||||
|
|
||||||
|
Form form = new Form();
|
||||||
|
form.param("grant_type", "refresh_token");
|
||||||
|
form.param("refresh_token", actualTokenResponse.getString("refresh_token"));
|
||||||
|
|
||||||
|
String scope = request.getParameter("scope");
|
||||||
|
if (scope != null && !scope.isEmpty()) {
|
||||||
|
form.param("scope", scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
Response jaxrsResponse = target.request(MediaType.APPLICATION_JSON_TYPE)
|
||||||
|
.header(HttpHeaders.AUTHORIZATION, getAuthorizationHeaderValue(clientId, clientSecret))
|
||||||
|
.post(Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE), Response.class);
|
||||||
|
JsonObject tokenResponse = jaxrsResponse.readEntity(JsonObject.class);
|
||||||
|
if (jaxrsResponse.getStatus() == 200) {
|
||||||
|
request.getSession().setAttribute("tokenResponse", tokenResponse);
|
||||||
|
} else {
|
||||||
|
request.setAttribute("error", tokenResponse.getString("error_description", "error!"));
|
||||||
|
}
|
||||||
|
dispatch("/", request, response);
|
||||||
|
}
|
||||||
|
}
|
@ -10,6 +10,7 @@
|
|||||||
body {
|
body {
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type=text], input[type=password] {
|
input[type=text], input[type=password] {
|
||||||
width: 75%;
|
width: 75%;
|
||||||
padding: 4px 0px;
|
padding: 4px 0px;
|
||||||
@ -17,6 +18,7 @@
|
|||||||
border: 1px solid #502bcc;
|
border: 1px solid #502bcc;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container-error {
|
.container-error {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
border: 1px solid #cc102a;
|
border: 1px solid #cc102a;
|
||||||
@ -25,6 +27,7 @@
|
|||||||
margin-left: 25px;
|
margin-left: 25px;
|
||||||
margin-bottom: 25px;
|
margin-bottom: 25px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
border: 1px solid #130ecc;
|
border: 1px solid #130ecc;
|
||||||
@ -81,8 +84,20 @@
|
|||||||
<li>access_token: ${tokenResponse.getString("access_token")}</li>
|
<li>access_token: ${tokenResponse.getString("access_token")}</li>
|
||||||
<li>scope: ${tokenResponse.getString("scope")}</li>
|
<li>scope: ${tokenResponse.getString("scope")}</li>
|
||||||
<li>Expires in (s): ${tokenResponse.getInt("expires_in")}</li>
|
<li>Expires in (s): ${tokenResponse.getInt("expires_in")}</li>
|
||||||
|
<li>refresh_token: ${tokenResponse.getString("refresh_token")}</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<span><h4>Refresh Token</h4></span>
|
||||||
|
<hr>
|
||||||
|
<ul>
|
||||||
|
<li><a href="refreshtoken">Refresh token (original scope)</a></li>
|
||||||
|
<li><a href="refreshtoken?scope=resource.read">Refresh token (scope: resource.read)</a></li>
|
||||||
|
<li><a href="refreshtoken?scope=resource.write">Refresh token (scope: resource.write)</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<span><h4>OAuth2 Resource Server Call</h4></span>
|
<span><h4>OAuth2 Resource Server Call</h4></span>
|
||||||
<hr>
|
<hr>
|
||||||
@ -90,7 +105,6 @@
|
|||||||
<li><a href="downstream?action=read">Read Protected Resource</a></li>
|
<li><a href="downstream?action=read">Read Protected Resource</a></li>
|
||||||
<li><a href="downstream?action=write">Write Protected Resource</a></li>
|
<li><a href="downstream?action=write">Write Protected Resource</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user