From 1402894515f7e35b85fd2be8ccfab257c12b922f Mon Sep 17 00:00:00 2001 From: YuriyZ Date: Thu, 27 Oct 2022 14:42:02 +0300 Subject: [PATCH] feat(jans-auth-server): added token exchange grant Native SSO #2518 --- .../io/jans/as/model/config/Constants.java | 1 + .../model/configuration/AppConfiguration.java | 10 ++ .../authorize/ws/rs/AuthzRequestService.java | 2 +- .../model/common/AuthorizationGrantList.java | 13 ++ .../model/common/AuthorizationGrantType.java | 2 + .../model/common/IAuthorizationGrantList.java | 2 + .../model/common/TokenExchangeGrant.java | 23 ++++ .../model/token/HandleTokenFactory.java | 4 + .../token/ws/rs/TokenInternalService.java | 46 ++++++++ .../token/ws/rs/TokenRestWebServiceImpl.java | 111 ++++++++++++++---- .../ws/rs/TokenRestWebServiceValidator.java | 70 +++++++++++ .../rs/TokenRestWebServiceValidatorTest.java | 71 +++++++++++ 12 files changed, 329 insertions(+), 26 deletions(-) create mode 100644 jans-auth-server/server/src/main/java/io/jans/as/server/model/common/TokenExchangeGrant.java create mode 100644 jans-auth-server/server/src/main/java/io/jans/as/server/token/ws/rs/TokenInternalService.java diff --git a/jans-auth-server/model/src/main/java/io/jans/as/model/config/Constants.java b/jans-auth-server/model/src/main/java/io/jans/as/model/config/Constants.java index 38ebdd6efbd..569edd95521 100644 --- a/jans-auth-server/model/src/main/java/io/jans/as/model/config/Constants.java +++ b/jans-auth-server/model/src/main/java/io/jans/as/model/config/Constants.java @@ -51,6 +51,7 @@ private Constants() { public static final String WWW_AUTHENTICATE = "WWW-Authenticate"; public static final String SUBJECT_TOKEN_TYPE_ID_TOKEN = "urn:ietf:params:oauth:token-type:id_token"; public static final String ACTOR_TOKEN_TYPE_DEVICE_SECRET = "urn:x-oath:params:oauth:token-type:device-secret"; + public static final String TOKEN_TYPE_ACCESS_TOKEN = "urn:ietf:params:oauth:token-type:access_token"; public static final String CONTENT_TYPE_APPLICATION_JSON_UTF_8 = "application/json;charset=UTF-8"; diff --git a/jans-auth-server/model/src/main/java/io/jans/as/model/configuration/AppConfiguration.java b/jans-auth-server/model/src/main/java/io/jans/as/model/configuration/AppConfiguration.java index 74f6de16c01..7cbe69efe8d 100644 --- a/jans-auth-server/model/src/main/java/io/jans/as/model/configuration/AppConfiguration.java +++ b/jans-auth-server/model/src/main/java/io/jans/as/model/configuration/AppConfiguration.java @@ -249,6 +249,7 @@ public class AppConfiguration implements Configuration { private Set authorizationRequestCustomAllowedParameters; private Boolean openidScopeBackwardCompatibility = false; private Boolean disableU2fEndpoint = false; + private Boolean rotateDeviceSecret = false; private Boolean dcrSignatureValidationEnabled = false; private String dcrSignatureValidationSharedSecret; @@ -345,6 +346,15 @@ public void setAllowAllValueForRevokeEndpoint(Boolean allowAllValueForRevokeEndp this.allowAllValueForRevokeEndpoint = allowAllValueForRevokeEndpoint; } + public Boolean getRotateDeviceSecret() { + if (rotateDeviceSecret == null) rotateDeviceSecret = false; + return rotateDeviceSecret; + } + + public void setRotateDeviceSecret(Boolean rotateDeviceSecret) { + this.rotateDeviceSecret = rotateDeviceSecret; + } + public Boolean getRequirePkce() { if (requirePkce == null) requirePkce = false; return requirePkce; diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/authorize/ws/rs/AuthzRequestService.java b/jans-auth-server/server/src/main/java/io/jans/as/server/authorize/ws/rs/AuthzRequestService.java index aba3603ac29..f6304b7e3ba 100644 --- a/jans-auth-server/server/src/main/java/io/jans/as/server/authorize/ws/rs/AuthzRequestService.java +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/authorize/ws/rs/AuthzRequestService.java @@ -116,7 +116,7 @@ public void addDeviceSecretToSession(AuthzRequest authzRequest, SessionId sessio return; } - final String newDeviceSecret = HandleTokenFactory.generateHandleToken(); + final String newDeviceSecret = HandleTokenFactory.generateDeviceSecret(); final List deviceSecrets = sessionId.getDeviceSecrets(); deviceSecrets.add(newDeviceSecret); diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/AuthorizationGrantList.java b/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/AuthorizationGrantList.java index 3ee753e19d7..7aab5392bf1 100644 --- a/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/AuthorizationGrantList.java +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/AuthorizationGrantList.java @@ -130,6 +130,13 @@ public ResourceOwnerPasswordCredentialsGrant createResourceOwnerPasswordCredenti return grant; } + @Override + public TokenExchangeGrant createTokenExchangeGrant(User user, Client client) { + TokenExchangeGrant grant = grantInstance.select(TokenExchangeGrant.class).get(); + grant.init(user, client); + return grant; + } + @Override public CIBAGrant createCIBAGrant(CibaRequestCacheControl request) { CIBAGrant grant = grantInstance.select(CIBAGrant.class).get(); @@ -308,6 +315,12 @@ public AuthorizationGrant asGrant(TokenEntity tokenEntity) { result = deviceCodeGrant; break; + case TOKEN_EXCHANGE: + TokenExchangeGrant tokenExchangeGrant = grantInstance.select(TokenExchangeGrant.class).get(); + tokenExchangeGrant.init(user, AuthorizationGrantType.TOKEN_EXCHANGE, client, tokenEntity.getCreationDate()); + + result = tokenExchangeGrant; + break; default: return null; } diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/AuthorizationGrantType.java b/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/AuthorizationGrantType.java index 617c5e8f7d8..9075753ebf9 100644 --- a/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/AuthorizationGrantType.java +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/AuthorizationGrantType.java @@ -61,6 +61,8 @@ public enum AuthorizationGrantType implements HasParamName { * grant types are not available (such as an authorization code). */ RESOURCE_OWNER_PASSWORD_CREDENTIALS("resource_owner_password_credentials"), + + TOKEN_EXCHANGE("urn:ietf:params:oauth:grant-type:token-exchange"), /** * An extension grant for Client Initiated Backchannel Authentication. */ diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/IAuthorizationGrantList.java b/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/IAuthorizationGrantList.java index 712384bd930..2402aef640c 100644 --- a/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/IAuthorizationGrantList.java +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/IAuthorizationGrantList.java @@ -31,6 +31,8 @@ public interface IAuthorizationGrantList { ResourceOwnerPasswordCredentialsGrant createResourceOwnerPasswordCredentialsGrant(User user, Client client); + TokenExchangeGrant createTokenExchangeGrant(User user, Client client); + CIBAGrant createCIBAGrant(CibaRequestCacheControl request); AuthorizationCodeGrant getAuthorizationCodeGrant(String authorizationCode); diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/TokenExchangeGrant.java b/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/TokenExchangeGrant.java new file mode 100644 index 00000000000..b5093e47099 --- /dev/null +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/TokenExchangeGrant.java @@ -0,0 +1,23 @@ +package io.jans.as.server.model.common; + +import io.jans.as.common.model.common.User; +import io.jans.as.common.model.registration.Client; +import io.jans.as.model.common.GrantType; + +/** + * @author Yuriy Z + */ +public class TokenExchangeGrant extends AuthorizationGrant { + + public TokenExchangeGrant() { + } + + public void init(User user, Client client) { + super.init(user, AuthorizationGrantType.TOKEN_EXCHANGE, client, null); + } + + @Override + public GrantType getGrantType() { + return GrantType.TOKEN_EXCHANGE; + } +} diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/model/token/HandleTokenFactory.java b/jans-auth-server/server/src/main/java/io/jans/as/server/model/token/HandleTokenFactory.java index 451e1f40ad7..0155b8cd10d 100644 --- a/jans-auth-server/server/src/main/java/io/jans/as/server/model/token/HandleTokenFactory.java +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/model/token/HandleTokenFactory.java @@ -40,4 +40,8 @@ public class HandleTokenFactory { public static String generateHandleToken() { return UUID.randomUUID().toString(); } + + public static String generateDeviceSecret() { + return UUID.randomUUID().toString(); + } } \ No newline at end of file diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/token/ws/rs/TokenInternalService.java b/jans-auth-server/server/src/main/java/io/jans/as/server/token/ws/rs/TokenInternalService.java new file mode 100644 index 00000000000..c550677983e --- /dev/null +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/token/ws/rs/TokenInternalService.java @@ -0,0 +1,46 @@ +package io.jans.as.server.token.ws.rs; + +import io.jans.as.common.model.session.SessionId; +import io.jans.as.model.configuration.AppConfiguration; +import io.jans.as.server.model.token.HandleTokenFactory; +import io.jans.as.server.service.SessionIdService; +import jakarta.ejb.Stateless; +import jakarta.inject.Inject; +import jakarta.inject.Named; +import org.apache.commons.lang3.BooleanUtils; +import org.slf4j.Logger; + +import java.util.List; + +/** + * @author Yuriy Z + */ +@Stateless +@Named +public class TokenInternalService { + + @Inject + private Logger log; + + @Inject + private AppConfiguration appConfiguration; + + @Inject + private SessionIdService sessionIdService; + + public String rotateDeviceSecret(SessionId sessionId, String actorToken) { + if (BooleanUtils.isFalse(appConfiguration.getRotateDeviceSecret())) { + return null; + } + + String newDeviceSecret = HandleTokenFactory.generateDeviceSecret(); + + final List deviceSecrets = sessionId.getDeviceSecrets(); + deviceSecrets.remove(actorToken); + deviceSecrets.add(newDeviceSecret); + + sessionIdService.updateSessionId(sessionId, false); + + return newDeviceSecret; + } +} diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/token/ws/rs/TokenRestWebServiceImpl.java b/jans-auth-server/server/src/main/java/io/jans/as/server/token/ws/rs/TokenRestWebServiceImpl.java index 69f353ff81b..6de092a904d 100644 --- a/jans-auth-server/server/src/main/java/io/jans/as/server/token/ws/rs/TokenRestWebServiceImpl.java +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/token/ws/rs/TokenRestWebServiceImpl.java @@ -66,6 +66,7 @@ import java.util.Date; import java.util.function.Function; +import static com.google.common.base.Preconditions.checkNotNull; import static io.jans.as.model.config.Constants.*; import static org.apache.commons.lang.BooleanUtils.isFalse; import static org.apache.commons.lang.BooleanUtils.isTrue; @@ -134,6 +135,9 @@ public class TokenRestWebServiceImpl implements TokenRestWebService { @Inject private TokenRestWebServiceValidator tokenRestWebServiceValidator; + @Inject + private TokenInternalService tokenInternalService; + @Override public Response requestAccessToken(String grantType, String code, String redirectUri, String username, String password, String scope, @@ -201,7 +205,7 @@ public Response requestAccessToken(String grantType, String code, } else if (gt == GrantType.DEVICE_CODE) { return processDeviceCodeGrantType(executionContext, deviceCode, scope); } else if (gt == GrantType.TOKEN_EXCHANGE) { - return processTokenExchange(scope, executionContext); + return processTokenExchange(scope, idTokenPreProcessing, executionContext); } } catch (WebApplicationException e) { throw e; @@ -213,19 +217,70 @@ public Response requestAccessToken(String grantType, String code, throw new WebApplicationException(tokenRestWebServiceValidator.error(400, TokenErrorResponseType.UNSUPPORTED_GRANT_TYPE, "Unsupported Grant Type.").build()); } - private Response processTokenExchange(String scope, ExecutionContext executionContext) { + private Response processTokenExchange(String scope, Function idTokenPreProcessing, ExecutionContext executionContext) { final HttpServletRequest httpRequest = executionContext.getHttpRequest(); + final Client client = executionContext.getClient(); + final OAuth2AuditLog auditLog = executionContext.getAuditLog(); + + final String audience = httpRequest.getParameter("audience"); + final String subjectToken = httpRequest.getParameter("subject_token"); + final String subjectTokenType = httpRequest.getParameter("subject_token_type"); + final String actorToken = httpRequest.getParameter("actor_token"); + final String actorTokenType = httpRequest.getParameter("actor_token_type"); + + tokenRestWebServiceValidator.validateAudience(audience, auditLog); + tokenRestWebServiceValidator.validateSubjectTokenType(subjectTokenType, auditLog); + tokenRestWebServiceValidator.validateActorTokenType(actorTokenType, auditLog); + tokenRestWebServiceValidator.validateActorToken(actorToken, auditLog); + + final SessionId sessionId = sessionIdService.getSessionByDeviceSecret(actorToken); + tokenRestWebServiceValidator.validateSessionForTokenExchange(sessionId, actorToken, auditLog); + checkNotNull(sessionId); // it checked by validator, added it only to relax static bugs checker + + tokenRestWebServiceValidator.validateSubjectToken(subjectToken, sessionId, auditLog); + + TokenExchangeGrant tokenExchangeGrant = authorizationGrantList.createTokenExchangeGrant(new User(), client); + tokenExchangeGrant.setSessionDn(sessionId.getDn()); + + executionContext.setGrant(tokenExchangeGrant); + + scope = tokenExchangeGrant.checkScopesPolicy(scope); + + AccessToken accessToken = tokenExchangeGrant.createAccessToken(executionContext); // create token after scopes are checked + + IdToken idToken = null; + if (isTrue(appConfiguration.getOpenidScopeBackwardCompatibility()) && tokenExchangeGrant.getScopes().contains(OPENID)) { + boolean includeIdTokenClaims = Boolean.TRUE.equals( + appConfiguration.getLegacyIdTokenClaims()); + + ExternalUpdateTokenContext context = new ExternalUpdateTokenContext(httpRequest, tokenExchangeGrant, client, appConfiguration, attributeService); + + executionContext.setIncludeIdTokenClaims(includeIdTokenClaims); + executionContext.setPreProcessing(idTokenPreProcessing); + executionContext.setPostProcessor(externalUpdateTokenService.buildModifyIdTokenProcessor(context)); + + idToken = tokenExchangeGrant.createIdToken( + null, null, null, null, null, executionContext); + } + + RefreshToken reToken = createRefreshToken(executionContext, scope); + + executionContext.getAuditLog().updateOAuth2AuditLog(tokenExchangeGrant, true); + + String rotatedDeviceSecret = tokenInternalService.rotateDeviceSecret(sessionId, actorToken); - String audience = httpRequest.getParameter("audience"); - String subjectToken = httpRequest.getParameter("subject_token"); - String subjectTokenType = httpRequest.getParameter("subject_token_type"); - String actorToken = httpRequest.getParameter("actor_token"); - String actorTokenType = httpRequest.getParameter("actor_token_type"); + JSONObject jsonObj = new JSONObject(); + try { + fillJsonObject(jsonObj, accessToken, accessToken.getTokenType(), accessToken.getExpiresIn(), reToken, scope, idToken); + jsonObj.put("issued_token_type", TOKEN_TYPE_ACCESS_TOKEN); + if (StringUtils.isNotBlank(rotatedDeviceSecret)) { + jsonObj.put("device_secret", rotatedDeviceSecret); + } + } catch (JSONException e) { + log.error(e.getMessage(), e); + } - tokenRestWebServiceValidator.validateSubjectTokenType(subjectTokenType, executionContext.getAuditLog()); - tokenRestWebServiceValidator.validateActorTokenType(actorTokenType, executionContext.getAuditLog()); - // todo - return null; + return response(Response.ok().entity(jsonObj.toString()), executionContext.getAuditLog()); } private Response processROPC(String username, String password, String scope, GrantType gt, Function idTokenPreProcessing, ExecutionContext executionContext) throws SearchException { @@ -649,20 +704,7 @@ public String getJSonResponse(AccessToken accessToken, TokenType tokenType, IdToken idToken) { JSONObject jsonObj = new JSONObject(); try { - jsonObj.put("access_token", accessToken.getCode()); // Required - jsonObj.put("token_type", tokenType.toString()); // Required - if (expiresIn != null) { // Optional - jsonObj.put("expires_in", expiresIn); - } - if (refreshToken != null) { // Optional - jsonObj.put("refresh_token", refreshToken.getCode()); - } - if (scope != null) { // Optional - jsonObj.put("scope", scope); - } - if (idToken != null) { - jsonObj.put("id_token", idToken.getCode()); - } + fillJsonObject(jsonObj, accessToken, tokenType, expiresIn, refreshToken, scope, idToken); } catch (JSONException e) { log.error(e.getMessage(), e); } @@ -670,6 +712,25 @@ public String getJSonResponse(AccessToken accessToken, TokenType tokenType, return jsonObj.toString(); } + public void fillJsonObject(JSONObject jsonObj, AccessToken accessToken, TokenType tokenType, + Integer expiresIn, RefreshToken refreshToken, String scope, + IdToken idToken) { + jsonObj.put("access_token", accessToken.getCode()); // Required + jsonObj.put("token_type", tokenType.toString()); // Required + if (expiresIn != null) { // Optional + jsonObj.put("expires_in", expiresIn); + } + if (refreshToken != null) { // Optional + jsonObj.put("refresh_token", refreshToken.getCode()); + } + if (scope != null) { // Optional + jsonObj.put("scope", scope); + } + if (idToken != null) { + jsonObj.put("id_token", idToken.getCode()); + } + } + private Response processCIBA(String scope, String authReqId, Function idTokenPreProcessing, ExecutionContext executionContext) { errorResponseFactory.validateFeatureEnabled(FeatureFlagType.CIBA); diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/token/ws/rs/TokenRestWebServiceValidator.java b/jans-auth-server/server/src/main/java/io/jans/as/server/token/ws/rs/TokenRestWebServiceValidator.java index c40a1f09069..5513814fed9 100644 --- a/jans-auth-server/server/src/main/java/io/jans/as/server/token/ws/rs/TokenRestWebServiceValidator.java +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/token/ws/rs/TokenRestWebServiceValidator.java @@ -2,9 +2,15 @@ import io.jans.as.common.model.common.User; import io.jans.as.common.model.registration.Client; +import io.jans.as.common.model.session.SessionId; +import io.jans.as.common.model.session.SessionIdState; import io.jans.as.model.common.GrantType; import io.jans.as.model.configuration.AppConfiguration; +import io.jans.as.model.crypto.AbstractCryptoProvider; import io.jans.as.model.error.ErrorResponseFactory; +import io.jans.as.model.exception.CryptoProviderException; +import io.jans.as.model.exception.InvalidJwtException; +import io.jans.as.model.jwt.Jwt; import io.jans.as.model.token.TokenErrorResponseType; import io.jans.as.server.audit.ApplicationAuditLogger; import io.jans.as.server.model.audit.OAuth2AuditLog; @@ -47,6 +53,9 @@ public class TokenRestWebServiceValidator { @Inject private AppConfiguration appConfiguration; + @Inject + private AbstractCryptoProvider cryptoProvider; + public void validateParams(String grantType, String code, String redirectUri, String refreshToken, OAuth2AuditLog auditLog) { log.debug("Starting to validate request parameters"); @@ -187,4 +196,65 @@ public void validateActorTokenType(String actorTokenType, OAuth2AuditLog auditLo throw new WebApplicationException(response(error(400, TokenErrorResponseType.INVALID_REQUEST, msg), auditLog)); } } + + public void validateActorToken(String actorToken, OAuth2AuditLog auditLog) { + if (StringUtils.isBlank(actorToken)) { + String msg = "actor_token is blank"; + log.trace(msg); + throw new WebApplicationException(response(error(400, TokenErrorResponseType.INVALID_REQUEST, msg), auditLog)); + } + } + + public void validateSessionForTokenExchange(SessionId session, String actorToken, OAuth2AuditLog auditLog) { + if (session == null) { + String msg = String.format("Unable to find session for device_secret (actor_token): %s", actorToken); + log.trace(msg); + throw new WebApplicationException(response(error(400, TokenErrorResponseType.INVALID_GRANT, msg), auditLog)); + } + if (session.getState() != SessionIdState.AUTHENTICATED) { + String msg = String.format("Session found by device_secret (actor_token) '%s' is not authenticated. SessionId: %s", actorToken, session.getId()); + log.trace(msg); + throw new WebApplicationException(response(error(400, TokenErrorResponseType.INVALID_GRANT, msg), auditLog)); + } + } + + public Jwt validateSubjectToken(String subjectToken, SessionId sidSession, OAuth2AuditLog auditLog) { + try { + final Jwt jwt = Jwt.parse(subjectToken); + validateSubjectTokenSignature(sidSession, jwt, auditLog); + return jwt; + } catch (InvalidJwtException e) { + log.error("Unable to parse subject_token as JWT.", e); + throw new WebApplicationException(response(error(400, TokenErrorResponseType.INVALID_REQUEST, "Unable to parse subject_token as JWT."), auditLog)); + } catch (WebApplicationException e) { + throw e; + } catch (Exception e) { + log.error("Unable to validate subject_token.", e); + throw new WebApplicationException(response(error(400, TokenErrorResponseType.INVALID_REQUEST, "Unable to validate subject_token as JWT."), auditLog)); + } + } + + private void validateSubjectTokenSignature(SessionId sidSession, Jwt jwt, OAuth2AuditLog auditLog) throws InvalidJwtException, CryptoProviderException { + // verify jwt signature if we can't find it in db + if (!cryptoProvider.verifySignature(jwt.getSigningInput(), jwt.getEncodedSignature(), jwt.getHeader().getKeyId(), + null, null, jwt.getHeader().getSignatureAlgorithm())) { + log.error("subject_token signature verification failed."); + throw new WebApplicationException(response(error(400, TokenErrorResponseType.INVALID_REQUEST, "subject_token signature verification failed."), auditLog)); + } + + final String sidClaim = jwt.getClaims().getClaimAsString("sid"); + if (sidSession != null && org.apache.commons.lang.StringUtils.equals(sidSession.getOutsideSid(), sidClaim)) { + return; + } + log.error("sid claim from subject_token does not match to session sid."); + throw new WebApplicationException(response(error(400, TokenErrorResponseType.INVALID_REQUEST, "sid claim from subject_token does not match to session sid."), auditLog)); + } + + public void validateAudience(String audience, OAuth2AuditLog auditLog) { + if (StringUtils.isBlank(audience)) { + String msg = "audience is blank"; + log.trace(msg); + throw new WebApplicationException(response(error(400, TokenErrorResponseType.INVALID_REQUEST, msg), auditLog)); + } + } } diff --git a/jans-auth-server/server/src/test/java/io/jans/as/server/token/ws/rs/TokenRestWebServiceValidatorTest.java b/jans-auth-server/server/src/test/java/io/jans/as/server/token/ws/rs/TokenRestWebServiceValidatorTest.java index 4bf61239a32..66dbcce6ed4 100644 --- a/jans-auth-server/server/src/test/java/io/jans/as/server/token/ws/rs/TokenRestWebServiceValidatorTest.java +++ b/jans-auth-server/server/src/test/java/io/jans/as/server/token/ws/rs/TokenRestWebServiceValidatorTest.java @@ -2,9 +2,12 @@ import io.jans.as.common.model.common.User; import io.jans.as.common.model.registration.Client; +import io.jans.as.common.model.session.SessionId; +import io.jans.as.common.model.session.SessionIdState; import io.jans.as.model.common.GrantType; import io.jans.as.model.config.Constants; import io.jans.as.model.configuration.AppConfiguration; +import io.jans.as.model.crypto.AbstractCryptoProvider; import io.jans.as.model.error.ErrorResponseFactory; import io.jans.as.server.audit.ApplicationAuditLogger; import io.jans.as.server.model.audit.OAuth2AuditLog; @@ -47,9 +50,77 @@ public class TokenRestWebServiceValidatorTest { @Mock private ErrorResponseFactory errorResponseFactory; + @Mock + private AbstractCryptoProvider cryptoProvider; + @InjectMocks private TokenRestWebServiceValidator validator; + @Test + public void validateSessionForTokenExchange_whenSessionIsNull_shouldThrowError() { + try { + validator.validateSessionForTokenExchange(null, "test_device_secret", AUDIT_LOG); + } catch (WebApplicationException e) { + assertBadRequest(e.getResponse()); + return; + } + fail("No error for invalid session."); + } + + @Test + public void validateSessionForTokenExchange_whenSessionIsNotAuthenticated_shouldThrowError() { + try { + SessionId sessionId = new SessionId(); + sessionId.setState(SessionIdState.UNAUTHENTICATED); + + validator.validateSessionForTokenExchange(sessionId, "test_device_secret", AUDIT_LOG); + } catch (WebApplicationException e) { + assertBadRequest(e.getResponse()); + return; + } + fail("No error for unauthenticated session."); + } + + @Test + public void validateSessionForTokenExchange_whenSessionIsAuthenticated_shouldPassSuccessfully() { + SessionId sessionId = new SessionId(); + sessionId.setState(SessionIdState.AUTHENTICATED); + + validator.validateSessionForTokenExchange(sessionId, "test_device_secret", AUDIT_LOG); + } + + @Test + public void validateActorToken_withEmptyActorToken_shouldThrowError() { + try { + validator.validateActorToken(null, AUDIT_LOG); + } catch (WebApplicationException e) { + assertBadRequest(e.getResponse()); + return; + } + fail("No error for invalid actor token."); + } + + @Test + public void validateActorToken_withNotBlankActorToken_shouldPassSuccessfully() { + validator.validateActorToken("not_blank_actor_token", AUDIT_LOG); + } + + @Test + public void validateAudience_withEmptyAudience_shouldThrowError() { + try { + validator.validateAudience(null, AUDIT_LOG); + } catch (WebApplicationException e) { + assertBadRequest(e.getResponse()); + return; + } + fail("No error for invalid audience."); + } + + @Test + public void validateAudience_withNotBlankAudience_shouldPassSuccessfully() { + validator.validateAudience("not_blank_audience", AUDIT_LOG); + } + @Test public void validateSubjectTokenType_withInvalidTokenType_shouldThrowError() { try {