From ae0b60b4f89ed250c445d0b313b9b8edf974a4ee Mon Sep 17 00:00:00 2001 From: YuriyZ Date: Wed, 27 Apr 2022 12:05:54 +0300 Subject: [PATCH] refactor(jans-auth-server): extracted request object processing into separate method https://github.com/JanssenProject/jans/issues/1208 --- .../ws/rs/AuthorizeRestWebServiceImpl.java | 452 +++++------------- .../rs/AuthorizeRestWebServiceValidator.java | 28 ++ .../server/authorize/ws/rs/AuthzRequest.java | 29 ++ .../authorize/ws/rs/AuthzRequestService.java | 310 +++++++++++- 4 files changed, 473 insertions(+), 346 deletions(-) diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/authorize/ws/rs/AuthorizeRestWebServiceImpl.java b/jans-auth-server/server/src/main/java/io/jans/as/server/authorize/ws/rs/AuthorizeRestWebServiceImpl.java index 93882e34eb9..4ce122a4c1d 100644 --- a/jans-auth-server/server/src/main/java/io/jans/as/server/authorize/ws/rs/AuthorizeRestWebServiceImpl.java +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/authorize/ws/rs/AuthorizeRestWebServiceImpl.java @@ -6,44 +6,48 @@ package io.jans.as.server.authorize.ws.rs; -import com.google.common.collect.Lists; import com.google.common.collect.Maps; -import com.google.common.collect.Sets; import io.jans.as.common.model.common.User; import io.jans.as.common.model.registration.Client; import io.jans.as.common.util.RedirectUri; import io.jans.as.model.authorize.AuthorizeErrorResponseType; import io.jans.as.model.authorize.AuthorizeRequestParam; import io.jans.as.model.authorize.AuthorizeResponseParam; -import io.jans.as.model.common.*; -import io.jans.as.model.config.WebKeysConfiguration; +import io.jans.as.model.common.BackchannelTokenDeliveryMode; +import io.jans.as.model.common.GrantType; +import io.jans.as.model.common.Prompt; +import io.jans.as.model.common.ResponseMode; +import io.jans.as.model.common.ResponseType; +import io.jans.as.model.common.ScopeConstants; +import io.jans.as.model.common.SubjectType; import io.jans.as.model.configuration.AppConfiguration; -import io.jans.as.model.crypto.AbstractCryptoProvider; import io.jans.as.model.crypto.binding.TokenBindingMessage; -import io.jans.as.model.crypto.encryption.BlockEncryptionAlgorithm; -import io.jans.as.model.crypto.encryption.KeyEncryptionAlgorithm; -import io.jans.as.model.crypto.signature.AlgorithmFamily; -import io.jans.as.model.crypto.signature.SignatureAlgorithm; import io.jans.as.model.error.ErrorResponseFactory; -import io.jans.as.model.exception.InvalidJwtException; -import io.jans.as.model.jwe.Jwe; -import io.jans.as.model.jwk.Algorithm; -import io.jans.as.model.jwk.JSONWebKeySet; -import io.jans.as.model.jwk.Use; -import io.jans.as.model.jwt.Jwt; -import io.jans.as.model.jwt.JwtClaimName; -import io.jans.as.model.jwt.JwtHeader; -import io.jans.as.model.jwt.JwtHeaderName; import io.jans.as.model.token.JsonWebResponse; -import io.jans.as.model.util.JwtUtil; import io.jans.as.model.util.Util; import io.jans.as.server.audit.ApplicationAuditLogger; import io.jans.as.server.ciba.CIBAPingCallbackService; import io.jans.as.server.ciba.CIBAPushTokenDeliveryService; import io.jans.as.server.model.audit.Action; import io.jans.as.server.model.audit.OAuth2AuditLog; -import io.jans.as.server.model.authorize.*; -import io.jans.as.server.model.common.*; +import io.jans.as.server.model.authorize.AuthorizeParamsValidator; +import io.jans.as.server.model.authorize.ScopeChecker; +import io.jans.as.server.model.common.AccessToken; +import io.jans.as.server.model.common.AuthorizationCode; +import io.jans.as.server.model.common.AuthorizationGrant; +import io.jans.as.server.model.common.AuthorizationGrantList; +import io.jans.as.server.model.common.CIBAGrant; +import io.jans.as.server.model.common.CibaRequestCacheControl; +import io.jans.as.server.model.common.CibaRequestStatus; +import io.jans.as.server.model.common.DefaultScope; +import io.jans.as.server.model.common.DeviceAuthorizationCacheControl; +import io.jans.as.server.model.common.DeviceAuthorizationStatus; +import io.jans.as.server.model.common.DeviceCodeGrant; +import io.jans.as.server.model.common.ExecutionContext; +import io.jans.as.server.model.common.IdToken; +import io.jans.as.server.model.common.RefreshToken; +import io.jans.as.server.model.common.SessionId; +import io.jans.as.server.model.common.SessionIdState; import io.jans.as.server.model.config.ConfigurationFactory; import io.jans.as.server.model.config.Constants; import io.jans.as.server.model.exception.AcrChangedException; @@ -52,7 +56,16 @@ import io.jans.as.server.model.ldap.ClientAuthorization; import io.jans.as.server.model.token.JwrService; import io.jans.as.server.security.Identity; -import io.jans.as.server.service.*; +import io.jans.as.server.service.AttributeService; +import io.jans.as.server.service.AuthenticationFilterService; +import io.jans.as.server.service.ClientAuthorizationsService; +import io.jans.as.server.service.ClientService; +import io.jans.as.server.service.CookieService; +import io.jans.as.server.service.DeviceAuthorizationService; +import io.jans.as.server.service.RedirectUriResponse; +import io.jans.as.server.service.RequestParameterService; +import io.jans.as.server.service.SessionIdService; +import io.jans.as.server.service.UserService; import io.jans.as.server.service.ciba.CibaRequestService; import io.jans.as.server.service.external.ExternalPostAuthnService; import io.jans.as.server.service.external.ExternalUpdateTokenService; @@ -64,13 +77,9 @@ import io.jans.as.server.util.RedirectUtil; import io.jans.as.server.util.ServerUtil; import io.jans.orm.exception.EntryPersistenceException; +import io.jans.orm.exception.operation.SearchException; +import io.jans.util.Pair; import io.jans.util.StringHelper; -import org.apache.commons.lang.ArrayUtils; -import org.apache.commons.lang.StringUtils; -import org.jetbrains.annotations.Nullable; -import org.json.JSONObject; -import org.slf4j.Logger; - import jakarta.inject.Inject; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @@ -82,12 +91,18 @@ import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Response.ResponseBuilder; import jakarta.ws.rs.core.SecurityContext; +import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; + import java.net.URI; -import java.net.URLDecoder; -import java.nio.charset.StandardCharsets; -import java.security.PrivateKey; -import java.util.*; +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.Map.Entry; +import java.util.Set; import java.util.function.Function; import static io.jans.as.model.util.StringUtils.implode; @@ -147,12 +162,6 @@ public class AuthorizeRestWebServiceImpl implements AuthorizeRestWebService { @Inject private ConfigurationFactory configurationFactory; - @Inject - private WebKeysConfiguration webKeysConfiguration; - - @Inject - private AbstractCryptoProvider cryptoProvider; - @Inject private AuthorizeRestWebServiceValidator authorizeRestWebServiceValidator; @@ -280,9 +289,9 @@ private Response requestAuthorization(AuthzRequest authzRequest) { ResponseBuilder builder = null; - Map customParameters = requestParameterService.getCustomParameters(QueryStringDecoder.decode(authzRequest.getHttpRequest().getQueryString())); + authzRequest.setCustomParameters(requestParameterService.getCustomParameters(QueryStringDecoder.decode(authzRequest.getHttpRequest().getQueryString()))); - boolean isPar = authzRequestService.processPar(authzRequest, customParameters); + boolean isPar = authzRequestService.processPar(authzRequest); List responseTypes = ResponseType.fromString(authzRequest.getResponseType(), " "); List prompts = Prompt.fromString(authzRequest.getPrompt(), " "); @@ -298,6 +307,7 @@ private Response requestAuthorization(AuthzRequest authzRequest) { Client client = authorizeRestWebServiceValidator.validateClient(authzRequest.getClientId(), authzRequest.getState(), isPar); String deviceAuthzUserCode = deviceAuthorizationService.getUserCodeFromSession(authzRequest.getHttpRequest()); authzRequest.setRedirectUri(authorizeRestWebServiceValidator.validateRedirectUri(client, authzRequest.getRedirectUri(), authzRequest.getState(), deviceAuthzUserCode, authzRequest.getHttpRequest())); + RedirectUriResponse redirectUriResponse = new RedirectUriResponse(new RedirectUri(authzRequest.getRedirectUri(), responseTypes, authzRequest.getResponseModeEnum()), authzRequest.getState(), authzRequest.getHttpRequest(), errorResponseFactory); redirectUriResponse.setFapiCompatible(appConfiguration.isFapi()); @@ -309,135 +319,10 @@ private Response requestAuthorization(AuthzRequest authzRequest) { throw authorizeRestWebServiceValidator.createInvalidJwtRequestException(redirectUriResponse, "A signed request object is required"); } - JwtAuthorizationRequest jwtRequest = null; - if (StringUtils.isNotBlank(authzRequest.getRequest()) || StringUtils.isNotBlank(authzRequest.getRequestUri())) { - try { - jwtRequest = JwtAuthorizationRequest.createJwtRequest(authzRequest.getRequest(), authzRequest.getRequestUri(), client, redirectUriResponse, cryptoProvider, appConfiguration); - - if (jwtRequest == null) { - throw authorizeRestWebServiceValidator.createInvalidJwtRequestException(redirectUriResponse, "Failed to parse jwt."); - } - if (StringUtils.isNotBlank(jwtRequest.getState())) { - authzRequest.setState(jwtRequest.getState()); - redirectUriResponse.setState(authzRequest.getState()); - } - if (appConfiguration.isFapi() && StringUtils.isBlank(jwtRequest.getState())) { - authzRequest.setState(""); // #1250 - FAPI : discard state if in JWT we don't have state - redirectUriResponse.setState(""); - } - - if (jwtRequest.getRedirectUri() != null) { - redirectUriResponse.getRedirectUri().setBaseRedirectUri(jwtRequest.getRedirectUri()); - } - - SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.fromString(jwtRequest.getAlgorithm()); - if (Boolean.TRUE.equals(appConfiguration.getForceSignedRequestObject()) && signatureAlgorithm == SignatureAlgorithm.NONE) { - throw authorizeRestWebServiceValidator.createInvalidJwtRequestException(redirectUriResponse, "A signed request object is required"); - } - - // JWT wins - if (!jwtRequest.getScopes().isEmpty()) { - if (!scopes.contains("openid")) { // spec: Even if a scope parameter is present in the Request Object value, a scope parameter MUST always be passed using the OAuth 2.0 request syntax containing the openid scope value - throw new WebApplicationException(Response - .status(Response.Status.BAD_REQUEST) - .entity(errorResponseFactory.getErrorAsJson(AuthorizeErrorResponseType.INVALID_SCOPE, authzRequest.getState(), "scope parameter does not contain openid value which is required.")) - .build()); - } - scopes = scopeChecker.checkScopesPolicy(client, Lists.newArrayList(jwtRequest.getScopes())); - } - if (jwtRequest.getRedirectUri() != null && !jwtRequest.getRedirectUri().equals(authzRequest.getRedirectUri())) { - throw authorizeRestWebServiceValidator.createInvalidJwtRequestException(redirectUriResponse, "The redirect_uri parameter is not the same in the JWT"); - } - if (StringUtils.isNotBlank(jwtRequest.getNonce())) { - authzRequest.setNonce(jwtRequest.getNonce()); - } - if (StringUtils.isNotBlank(jwtRequest.getCodeChallenge())) { - authzRequest.setCodeChallenge(jwtRequest.getCodeChallenge()); - } - if (StringUtils.isNotBlank(jwtRequest.getCodeChallengeMethod())) { - authzRequest.setCodeChallengeMethod(jwtRequest.getCodeChallengeMethod()); - } - if (jwtRequest.getDisplay() != null && StringUtils.isNotBlank(jwtRequest.getDisplay().getParamName())) { - authzRequest.setDisplay(jwtRequest.getDisplay().getParamName()); - } - if (!jwtRequest.getPrompts().isEmpty()) { - prompts = Lists.newArrayList(jwtRequest.getPrompts()); - } - if (jwtRequest.getResponseMode() != null) { - authzRequest.setResponseMode(jwtRequest.getResponseMode().getValue()); - redirectUriResponse.getRedirectUri().setResponseMode(jwtRequest.getResponseMode()); - } - - final IdTokenMember idTokenMember = jwtRequest.getIdTokenMember(); - if (idTokenMember != null) { - if (idTokenMember.getMaxAge() != null) { - authzRequest.setMaxAge(idTokenMember.getMaxAge()); - } - final Claim acrClaim = idTokenMember.getClaim(JwtClaimName.AUTHENTICATION_CONTEXT_CLASS_REFERENCE); - if (acrClaim != null && acrClaim.getClaimValue() != null) { - authzRequest.setAcrValues(acrClaim.getClaimValue().getValueAsString()); - } - - Claim userIdClaim = idTokenMember.getClaim(JwtClaimName.SUBJECT_IDENTIFIER); - if (userIdClaim != null && userIdClaim.getClaimValue() != null - && userIdClaim.getClaimValue().getValue() != null) { - String userIdClaimValue = userIdClaim.getClaimValue().getValue(); - - if (user != null) { - String userId = user.getUserId(); - - if (!userId.equalsIgnoreCase(userIdClaimValue)) { - builder = redirectUriResponse.createErrorBuilder(AuthorizeErrorResponseType.USER_MISMATCHED); - applicationAuditLogger.sendMessage(oAuth2AuditLog); - return builder.build(); - } - } - } - } - requestParameterService.getCustomParameters(jwtRequest, customParameters); - } catch (WebApplicationException e) { - JsonWebResponse jwr = parseRequestToJwr(authzRequest.getRequest()); - if (jwr != null) { - String checkForAlg = jwr.getClaims().getClaimAsString("alg"); // to handle Jans Issue#310 - if ("none".equals(checkForAlg)) { - throw new WebApplicationException(Response.status(Response.Status.BAD_REQUEST) - .entity(errorResponseFactory.getErrorAsJson( - AuthorizeErrorResponseType.INVALID_REQUEST_OBJECT, "", - "The None algorithm in nested JWT is not allowed for FAPI")) - .type(MediaType.APPLICATION_JSON_TYPE).build()); - } - ResponseMode responseMode = ResponseMode.getByValue(jwr.getClaims().getClaimAsString("response_mode")); - if (responseMode == ResponseMode.JWT) { - authzRequest.setResponseMode(responseMode.getValue()); - redirectUriResponse.getRedirectUri().setResponseMode(ResponseMode.JWT); - fillRedirectUriResponseforJARM(redirectUriResponse, jwr, client); - if (appConfiguration.isFapi()) { - authorizeRestWebServiceValidator.throwInvalidJwtRequestExceptionAsJwtMode( - redirectUriResponse, "Invalid JWT authorization request", - jwr.getClaims().getClaimAsString("state"), authzRequest.getHttpRequest()); - } - } - } - throw e; - } catch (Exception e) { - log.error("Invalid JWT authorization request. Message : " + e.getMessage(), e); - throw authorizeRestWebServiceValidator.createInvalidJwtRequestException(redirectUriResponse, "Invalid JWT authorization request"); - } - } - - // JARM - Set jwtResponseModes = Sets.newHashSet(ResponseMode.QUERY_JWT, ResponseMode.FRAGMENT_JWT, ResponseMode.JWT, ResponseMode.FORM_POST_JWT); - if (jwtResponseModes.contains(authzRequest.getResponseModeEnum())) { - JsonWebResponse jwe = parseRequestToJwr(authzRequest.getRequest()); - fillRedirectUriResponseforJARM(redirectUriResponse, jwe, client); - } - // Validate JWT request object after JARM check, because we want to return errors well formatted (JSON/JWT). - if (jwtRequest != null) { - validateJwtRequest(authzRequest.getClientId(), authzRequest.getState(), authzRequest.getHttpRequest(), responseTypes, redirectUriResponse, jwtRequest); - } + authzRequestService.processRequestObject(authzRequest, client, redirectUriResponse, scopes, user); if (!cibaRequestService.hasCibaCompatibility(client) && !isPar) { - if (appConfiguration.isFapi() && jwtRequest == null) { + if (appConfiguration.isFapi() && authzRequest.getJwtRequest() == null) { throw redirectUriResponse.createWebException(AuthorizeErrorResponseType.INVALID_REQUEST); } authorizeRestWebServiceValidator.validateRequestJwt(authzRequest.getRequest(), authzRequest.getRequestUri(), redirectUriResponse); @@ -475,48 +360,9 @@ private Response requestAuthorization(AuthzRequest authzRequest) { AuthorizationGrant authorizationGrant = null; if (user == null) { - identity.logout(); - if (prompts.contains(Prompt.NONE)) { - if (authenticationFilterService.isEnabled()) { - Map params; - if (authzRequest.getHttpMethod().equals(HttpMethod.GET)) { - params = QueryStringDecoder.decode(authzRequest.getHttpRequest().getQueryString()); - } else { - params = getGenericRequestMap(authzRequest.getHttpRequest()); - } - - String userDn = authenticationFilterService.processAuthenticationFilters(params); - if (userDn != null) { - Map genericRequestMap = getGenericRequestMap(authzRequest.getHttpRequest()); - - Map parameterMap = Maps.newHashMap(genericRequestMap); - Map requestParameterMap = requestParameterService.getAllowedParameters(parameterMap); - - sessionUser = sessionIdService.generateAuthenticatedSessionId(authzRequest.getHttpRequest(), userDn, authzRequest.getPrompt()); - sessionUser.setSessionAttributes(requestParameterMap); - - cookieService.createSessionIdCookie(sessionUser, authzRequest.getHttpRequest(), authzRequest.getHttpResponse(), false); - sessionIdService.updateSessionId(sessionUser); - user = userService.getUserByDn(sessionUser.getUserDn()); - } else { - builder = redirectUriResponse.createErrorBuilder(AuthorizeErrorResponseType.LOGIN_REQUIRED); - applicationAuditLogger.sendMessage(oAuth2AuditLog); - return builder.build(); - } - } else { - builder = redirectUriResponse.createErrorBuilder(AuthorizeErrorResponseType.LOGIN_REQUIRED); - applicationAuditLogger.sendMessage(oAuth2AuditLog); - return builder.build(); - } - } else { - if (prompts.contains(Prompt.LOGIN)) { - unauthenticateSession(authzRequest.getSessionId(), authzRequest.getHttpRequest()); - authzRequest.setSessionId(null); - prompts.remove(Prompt.LOGIN); - } - - return redirectToAuthorizationPage(authzRequest, redirectUriResponse.getRedirectUri(), prompts, customParameters, oAuth2AuditLog); - } + final Pair pair = ifUserIsNull(authzRequest, redirectUriResponse, oAuth2AuditLog); + user = pair.getFirst(); + sessionUser = pair.getSecond(); } boolean validAuthenticationMaxAge = authorizeRestWebServiceValidator.validateAuthnMaxAge(authzRequest.getMaxAge(), sessionUser, client); @@ -524,10 +370,10 @@ private Response requestAuthorization(AuthzRequest authzRequest) { unauthenticateSession(authzRequest.getSessionId(), authzRequest.getHttpRequest()); authzRequest.setSessionId(null); - return redirectToAuthorizationPage(authzRequest, redirectUriResponse.getRedirectUri(), prompts, customParameters, oAuth2AuditLog); + return redirectToAuthorizationPage(authzRequest, redirectUriResponse.getRedirectUri(), prompts, oAuth2AuditLog); } - oAuth2AuditLog.setUsername(user != null ? user.getUserId() : ""); + oAuth2AuditLog.setUsername(user.getUserId()); ExternalPostAuthnContext postAuthnContext = new ExternalPostAuthnContext(client, sessionUser, authzRequest.getHttpRequest(), authzRequest.getHttpResponse()); final boolean forceReAuthentication = externalPostAuthnService.externalForceReAuthentication(client, postAuthnContext); @@ -535,19 +381,19 @@ private Response requestAuthorization(AuthzRequest authzRequest) { unauthenticateSession(authzRequest.getSessionId(), authzRequest.getHttpRequest()); authzRequest.setSessionId(null); - return redirectToAuthorizationPage(authzRequest, redirectUriResponse.getRedirectUri(), prompts, customParameters, oAuth2AuditLog); + return redirectToAuthorizationPage(authzRequest, redirectUriResponse.getRedirectUri(), prompts, oAuth2AuditLog); } final boolean forceAuthorization = externalPostAuthnService.externalForceAuthorization(client, postAuthnContext); if (forceAuthorization) { - return redirectToAuthorizationPage(authzRequest, redirectUriResponse.getRedirectUri(), prompts, customParameters, oAuth2AuditLog); + return redirectToAuthorizationPage(authzRequest, redirectUriResponse.getRedirectUri(), prompts, oAuth2AuditLog); } ClientAuthorization clientAuthorization = null; boolean clientAuthorizationFetched = false; if (!scopes.isEmpty()) { if (prompts.contains(Prompt.CONSENT)) { - return redirectToAuthorizationPage(authzRequest, redirectUriResponse.getRedirectUri(), prompts, customParameters, oAuth2AuditLog); + return redirectToAuthorizationPage(authzRequest, redirectUriResponse.getRedirectUri(), prompts, oAuth2AuditLog); } // There is no need to present the consent page: // If Client is a Trusted Client. @@ -557,7 +403,7 @@ private Response requestAuthorization(AuthzRequest authzRequest) { && scopes.size() == 1 && scopes.contains(DefaultScope.OPEN_ID.toString()) && authzRequest.getClaims() == null - && (jwtRequest == null || (jwtRequest.getUserInfoMember() == null && jwtRequest.getIdTokenMember() == null)); + && (authzRequest.getJwtRequest() == null || (authzRequest.getJwtRequest().getUserInfoMember() == null && authzRequest.getJwtRequest().getIdTokenMember() == null)); if (client.getTrustedClient() || isPairwiseWithOnlyOpenIdScope) { sessionUser.addPermission(authzRequest.getClientId(), true); sessionIdService.updateSessionId(sessionUser); @@ -571,8 +417,7 @@ private Response requestAuthorization(AuthzRequest authzRequest) { sessionUser.addPermission(authzRequest.getClientId(), true); sessionIdService.updateSessionId(sessionUser); } else { - return redirectToAuthorizationPage(authzRequest, redirectUriResponse.getRedirectUri(), prompts, - customParameters, oAuth2AuditLog); + return redirectToAuthorizationPage(authzRequest, redirectUriResponse.getRedirectUri(), prompts, oAuth2AuditLog); } } } @@ -594,8 +439,7 @@ && new Date().getTime() - identity.getSessionId().getAuthenticationTime().getTim authzRequest.setSessionId(null); prompts.remove(Prompt.LOGIN); - return redirectToAuthorizationPage(authzRequest, redirectUriResponse.getRedirectUri(), prompts, - customParameters, oAuth2AuditLog); + return redirectToAuthorizationPage(authzRequest, redirectUriResponse.getRedirectUri(), prompts, oAuth2AuditLog); } if (prompts.contains(Prompt.CONSENT) || !isTrue(sessionUser.isPermissionGrantedForClient(authzRequest.getClientId()))) { @@ -607,12 +451,12 @@ && new Date().getTime() - identity.getSessionId().getAuthenticationTime().getTim prompts.remove(Prompt.CONSENT); return redirectToAuthorizationPage(authzRequest, redirectUriResponse.getRedirectUri(), - prompts, customParameters, oAuth2AuditLog); + prompts, oAuth2AuditLog); } if (prompts.contains(Prompt.SELECT_ACCOUNT)) { return redirectToSelectAccountPage(authzRequest, redirectUriResponse.getRedirectUri(), - prompts, customParameters, oAuth2AuditLog); + prompts, oAuth2AuditLog); } AuthorizationCode authorizationCode = null; @@ -620,7 +464,7 @@ && new Date().getTime() - identity.getSessionId().getAuthenticationTime().getTim authorizationGrant = authorizationGrantList.createAuthorizationCodeGrant(user, client, sessionUser.getAuthenticationTime()); authorizationGrant.setNonce(authzRequest.getNonce()); - authorizationGrant.setJwtAuthorizationRequest(jwtRequest); + authorizationGrant.setJwtAuthorizationRequest(authzRequest.getJwtRequest()); authorizationGrant.setTokenBindingHash(TokenBindingMessage.getTokenBindingIdHashFromTokenBindingMessage(tokenBindingHeader, client.getIdTokenTokenBindingCnf())); authorizationGrant.setScopes(scopes); authorizationGrant.setCodeChallenge(authzRequest.getCodeChallenge()); @@ -643,7 +487,7 @@ && new Date().getTime() - identity.getSessionId().getAuthenticationTime().getTim authorizationGrant = authorizationGrantList.createImplicitGrant(user, client, sessionUser.getAuthenticationTime()); authorizationGrant.setNonce(authzRequest.getNonce()); - authorizationGrant.setJwtAuthorizationRequest(jwtRequest); + authorizationGrant.setJwtAuthorizationRequest(authzRequest.getJwtRequest()); authorizationGrant.setScopes(scopes); authorizationGrant.setClaims(authzRequest.getClaims()); @@ -669,7 +513,7 @@ && new Date().getTime() - identity.getSessionId().getAuthenticationTime().getTim authorizationGrant = authorizationGrantList.createImplicitGrant(user, client, sessionUser.getAuthenticationTime()); authorizationGrant.setNonce(authzRequest.getNonce()); - authorizationGrant.setJwtAuthorizationRequest(jwtRequest); + authorizationGrant.setJwtAuthorizationRequest(authzRequest.getJwtRequest()); authorizationGrant.setScopes(scopes); authorizationGrant.setClaims(authzRequest.getClaims()); @@ -701,7 +545,7 @@ && new Date().getTime() - identity.getSessionId().getAuthenticationTime().getTim redirectUriResponse.getRedirectUri().addResponseParameter(AuthorizeResponseParam.ACR_VALUES, authzRequest.getAcrValues()); } - for (Map.Entry customParam : requestParameterService.getCustomParameters(customParameters, true).entrySet()) { + for (Map.Entry customParam : requestParameterService.getCustomParameters(authzRequest.getCustomParameters(), true).entrySet()) { redirectUriResponse.getRedirectUri().addResponseParameter(customParam.getKey(), customParam.getValue()); } @@ -778,124 +622,48 @@ && new Date().getTime() - identity.getSessionId().getAuthenticationTime().getTim return builder.build(); } - @Nullable - private JsonWebResponse parseRequestToJwr(String request) { - if (request == null) { - return null; - } - String[] parts = request.split("\\."); - try { - if (parts.length == 5) { - String encodedHeader = parts[0]; - JwtHeader jwtHeader = new JwtHeader(encodedHeader); - String keyId = jwtHeader.getKeyId(); - PrivateKey privateKey = null; - KeyEncryptionAlgorithm keyEncryptionAlgorithm = KeyEncryptionAlgorithm - .fromName(jwtHeader.getClaimAsString(JwtHeaderName.ALGORITHM)); - if (AlgorithmFamily.RSA.equals(keyEncryptionAlgorithm.getFamily())) { - privateKey = cryptoProvider.getPrivateKey(keyId); - } - return Jwe.parse(request, privateKey, null); - } - return Jwt.parseSilently(request); - } catch (Exception e) { - log.error(e.getMessage(), e); - return null; - } - } - - private void fillRedirectUriResponseforJARM(RedirectUriResponse redirectUriResponse, JsonWebResponse jwr, Client client) { - try { - if (jwr != null) { - String tempRedirectUri = jwr.getClaims().getClaimAsString("redirect_uri"); - if (StringUtils.isNotBlank(tempRedirectUri)) { - redirectUriResponse.getRedirectUri().setBaseRedirectUri(URLDecoder.decode(tempRedirectUri, "UTF-8")); - } - } - String clientId = client.getClientId(); - redirectUriResponse.getRedirectUri().setIssuer(appConfiguration.getIssuer()); - redirectUriResponse.getRedirectUri().setAudience(clientId); - redirectUriResponse.getRedirectUri().setAuthorizationCodeLifetime(appConfiguration.getAuthorizationCodeLifetime()); - redirectUriResponse.getRedirectUri().setSignatureAlgorithm(SignatureAlgorithm.fromString(client.getAttributes().getAuthorizationSignedResponseAlg())); - redirectUriResponse.getRedirectUri().setKeyEncryptionAlgorithm(KeyEncryptionAlgorithm.fromName(client.getAttributes().getAuthorizationEncryptedResponseAlg())); - redirectUriResponse.getRedirectUri().setBlockEncryptionAlgorithm(BlockEncryptionAlgorithm.fromName(client.getAttributes().getAuthorizationEncryptedResponseEnc())); - redirectUriResponse.getRedirectUri().setCryptoProvider(cryptoProvider); - - String keyId = null; - if (client.getAttributes().getAuthorizationEncryptedResponseAlg() != null - && client.getAttributes().getAuthorizationEncryptedResponseEnc() != null) { - if (client.getAttributes().getAuthorizationSignedResponseAlg() != null) { // Signed then Encrypted - // response - SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm - .fromString(client.getAttributes().getAuthorizationSignedResponseAlg()); - - String nestedKeyId = new ServerCryptoProvider(cryptoProvider).getKeyId(webKeysConfiguration, - Algorithm.fromString(signatureAlgorithm.getName()), Use.SIGNATURE); - - JSONObject jsonWebKeys = JwtUtil.getJSONWebKeys(client.getJwksUri()); - redirectUriResponse.getRedirectUri().setNestedJsonWebKeys(jsonWebKeys); - - String clientSecret = clientService.decryptSecret(client.getClientSecret()); - redirectUriResponse.getRedirectUri().setNestedSharedSecret(clientSecret); - redirectUriResponse.getRedirectUri().setNestedKeyId(nestedKeyId); - } - - // Encrypted response - JSONObject jsonWebKeys = JwtUtil.getJSONWebKeys(client.getJwksUri()); - if (jsonWebKeys != null) { - keyId = new ServerCryptoProvider(cryptoProvider).getKeyId(JSONWebKeySet.fromJSONObject(jsonWebKeys), - Algorithm.fromString(client.getAttributes().getAuthorizationEncryptedResponseAlg()), - Use.ENCRYPTION); - } - String sharedSecret = clientService.decryptSecret(client.getClientSecret()); - byte[] sharedSymmetricKey = sharedSecret.getBytes(StandardCharsets.UTF_8); - redirectUriResponse.getRedirectUri().setSharedSymmetricKey(sharedSymmetricKey); - redirectUriResponse.getRedirectUri().setJsonWebKeys(jsonWebKeys); - redirectUriResponse.getRedirectUri().setKeyId(keyId); - } else { // Signed response - SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.RS256; - if (client.getAttributes().getAuthorizationSignedResponseAlg() != null) { - signatureAlgorithm = SignatureAlgorithm - .fromString(client.getAttributes().getAuthorizationSignedResponseAlg()); + private Pair ifUserIsNull(AuthzRequest authzRequest, RedirectUriResponse redirectUriResponse, OAuth2AuditLog oAuth2AuditLog) throws SearchException { + identity.logout(); + final List prompts = authzRequest.getPromptList(); + if (prompts.contains(Prompt.NONE)) { + if (authenticationFilterService.isEnabled()) { + final Map params; + if (authzRequest.getHttpMethod().equals(HttpMethod.GET)) { + params = QueryStringDecoder.decode(authzRequest.getHttpRequest().getQueryString()); + } else { + params = getGenericRequestMap(authzRequest.getHttpRequest()); } - keyId = new ServerCryptoProvider(cryptoProvider).getKeyId(webKeysConfiguration, - Algorithm.fromString(signatureAlgorithm.getName()), Use.SIGNATURE); - - JSONObject jsonWebKeys = JwtUtil.getJSONWebKeys(client.getJwksUri()); - redirectUriResponse.getRedirectUri().setJsonWebKeys(jsonWebKeys); - - String clientSecret = clientService.decryptSecret(client.getClientSecret()); - redirectUriResponse.getRedirectUri().setSharedSecret(clientSecret); - redirectUriResponse.getRedirectUri().setKeyId(keyId); - } - } catch (Exception e) { - log.error(e.getMessage(), e); - } - } + String userDn = authenticationFilterService.processAuthenticationFilters(params); + if (userDn != null) { + Map genericRequestMap = getGenericRequestMap(authzRequest.getHttpRequest()); - private void validateJwtRequest(String clientId, String state, HttpServletRequest httpRequest, List responseTypes, RedirectUriResponse redirectUriResponse, JwtAuthorizationRequest jwtRequest) { - try { - jwtRequest.validate(); + Map parameterMap = Maps.newHashMap(genericRequestMap); + Map requestParameterMap = requestParameterService.getAllowedParameters(parameterMap); - authorizeRestWebServiceValidator.validateRequestObject(jwtRequest, redirectUriResponse); + SessionId sessionUser = sessionIdService.generateAuthenticatedSessionId(authzRequest.getHttpRequest(), userDn, authzRequest.getPrompt()); + sessionUser.setSessionAttributes(requestParameterMap); - // MUST be equal - if (!jwtRequest.getResponseTypes().containsAll(responseTypes) || !responseTypes.containsAll(jwtRequest.getResponseTypes())) { - throw authorizeRestWebServiceValidator.createInvalidJwtRequestException(redirectUriResponse, "The responseType parameter is not the same in the JWT"); + cookieService.createSessionIdCookie(sessionUser, authzRequest.getHttpRequest(), authzRequest.getHttpResponse(), false); + sessionIdService.updateSessionId(sessionUser); + User user = userService.getUserByDn(sessionUser.getUserDn()); + return new Pair<>(user, sessionUser); + } else { + applicationAuditLogger.sendMessage(oAuth2AuditLog); + throw new WebApplicationException(redirectUriResponse.createErrorBuilder(AuthorizeErrorResponseType.LOGIN_REQUIRED).build()); + } + } else { + throw new WebApplicationException(redirectUriResponse.createErrorBuilder(AuthorizeErrorResponseType.LOGIN_REQUIRED).build()); } - if (StringUtils.isBlank(jwtRequest.getClientId()) || !jwtRequest.getClientId().equals(clientId)) { - throw authorizeRestWebServiceValidator.createInvalidJwtRequestException(redirectUriResponse, "The clientId parameter is not the same in the JWT"); + } else { + if (prompts.contains(Prompt.LOGIN)) { + unauthenticateSession(authzRequest.getSessionId(), authzRequest.getHttpRequest()); + authzRequest.setSessionId(null); + prompts.remove(Prompt.LOGIN); + authzRequest.setPrompt(implode(prompts, " ")); } - } catch (WebApplicationException | InvalidRedirectUrlException e) { - throw e; - } catch (InvalidJwtException e) { - log.debug("Invalid JWT authorization request. {}", e.getMessage()); - redirectUriResponse.getRedirectUri().parseQueryString(errorResponseFactory.getErrorAsQueryString( - AuthorizeErrorResponseType.INVALID_REQUEST_OBJECT, state)); - throw new WebApplicationException(RedirectUtil.getRedirectResponseBuilder(redirectUriResponse.getRedirectUri(), httpRequest).build()); - } catch (Exception e) { - log.error("Unexpected exception. " + e.getMessage(), e); + + throw new WebApplicationException(redirectToAuthorizationPage(authzRequest, redirectUriResponse.getRedirectUri(), prompts, oAuth2AuditLog)); } } @@ -1000,18 +768,17 @@ private Map getGenericRequestMap(HttpServletRequest httpRequest) } private Response redirectToAuthorizationPage(AuthzRequest authzRequest, RedirectUri redirectUriResponse, - List prompts, - Map customParameters, OAuth2AuditLog oAuth2AuditLog) { - return redirectTo("/authorize", authzRequest, redirectUriResponse, prompts, customParameters, oAuth2AuditLog); + List prompts, OAuth2AuditLog oAuth2AuditLog) { + return redirectTo("/authorize", authzRequest, redirectUriResponse, prompts, oAuth2AuditLog); } private Response redirectToSelectAccountPage(AuthzRequest authzRequest, RedirectUri redirectUriResponse, - List prompts, Map customParameters, OAuth2AuditLog oAuth2AuditLog) { - return redirectTo("/selectAccount", authzRequest, redirectUriResponse, prompts, customParameters, oAuth2AuditLog); + List prompts, OAuth2AuditLog oAuth2AuditLog) { + return redirectTo("/selectAccount", authzRequest, redirectUriResponse, prompts, oAuth2AuditLog); } private Response redirectTo(String pathToRedirect, AuthzRequest authzRequest, RedirectUri redirectUriResponse, - List prompts, Map customParameters, OAuth2AuditLog oAuth2AuditLog) { + List prompts, OAuth2AuditLog oAuth2AuditLog) { final URI contextUri = URI.create(appConfiguration.getIssuer()).resolve(servletRequest.getContextPath() + pathToRedirect + configurationFactory.getFacesMapping()); @@ -1096,6 +863,7 @@ private Response redirectTo(String pathToRedirect, AuthzRequest authzRequest, Re redirectUriResponse.addResponseParameter(AuthorizeRequestParam.ORIGIN_HEADERS, authzRequest.getOriginHeaders()); } + final Map customParameters = authzRequest.getCustomParameters(); if (customParameters != null && customParameters.size() > 0) { for (Entry entry : customParameters.entrySet()) { redirectUriResponse.addResponseParameter(entry.getKey(), entry.getValue()); diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/authorize/ws/rs/AuthorizeRestWebServiceValidator.java b/jans-auth-server/server/src/main/java/io/jans/as/server/authorize/ws/rs/AuthorizeRestWebServiceValidator.java index ae8c97da660..f9ee5b753f8 100644 --- a/jans-auth-server/server/src/main/java/io/jans/as/server/authorize/ws/rs/AuthorizeRestWebServiceValidator.java +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/authorize/ws/rs/AuthorizeRestWebServiceValidator.java @@ -13,15 +13,18 @@ import io.jans.as.model.authorize.AuthorizeErrorResponseType; import io.jans.as.model.common.Prompt; import io.jans.as.model.common.ResponseMode; +import io.jans.as.model.common.ResponseType; import io.jans.as.model.configuration.AppConfiguration; import io.jans.as.model.crypto.signature.SignatureAlgorithm; import io.jans.as.model.error.ErrorResponseFactory; +import io.jans.as.model.exception.InvalidJwtException; import io.jans.as.server.model.authorize.AuthorizeParamsValidator; import io.jans.as.server.model.authorize.JwtAuthorizationRequest; import io.jans.as.server.model.common.DeviceAuthorizationCacheControl; import io.jans.as.server.model.common.SessionId; import io.jans.as.server.model.common.SessionIdState; import io.jans.as.server.model.exception.AcrChangedException; +import io.jans.as.server.model.exception.InvalidRedirectUrlException; import io.jans.as.server.security.Identity; import io.jans.as.server.service.ClientService; import io.jans.as.server.service.DeviceAuthorizationService; @@ -412,4 +415,29 @@ private void checkAcrChanged(AuthzRequest authzRequest, SessionId sessionUser) t } } } + + public void validateJwtRequest(String clientId, String state, HttpServletRequest httpRequest, List responseTypes, RedirectUriResponse redirectUriResponse, JwtAuthorizationRequest jwtRequest) { + try { + jwtRequest.validate(); + + validateRequestObject(jwtRequest, redirectUriResponse); + + // MUST be equal + if (!jwtRequest.getResponseTypes().containsAll(responseTypes) || !responseTypes.containsAll(jwtRequest.getResponseTypes())) { + throw createInvalidJwtRequestException(redirectUriResponse, "The responseType parameter is not the same in the JWT"); + } + if (StringUtils.isBlank(jwtRequest.getClientId()) || !jwtRequest.getClientId().equals(clientId)) { + throw createInvalidJwtRequestException(redirectUriResponse, "The clientId parameter is not the same in the JWT"); + } + } catch (WebApplicationException | InvalidRedirectUrlException e) { + throw e; + } catch (InvalidJwtException e) { + log.debug("Invalid JWT authorization request. {}", e.getMessage()); + redirectUriResponse.getRedirectUri().parseQueryString(errorResponseFactory.getErrorAsQueryString( + AuthorizeErrorResponseType.INVALID_REQUEST_OBJECT, state)); + throw new WebApplicationException(RedirectUtil.getRedirectResponseBuilder(redirectUriResponse.getRedirectUri(), httpRequest).build()); + } catch (Exception e) { + log.error("Unexpected exception. " + e.getMessage(), e); + } + } } diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/authorize/ws/rs/AuthzRequest.java b/jans-auth-server/server/src/main/java/io/jans/as/server/authorize/ws/rs/AuthzRequest.java index b3668b19c17..a9efa82e37b 100644 --- a/jans-auth-server/server/src/main/java/io/jans/as/server/authorize/ws/rs/AuthzRequest.java +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/authorize/ws/rs/AuthzRequest.java @@ -1,13 +1,17 @@ package io.jans.as.server.authorize.ws.rs; import io.jans.as.model.common.Prompt; +import io.jans.as.model.common.ResponseType; +import io.jans.as.server.model.authorize.JwtAuthorizationRequest; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.ws.rs.core.SecurityContext; import io.jans.as.model.common.ResponseMode; import io.jans.as.model.util.Util; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * @author Yuriy Zabrovarnyy @@ -42,6 +46,27 @@ public class AuthzRequest { private HttpServletRequest httpRequest; private HttpServletResponse httpResponse; private SecurityContext securityContext; + private Map customParameters; + private JwtAuthorizationRequest jwtRequest; + + public JwtAuthorizationRequest getJwtRequest() { + return jwtRequest; + } + + public void setJwtRequest(JwtAuthorizationRequest jwtRequest) { + this.jwtRequest = jwtRequest; + } + + public Map getCustomParameters() { + if (customParameters == null) { + customParameters = new HashMap<>(); + } + return customParameters; + } + + public void setCustomParameters(Map customParameters) { + this.customParameters = customParameters; + } public String getHttpMethod() { return httpMethod; @@ -59,6 +84,10 @@ public void setScope(String scope) { this.scope = scope; } + public List getResponseTypeList() { + return ResponseType.fromString(responseType, " "); + } + public String getResponseType() { return responseType; } 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 90cfcbae956..3970dab6cdd 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 @@ -1,11 +1,40 @@ package io.jans.as.server.authorize.ws.rs; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import io.jans.as.common.model.common.User; +import io.jans.as.common.model.registration.Client; import io.jans.as.model.authorize.AuthorizeErrorResponseType; +import io.jans.as.model.common.ResponseMode; +import io.jans.as.model.config.WebKeysConfiguration; import io.jans.as.model.configuration.AppConfiguration; +import io.jans.as.model.crypto.AbstractCryptoProvider; +import io.jans.as.model.crypto.encryption.BlockEncryptionAlgorithm; +import io.jans.as.model.crypto.encryption.KeyEncryptionAlgorithm; +import io.jans.as.model.crypto.signature.AlgorithmFamily; +import io.jans.as.model.crypto.signature.SignatureAlgorithm; import io.jans.as.model.error.ErrorResponseFactory; +import io.jans.as.model.jwe.Jwe; +import io.jans.as.model.jwk.Algorithm; +import io.jans.as.model.jwk.JSONWebKeySet; +import io.jans.as.model.jwk.Use; +import io.jans.as.model.jwt.Jwt; +import io.jans.as.model.jwt.JwtClaimName; +import io.jans.as.model.jwt.JwtHeader; +import io.jans.as.model.jwt.JwtHeaderName; +import io.jans.as.model.token.JsonWebResponse; +import io.jans.as.model.util.JwtUtil; import io.jans.as.model.util.Util; import io.jans.as.persistence.model.Par; +import io.jans.as.server.model.authorize.Claim; +import io.jans.as.server.model.authorize.IdTokenMember; +import io.jans.as.server.model.authorize.JwtAuthorizationRequest; +import io.jans.as.server.model.authorize.ScopeChecker; import io.jans.as.server.par.ws.rs.ParService; +import io.jans.as.server.service.ClientService; +import io.jans.as.server.service.RedirectUriResponse; +import io.jans.as.server.service.RequestParameterService; +import io.jans.as.server.service.ServerCryptoProvider; import jakarta.ejb.Stateless; import jakarta.inject.Inject; import jakarta.inject.Named; @@ -13,10 +42,17 @@ import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; import org.apache.commons.lang.StringUtils; +import org.jetbrains.annotations.Nullable; +import org.json.JSONObject; import org.slf4j.Logger; -import java.util.Map; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; +import java.security.PrivateKey; +import java.util.HashMap; +import java.util.Set; +import static io.jans.as.model.util.StringUtils.implode; import static org.apache.commons.lang3.BooleanUtils.isTrue; /** @@ -35,11 +71,28 @@ public class AuthzRequestService { @Inject private ErrorResponseFactory errorResponseFactory; + @Inject + private AuthorizeRestWebServiceValidator authorizeRestWebServiceValidator; + @Inject private ParService parService; + @Inject + private AbstractCryptoProvider cryptoProvider; + + @Inject + private ScopeChecker scopeChecker; + + @Inject + private RequestParameterService requestParameterService; + + @Inject + private WebKeysConfiguration webKeysConfiguration; + + @Inject + private ClientService clientService; - public boolean processPar(AuthzRequest authzRequest, Map customParameters) { + public boolean processPar(AuthzRequest authzRequest) { boolean isPar = Util.isPar(authzRequest.getRequestUri()); if (!isPar && isTrue(appConfiguration.getRequirePar())) { log.debug("Server configured for PAR only (via requirePar conf property). Failed to find PAR by request_uri (id): {}", authzRequest.getRequestUri()); @@ -85,9 +138,258 @@ public boolean processPar(AuthzRequest authzRequest, Map customP authzRequest.setOriginHeaders(par.getAttributes().getOriginHeaders()); if (StringUtils.isNotBlank(par.getAttributes().getUiLocales())) authzRequest.setUiLocales(par.getAttributes().getUiLocales()); - if (!par.getAttributes().getCustomParameters().isEmpty()) - customParameters.putAll(par.getAttributes().getCustomParameters()); + if (!par.getAttributes().getCustomParameters().isEmpty()) { + if (authzRequest.getCustomParameters() == null) { + authzRequest.setCustomParameters(new HashMap<>()); + } + authzRequest.getCustomParameters().putAll(par.getAttributes().getCustomParameters()); + } return isPar; } + + @SuppressWarnings("java:S3776") + public void processRequestObject(AuthzRequest authzRequest, Client client, RedirectUriResponse redirectUriResponse, Set scopes, User user) { + JwtAuthorizationRequest jwtRequest = null; + + if (StringUtils.isBlank(authzRequest.getRequest()) && StringUtils.isBlank(authzRequest.getRequestUri())) { + return; + } + + try { + jwtRequest = JwtAuthorizationRequest.createJwtRequest(authzRequest.getRequest(), authzRequest.getRequestUri(), client, redirectUriResponse, cryptoProvider, appConfiguration); + + if (jwtRequest == null) { + throw authorizeRestWebServiceValidator.createInvalidJwtRequestException(redirectUriResponse, "Failed to parse jwt."); + } + authzRequest.setJwtRequest(jwtRequest); + + if (StringUtils.isNotBlank(jwtRequest.getState())) { + authzRequest.setState(jwtRequest.getState()); + redirectUriResponse.setState(authzRequest.getState()); + } + if (appConfiguration.isFapi() && StringUtils.isBlank(jwtRequest.getState())) { + authzRequest.setState(""); // #1250 - FAPI : discard state if in JWT we don't have state + redirectUriResponse.setState(""); + } + + if (jwtRequest.getRedirectUri() != null) { + redirectUriResponse.getRedirectUri().setBaseRedirectUri(jwtRequest.getRedirectUri()); + } + + SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.fromString(jwtRequest.getAlgorithm()); + if (Boolean.TRUE.equals(appConfiguration.getForceSignedRequestObject()) && signatureAlgorithm == SignatureAlgorithm.NONE) { + throw authorizeRestWebServiceValidator.createInvalidJwtRequestException(redirectUriResponse, "A signed request object is required"); + } + + // JWT wins + if (!jwtRequest.getScopes().isEmpty()) { + if (!scopes.contains("openid")) { // spec: Even if a scope parameter is present in the Request Object value, a scope parameter MUST always be passed using the OAuth 2.0 request syntax containing the openid scope value + throw new WebApplicationException(Response + .status(Response.Status.BAD_REQUEST) + .entity(errorResponseFactory.getErrorAsJson(AuthorizeErrorResponseType.INVALID_SCOPE, authzRequest.getState(), "scope parameter does not contain openid value which is required.")) + .build()); + } + Set checkedScopes = scopeChecker.checkScopesPolicy(client, Lists.newArrayList(jwtRequest.getScopes())); + + scopes.clear(); + scopes.addAll(checkedScopes); + } + if (jwtRequest.getRedirectUri() != null && !jwtRequest.getRedirectUri().equals(authzRequest.getRedirectUri())) { + throw authorizeRestWebServiceValidator.createInvalidJwtRequestException(redirectUriResponse, "The redirect_uri parameter is not the same in the JWT"); + } + if (StringUtils.isNotBlank(jwtRequest.getNonce())) { + authzRequest.setNonce(jwtRequest.getNonce()); + } + if (StringUtils.isNotBlank(jwtRequest.getCodeChallenge())) { + authzRequest.setCodeChallenge(jwtRequest.getCodeChallenge()); + } + if (StringUtils.isNotBlank(jwtRequest.getCodeChallengeMethod())) { + authzRequest.setCodeChallengeMethod(jwtRequest.getCodeChallengeMethod()); + } + if (jwtRequest.getDisplay() != null && StringUtils.isNotBlank(jwtRequest.getDisplay().getParamName())) { + authzRequest.setDisplay(jwtRequest.getDisplay().getParamName()); + } + if (!jwtRequest.getPrompts().isEmpty()) { + authzRequest.setPrompt(implode(jwtRequest.getPrompts(), " ")); + } + if (jwtRequest.getResponseMode() != null) { + authzRequest.setResponseMode(jwtRequest.getResponseMode().getValue()); + redirectUriResponse.getRedirectUri().setResponseMode(jwtRequest.getResponseMode()); + } + + checkIdTokenMember(authzRequest, redirectUriResponse, user, jwtRequest); + requestParameterService.getCustomParameters(jwtRequest, authzRequest.getCustomParameters()); + } catch (WebApplicationException e) { + JsonWebResponse jwr = parseRequestToJwr(authzRequest.getRequest()); + handleJwr(authzRequest, client, redirectUriResponse, jwr); + throw e; + } catch (Exception e) { + log.error("Invalid JWT authorization request. Message : " + e.getMessage(), e); + throw authorizeRestWebServiceValidator.createInvalidJwtRequestException(redirectUriResponse, "Invalid JWT authorization request"); + } + + + // JARM + Set jwtResponseModes = Sets.newHashSet(ResponseMode.QUERY_JWT, ResponseMode.FRAGMENT_JWT, ResponseMode.JWT, ResponseMode.FORM_POST_JWT); + if (jwtResponseModes.contains(authzRequest.getResponseModeEnum())) { + JsonWebResponse jwe = parseRequestToJwr(authzRequest.getRequest()); + fillRedirectUriResponseforJARM(redirectUriResponse, jwe, client); + } + + // Validate JWT request object after JARM check, because we want to return errors well formatted (JSON/JWT). + authorizeRestWebServiceValidator.validateJwtRequest(authzRequest.getClientId(), authzRequest.getState(), authzRequest.getHttpRequest(), authzRequest.getResponseTypeList(), redirectUriResponse, jwtRequest); + } + + private void handleJwr(AuthzRequest authzRequest, Client client, RedirectUriResponse redirectUriResponse, JsonWebResponse jwr) { + if (jwr == null) { + return; + } + + String checkForAlg = jwr.getClaims().getClaimAsString("alg"); // to handle Jans Issue#310 + if ("none".equals(checkForAlg)) { + throw new WebApplicationException(Response.status(Response.Status.BAD_REQUEST) + .entity(errorResponseFactory.getErrorAsJson( + AuthorizeErrorResponseType.INVALID_REQUEST_OBJECT, "", + "The None algorithm in nested JWT is not allowed for FAPI")) + .type(MediaType.APPLICATION_JSON_TYPE).build()); + } + ResponseMode responseMode = ResponseMode.getByValue(jwr.getClaims().getClaimAsString("response_mode")); + if (responseMode == ResponseMode.JWT) { + authzRequest.setResponseMode(responseMode.getValue()); + redirectUriResponse.getRedirectUri().setResponseMode(ResponseMode.JWT); + fillRedirectUriResponseforJARM(redirectUriResponse, jwr, client); + if (appConfiguration.isFapi()) { + authorizeRestWebServiceValidator.throwInvalidJwtRequestExceptionAsJwtMode( + redirectUriResponse, "Invalid JWT authorization request", + jwr.getClaims().getClaimAsString("state"), authzRequest.getHttpRequest()); + } + } + } + + private void checkIdTokenMember(AuthzRequest authzRequest, RedirectUriResponse redirectUriResponse, User user, JwtAuthorizationRequest jwtRequest) { + final IdTokenMember idTokenMember = jwtRequest.getIdTokenMember(); + if (idTokenMember == null) { + return; + } + + if (idTokenMember.getMaxAge() != null) { + authzRequest.setMaxAge(idTokenMember.getMaxAge()); + } + final Claim acrClaim = idTokenMember.getClaim(JwtClaimName.AUTHENTICATION_CONTEXT_CLASS_REFERENCE); + if (acrClaim != null && acrClaim.getClaimValue() != null) { + authzRequest.setAcrValues(acrClaim.getClaimValue().getValueAsString()); + } + + Claim userIdClaim = idTokenMember.getClaim(JwtClaimName.SUBJECT_IDENTIFIER); + if (userIdClaim != null && userIdClaim.getClaimValue() != null + && userIdClaim.getClaimValue().getValue() != null) { + String userIdClaimValue = userIdClaim.getClaimValue().getValue(); + + if (user != null) { + String userId = user.getUserId(); + + if (!userId.equalsIgnoreCase(userIdClaimValue)) { + throw new WebApplicationException(redirectUriResponse.createErrorBuilder(AuthorizeErrorResponseType.USER_MISMATCHED).build()); + } + } + } + + } + + @Nullable + public JsonWebResponse parseRequestToJwr(String request) { + if (request == null) { + return null; + } + String[] parts = request.split("\\."); + try { + if (parts.length == 5) { + String encodedHeader = parts[0]; + JwtHeader jwtHeader = new JwtHeader(encodedHeader); + String keyId = jwtHeader.getKeyId(); + PrivateKey privateKey = null; + KeyEncryptionAlgorithm keyEncryptionAlgorithm = KeyEncryptionAlgorithm + .fromName(jwtHeader.getClaimAsString(JwtHeaderName.ALGORITHM)); + if (AlgorithmFamily.RSA.equals(keyEncryptionAlgorithm.getFamily())) { + privateKey = cryptoProvider.getPrivateKey(keyId); + } + return Jwe.parse(request, privateKey, null); + } + return Jwt.parseSilently(request); + } catch (Exception e) { + log.error(e.getMessage(), e); + return null; + } + } + + private void fillRedirectUriResponseforJARM(RedirectUriResponse redirectUriResponse, JsonWebResponse jwr, Client client) { + try { + if (jwr != null) { + String tempRedirectUri = jwr.getClaims().getClaimAsString("redirect_uri"); + if (StringUtils.isNotBlank(tempRedirectUri)) { + redirectUriResponse.getRedirectUri().setBaseRedirectUri(URLDecoder.decode(tempRedirectUri, "UTF-8")); + } + } + String clientId = client.getClientId(); + redirectUriResponse.getRedirectUri().setIssuer(appConfiguration.getIssuer()); + redirectUriResponse.getRedirectUri().setAudience(clientId); + redirectUriResponse.getRedirectUri().setAuthorizationCodeLifetime(appConfiguration.getAuthorizationCodeLifetime()); + redirectUriResponse.getRedirectUri().setSignatureAlgorithm(SignatureAlgorithm.fromString(client.getAttributes().getAuthorizationSignedResponseAlg())); + redirectUriResponse.getRedirectUri().setKeyEncryptionAlgorithm(KeyEncryptionAlgorithm.fromName(client.getAttributes().getAuthorizationEncryptedResponseAlg())); + redirectUriResponse.getRedirectUri().setBlockEncryptionAlgorithm(BlockEncryptionAlgorithm.fromName(client.getAttributes().getAuthorizationEncryptedResponseEnc())); + redirectUriResponse.getRedirectUri().setCryptoProvider(cryptoProvider); + + String keyId = null; + if (client.getAttributes().getAuthorizationEncryptedResponseAlg() != null + && client.getAttributes().getAuthorizationEncryptedResponseEnc() != null) { + if (client.getAttributes().getAuthorizationSignedResponseAlg() != null) { // Signed then Encrypted + // response + SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm + .fromString(client.getAttributes().getAuthorizationSignedResponseAlg()); + + String nestedKeyId = new ServerCryptoProvider(cryptoProvider).getKeyId(webKeysConfiguration, + Algorithm.fromString(signatureAlgorithm.getName()), Use.SIGNATURE); + + JSONObject jsonWebKeys = JwtUtil.getJSONWebKeys(client.getJwksUri()); + redirectUriResponse.getRedirectUri().setNestedJsonWebKeys(jsonWebKeys); + + String clientSecret = clientService.decryptSecret(client.getClientSecret()); + redirectUriResponse.getRedirectUri().setNestedSharedSecret(clientSecret); + redirectUriResponse.getRedirectUri().setNestedKeyId(nestedKeyId); + } + + // Encrypted response + JSONObject jsonWebKeys = JwtUtil.getJSONWebKeys(client.getJwksUri()); + if (jsonWebKeys != null) { + keyId = new ServerCryptoProvider(cryptoProvider).getKeyId(JSONWebKeySet.fromJSONObject(jsonWebKeys), + Algorithm.fromString(client.getAttributes().getAuthorizationEncryptedResponseAlg()), + Use.ENCRYPTION); + } + String sharedSecret = clientService.decryptSecret(client.getClientSecret()); + byte[] sharedSymmetricKey = sharedSecret.getBytes(StandardCharsets.UTF_8); + redirectUriResponse.getRedirectUri().setSharedSymmetricKey(sharedSymmetricKey); + redirectUriResponse.getRedirectUri().setJsonWebKeys(jsonWebKeys); + redirectUriResponse.getRedirectUri().setKeyId(keyId); + } else { // Signed response + SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.RS256; + if (client.getAttributes().getAuthorizationSignedResponseAlg() != null) { + signatureAlgorithm = SignatureAlgorithm + .fromString(client.getAttributes().getAuthorizationSignedResponseAlg()); + } + + keyId = new ServerCryptoProvider(cryptoProvider).getKeyId(webKeysConfiguration, + Algorithm.fromString(signatureAlgorithm.getName()), Use.SIGNATURE); + + JSONObject jsonWebKeys = JwtUtil.getJSONWebKeys(client.getJwksUri()); + redirectUriResponse.getRedirectUri().setJsonWebKeys(jsonWebKeys); + + String clientSecret = clientService.decryptSecret(client.getClientSecret()); + redirectUriResponse.getRedirectUri().setSharedSecret(clientSecret); + redirectUriResponse.getRedirectUri().setKeyId(keyId); + } + } catch (Exception e) { + log.error(e.getMessage(), e); + } + } }