diff --git a/jans-auth-server/client/src/test/java/io/jans/as/client/client/assertbuilders/RegisterResponseAssertBuilder.java b/jans-auth-server/client/src/test/java/io/jans/as/client/client/assertbuilders/RegisterResponseAssertBuilder.java index 0b823b2d0d7..9de304dbd8d 100644 --- a/jans-auth-server/client/src/test/java/io/jans/as/client/client/assertbuilders/RegisterResponseAssertBuilder.java +++ b/jans-auth-server/client/src/test/java/io/jans/as/client/client/assertbuilders/RegisterResponseAssertBuilder.java @@ -3,6 +3,7 @@ import io.jans.as.client.RegisterResponse; import io.jans.as.model.common.BackchannelTokenDeliveryMode; import io.jans.as.model.crypto.signature.AsymmetricSignatureAlgorithm; +import org.apache.commons.lang3.BooleanUtils; import static io.jans.as.model.register.RegisterRequestParam.*; import static org.testng.Assert.*; @@ -84,7 +85,7 @@ public void check() { assertTrue(response.getClaims().containsKey(BACKCHANNEL_AUTHENTICATION_REQUEST_SIGNING_ALG.toString())); assertEquals(response.getClaims().get(BACKCHANNEL_AUTHENTICATION_REQUEST_SIGNING_ALG.toString()), backchannelRequestSigningAlgorithm.getValue()); } - if (backchannelUserCodeParameter != null) { + if (BooleanUtils.isTrue(backchannelUserCodeParameter)) { assertTrue(response.getClaims().containsKey(BACKCHANNEL_USER_CODE_PARAMETER.toString())); assertEquals(response.getClaims().get(BACKCHANNEL_USER_CODE_PARAMETER.toString()), String.valueOf(backchannelUserCodeParameter)); } diff --git a/jans-auth-server/model/pom.xml b/jans-auth-server/model/pom.xml index 2641ddf410d..bf5c55833e4 100644 --- a/jans-auth-server/model/pom.xml +++ b/jans-auth-server/model/pom.xml @@ -101,7 +101,7 @@ com.fasterxml.jackson.module - jackson-module-jaxb-annotations + jackson-module-jakarta-xmlbind-annotations commons-codec 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 8733ae0b1ca..c1e27bc8bcf 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 @@ -332,23 +332,20 @@ private static boolean canLogWebApplicationException(WebApplicationException e) private ResponseBuilder authorize(AuthzRequest authzRequest) throws AcrChangedException, SearchException, TokenBindingParseException { String tokenBindingHeader = authzRequest.getHttpRequest().getHeader("Sec-Token-Binding"); + boolean isPar = authzRequestService.processPar(authzRequest); + List prompts = Prompt.fromString(authzRequest.getPrompt(), " "); final List responseTypes = authzRequest.getResponseTypeList(); SessionId sessionUser = identity.getSessionId(); User user = sessionIdService.getUser(sessionUser); - boolean isPar = authzRequestService.processPar(authzRequest); - - Map customResponseHeaders = Util.jsonObjectArrayStringAsMap(authzRequest.getCustomResponseHeaders()); - updateSessionForROPC(authzRequest.getHttpRequest(), sessionUser); Client client = authorizeRestWebServiceValidator.validateClient(authzRequest, isPar); String deviceAuthzUserCode = deviceAuthorizationService.getUserCodeFromSession(authzRequest.getHttpRequest()); authzRequest.setRedirectUri(authorizeRestWebServiceValidator.validateRedirectUri(client, authzRequest.getRedirectUri(), authzRequest.getState(), deviceAuthzUserCode, authzRequest.getHttpRequest())); - authzRequestService.createRedirectUriResponse(authzRequest); authorizeRestWebServiceValidator.validateAcrs(authzRequest, client); @@ -357,7 +354,8 @@ private ResponseBuilder authorize(AuthzRequest authzRequest) throws AcrChangedEx authorizeRestWebServiceValidator.checkSignedRequestRequired(authzRequest); - authzRequestService.processRequestObject(authzRequest, client, scopes, user); + authzRequestService.processRequestObject(authzRequest, client, scopes, user, prompts); + validateRequestJwt(authzRequest, isPar, client); authorizeRestWebServiceValidator.validate(authzRequest, responseTypes, client); @@ -503,11 +501,7 @@ private ResponseBuilder authorize(AuthzRequest authzRequest) throws AcrChangedEx ResponseBuilder builder = RedirectUtil.getRedirectResponseBuilder(authzRequest.getRedirectUriResponse().getRedirectUri(), authzRequest.getHttpRequest()); - if (isTrue(appConfiguration.getCustomHeadersWithAuthorizationResponse())) { - for (Entry entry : customResponseHeaders.entrySet()) { - builder.header(entry.getKey(), entry.getValue()); - } - } + addCustomHeaders(builder, authzRequest); runCiba(authzRequest.getAuthReqId(), client, authzRequest.getHttpRequest(), authzRequest.getHttpResponse()); processDeviceAuthorization(deviceAuthzUserCode, user); @@ -515,6 +509,16 @@ private ResponseBuilder authorize(AuthzRequest authzRequest) throws AcrChangedEx return builder; } + private void addCustomHeaders(ResponseBuilder builder, AuthzRequest authzRequest) { + if (isTrue(appConfiguration.getCustomHeadersWithAuthorizationResponse())) { + + Map customResponseHeaders = Util.jsonObjectArrayStringAsMap(authzRequest.getCustomResponseHeaders()); + for (Entry entry : customResponseHeaders.entrySet()) { + builder.header(entry.getKey(), entry.getValue()); + } + } + } + private void addResponseParameterScope(AuthzRequest authzRequest, AuthorizationGrant authorizationGrant) { if (authorizationGrant != null && !appConfiguration.isFapi()) { authzRequest.setScope(authorizationGrant.checkScopesPolicy(authzRequest.getScope())); 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 26d727794b3..e9001a51de0 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 @@ -7,6 +7,7 @@ import io.jans.as.common.util.RedirectUri; import io.jans.as.common.util.CommonUtils; 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.config.WebKeysConfiguration; import io.jans.as.model.configuration.AppConfiguration; @@ -25,7 +26,6 @@ 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.audit.Action; @@ -56,6 +56,7 @@ import java.nio.charset.StandardCharsets; import java.security.PrivateKey; import java.util.HashMap; +import java.util.List; import java.util.Set; import static io.jans.as.model.util.StringUtils.implode; @@ -68,6 +69,8 @@ @Named public class AuthzRequestService { + public static final String INVALID_JWT_AUTHORIZATION_REQUEST = "Invalid JWT authorization request"; + @Inject private Logger log; @@ -155,100 +158,119 @@ public boolean processPar(AuthzRequest authzRequest) { } @SuppressWarnings("java:S3776") - public void processRequestObject(AuthzRequest authzRequest, Client client, Set scopes, User user) { - JwtAuthorizationRequest jwtRequest = null; - - if (StringUtils.isBlank(authzRequest.getRequest()) && StringUtils.isBlank(authzRequest.getRequestUri())) { - return; - } + public void processRequestObject(AuthzRequest authzRequest, Client client, Set scopes, User user, List prompts) { + final RedirectUriResponse redirectUriResponse = authzRequest.getRedirectUriResponse(); - RedirectUriResponse redirectUriResponse = authzRequest.getRedirectUriResponse(); - try { - jwtRequest = JwtAuthorizationRequest.createJwtRequest(authzRequest.getRequest(), authzRequest.getRequestUri(), client, redirectUriResponse, cryptoProvider, appConfiguration); + 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."); - } - authzRequest.setJwtRequest(jwtRequest); + 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 (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()); - } + 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"); - } + 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()); + // 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.clear(); + scopes.addAll(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.clear(); + prompts.addAll(Lists.newArrayList(jwtRequest.getPrompts())); + authzRequest.setPrompt(implode(prompts, " ")); + } + if (jwtRequest.getResponseMode() != null) { + authzRequest.setResponseMode(jwtRequest.getResponseMode().getValue()); + redirectUriResponse.getRedirectUri().setResponseMode(jwtRequest.getResponseMode()); } - 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()); + 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); } - - 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); + if (jwtRequest != null) { + authorizeRestWebServiceValidator.validateJwtRequest(authzRequest.getClientId(), authzRequest.getState(), authzRequest.getHttpRequest(), authzRequest.getResponseTypeList(), redirectUriResponse, jwtRequest); + } } - private void handleJwr(AuthzRequest authzRequest, Client client, RedirectUriResponse redirectUriResponse, JsonWebResponse jwr) { + public void handleJwr(AuthzRequest authzRequest, Client client, RedirectUriResponse redirectUriResponse, JsonWebResponse jwr) { if (jwr == null) { return; } @@ -268,13 +290,13 @@ private void handleJwr(AuthzRequest authzRequest, Client client, RedirectUriResp fillRedirectUriResponseforJARM(redirectUriResponse, jwr, client); if (appConfiguration.isFapi()) { authorizeRestWebServiceValidator.throwInvalidJwtRequestExceptionAsJwtMode( - redirectUriResponse, "Invalid JWT authorization request", + redirectUriResponse, INVALID_JWT_AUTHORIZATION_REQUEST, jwr.getClaims().getClaimAsString("state"), authzRequest.getHttpRequest()); } } } - private void checkIdTokenMember(AuthzRequest authzRequest, RedirectUriResponse redirectUriResponse, User user, JwtAuthorizationRequest jwtRequest) { + public void checkIdTokenMember(AuthzRequest authzRequest, RedirectUriResponse redirectUriResponse, User user, JwtAuthorizationRequest jwtRequest) { final IdTokenMember idTokenMember = jwtRequest.getIdTokenMember(); if (idTokenMember == null) { return; @@ -330,7 +352,7 @@ public JsonWebResponse parseRequestToJwr(String request) { } } - private void fillRedirectUriResponseforJARM(RedirectUriResponse redirectUriResponse, JsonWebResponse jwr, Client client) { + public void fillRedirectUriResponseforJARM(RedirectUriResponse redirectUriResponse, JsonWebResponse jwr, Client client) { try { if (jwr != null) { String tempRedirectUri = jwr.getClaims().getClaimAsString("redirect_uri"); diff --git a/jans-bom/pom.xml b/jans-bom/pom.xml index 9cd56a00733..d66f7da2e62 100644 --- a/jans-bom/pom.xml +++ b/jans-bom/pom.xml @@ -518,8 +518,8 @@ ${jackson.version} - com.fasterxml.jackson.module - jackson-module-jaxb-annotations + com.fasterxml.jackson.module + jackson-module-jakarta-xmlbind-annotations ${jackson.version}