From 0e865fbace7d12634ad57b510a1ad81a9067f01f Mon Sep 17 00:00:00 2001 From: Javier Rojas Date: Thu, 3 Feb 2022 06:14:36 -0400 Subject: [PATCH] feat(jans-auth-server): allow return custom authz params to rp in response (#756) * feat(jans-auth-server): allow return of custom authz endpoint request parameters to rp in response * feat(jans-auth-server): allow return of custom authz endpoint request parameters to rp in response --- ...AuthorizationResponseCustomHeaderTest.java | 23 +++------- .../rs/AuthorizationSupportCustomParams.java | 22 +++++++--- .../client/src/test/resources/testng.xml | 6 +++ .../model/configuration/AppConfiguration.java | 8 ++-- .../AuthorizationRequestCustomParameter.java | 44 +++++++++++++++++++ jans-auth-server/server/conf/jans-config.json | 23 ++++++++-- .../ws/rs/AuthorizeRestWebServiceImpl.java | 15 ++++--- .../service/RequestParameterService.java | 41 ++++++++++------- .../templates/jans-auth/jans-auth-config.json | 23 ++++++++-- .../templates/jans-auth/jans-auth-config.json | 23 ++++++++-- 10 files changed, 171 insertions(+), 57 deletions(-) create mode 100644 jans-auth-server/model/src/main/java/io/jans/as/model/configuration/AuthorizationRequestCustomParameter.java diff --git a/jans-auth-server/client/src/test/java/io/jans/as/client/ws/rs/AuthorizationResponseCustomHeaderTest.java b/jans-auth-server/client/src/test/java/io/jans/as/client/ws/rs/AuthorizationResponseCustomHeaderTest.java index f3cbe562f57..65ebbb9122a 100644 --- a/jans-auth-server/client/src/test/java/io/jans/as/client/ws/rs/AuthorizationResponseCustomHeaderTest.java +++ b/jans-auth-server/client/src/test/java/io/jans/as/client/ws/rs/AuthorizationResponseCustomHeaderTest.java @@ -6,13 +6,7 @@ package io.jans.as.client.ws.rs; -import io.jans.as.client.AuthorizationRequest; -import io.jans.as.client.AuthorizationResponse; -import io.jans.as.client.AuthorizeClient; -import io.jans.as.client.BaseTest; -import io.jans.as.client.RegisterClient; -import io.jans.as.client.RegisterRequest; -import io.jans.as.client.RegisterResponse; +import io.jans.as.client.*; import io.jans.as.model.common.Prompt; import io.jans.as.model.common.ResponseType; import io.jans.as.model.register.ApplicationType; @@ -21,19 +15,13 @@ import org.testng.annotations.DataProvider; import org.testng.annotations.Test; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; +import java.util.*; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertTrue; +import static org.testng.Assert.*; /** * @author Javier Rojas Blum - * @version December 26, 2016 + * @version February 2, 2022 */ public class AuthorizationResponseCustomHeaderTest extends BaseTest { @@ -93,6 +81,9 @@ public void requestAuthorizationCustomHeader( assertTrue(authorizationResponse.getHeaders().containsKey("CustomHeader1")); assertTrue(authorizationResponse.getHeaders().containsKey("CustomHeader2")); assertTrue(authorizationResponse.getHeaders().containsKey("CustomHeader3")); + assertTrue(authorizationResponse.getHeaders().get("CustomHeader1").contains("custom_header_value_1")); + assertTrue(authorizationResponse.getHeaders().get("CustomHeader2").contains("custom_header_value_2")); + assertTrue(authorizationResponse.getHeaders().get("CustomHeader3").contains("custom_header_value_3")); } @DataProvider(name = "requestAuthorizationCustomHeaderDataProvider") diff --git a/jans-auth-server/client/src/test/java/io/jans/as/client/ws/rs/AuthorizationSupportCustomParams.java b/jans-auth-server/client/src/test/java/io/jans/as/client/ws/rs/AuthorizationSupportCustomParams.java index 13b988a48e0..a1a6c2ed280 100644 --- a/jans-auth-server/client/src/test/java/io/jans/as/client/ws/rs/AuthorizationSupportCustomParams.java +++ b/jans-auth-server/client/src/test/java/io/jans/as/client/ws/rs/AuthorizationSupportCustomParams.java @@ -23,12 +23,11 @@ import java.util.List; import java.util.UUID; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.*; /** * @author Javier Rojas Blum - * @version November 23, 2017 + * @version February 2, 2022 */ public class AuthorizationSupportCustomParams extends BaseTest { @@ -68,9 +67,11 @@ public void authorizationSupportCustomParams( AuthorizationRequest authorizationRequest = new AuthorizationRequest(responseTypes, clientId, scopes, redirectUri, nonce); authorizationRequest.setState(state); - authorizationRequest.addCustomParameter("customParam1", "value1"); - authorizationRequest.addCustomParameter("customParam2", "value2"); - authorizationRequest.addCustomParameter("customParam3", "value3"); + authorizationRequest.addCustomParameter("customParam1", "value1"); // returnInResponse = false + authorizationRequest.addCustomParameter("customParam2", "value2"); // returnInResponse = false + authorizationRequest.addCustomParameter("customParam3", "value3"); // returnInResponse = false + authorizationRequest.addCustomParameter("customParam4", "value4"); // returnInResponse = true + authorizationRequest.addCustomParameter("customParam5", "value5"); // returnInResponse = true AuthorizeClient authorizeClient = new AuthorizeClient(authorizationEndpoint); authorizeClient.setRequest(authorizationRequest); @@ -84,6 +85,15 @@ public void authorizationSupportCustomParams( assertNotNull(authorizationResponse.getIdToken(), "The idToken is null"); assertNotNull(authorizationResponse.getState(), "The state is null"); + assertNotNull(authorizationResponse.getCustomParams()); + assertFalse(authorizationResponse.getCustomParams().containsKey("customParam1")); + assertFalse(authorizationResponse.getCustomParams().containsKey("customParam2")); + assertFalse(authorizationResponse.getCustomParams().containsKey("customParam3")); + assertTrue(authorizationResponse.getCustomParams().containsKey("customParam4")); + assertTrue(authorizationResponse.getCustomParams().containsKey("customParam5")); + assertEquals(authorizationResponse.getCustomParams().get("customParam4"), "value4"); + assertEquals(authorizationResponse.getCustomParams().get("customParam5"), "value5"); + // NOTE: After complete successfully this test, check whether the stored session in LDAP has the 3 custom params // stored in its session attributes list. } diff --git a/jans-auth-server/client/src/test/resources/testng.xml b/jans-auth-server/client/src/test/resources/testng.xml index 1b152975ff0..0e07cb5abeb 100644 --- a/jans-auth-server/client/src/test/resources/testng.xml +++ b/jans-auth-server/client/src/test/resources/testng.xml @@ -90,6 +90,12 @@ + + + + + + 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 e7d7a780173..a9f1411430c 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 @@ -28,7 +28,7 @@ * @author Javier Rojas Blum * @author Yuriy Zabrovarnyy * @author Yuriy Movchan - * @version September 30, 2021 + * @version February 2, 2022 */ @JsonIgnoreProperties(ignoreUnknown = true) public class AppConfiguration implements Configuration { @@ -238,7 +238,7 @@ public class AppConfiguration implements Configuration { private Boolean logClientIdOnClientAuthentication; private Boolean logClientNameOnClientAuthentication; private Boolean disableJdkLogger = true; - private Set authorizationRequestCustomAllowedParameters; + private Set authorizationRequestCustomAllowedParameters; private Boolean openidScopeBackwardCompatibility = false; private Boolean disableU2fEndpoint = false; @@ -2010,11 +2010,11 @@ public void setExternalLoggerConfiguration(String externalLoggerConfiguration) { this.externalLoggerConfiguration = externalLoggerConfiguration; } - public Set getAuthorizationRequestCustomAllowedParameters() { + public Set getAuthorizationRequestCustomAllowedParameters() { return authorizationRequestCustomAllowedParameters; } - public void setAuthorizationRequestCustomAllowedParameters(Set authorizationRequestCustomAllowedParameters) { + public void setAuthorizationRequestCustomAllowedParameters(Set authorizationRequestCustomAllowedParameters) { this.authorizationRequestCustomAllowedParameters = authorizationRequestCustomAllowedParameters; } diff --git a/jans-auth-server/model/src/main/java/io/jans/as/model/configuration/AuthorizationRequestCustomParameter.java b/jans-auth-server/model/src/main/java/io/jans/as/model/configuration/AuthorizationRequestCustomParameter.java new file mode 100644 index 00000000000..3c3eb11b460 --- /dev/null +++ b/jans-auth-server/model/src/main/java/io/jans/as/model/configuration/AuthorizationRequestCustomParameter.java @@ -0,0 +1,44 @@ +/* + * Janssen Project software is available under the Apache License (2004). See http://www.apache.org/licenses/ for full text. + * + * Copyright (c) 2020, Janssen Project + */ + +package io.jans.as.model.configuration; + +import io.jans.util.exception.ConfigurationException; +import org.apache.commons.lang.StringUtils; + +/** + * @author Javier Rojas Blum + * @version February 2, 2022 + */ +public class AuthorizationRequestCustomParameter { + + private String paramName; + private Boolean returnInResponse; + + private static final Boolean DEFAULT_RETURN_IN_RESPONSE = false; + + public String getParamName() { + if (StringUtils.isEmpty(paramName)) { + throw new ConfigurationException("The param name in a AuthorizationRequestCustomParameter is empty"); + } + return paramName; + } + + public void setParamName(String paramName) { + this.paramName = paramName; + } + + public Boolean getReturnInResponse() { + if (returnInResponse == null) { + returnInResponse = DEFAULT_RETURN_IN_RESPONSE; + } + return returnInResponse; + } + + public void setReturnInResponse(Boolean returnInResponse) { + this.returnInResponse = returnInResponse; + } +} diff --git a/jans-auth-server/server/conf/jans-config.json b/jans-auth-server/server/conf/jans-config.json index f328badd3c7..64b04744c52 100644 --- a/jans-auth-server/server/conf/jans-config.json +++ b/jans-auth-server/server/conf/jans-config.json @@ -359,9 +359,26 @@ "logClientIdOnClientAuthentication": true, "logClientNameOnClientAuthentication": false, "authorizationRequestCustomAllowedParameters" : [ - "customParam1", - "customParam2", - "customParam3" + { + "paramName": "customParam1", + "returnInResponse": false + }, + { + "paramName": "customParam2", + "returnInResponse": false + }, + { + "paramName": "customParam3", + "returnInResponse": false + }, + { + "paramName": "customParam4", + "returnInResponse": true + }, + { + "paramName": "customParam5", + "returnInResponse": true + } ], "legacyDynamicRegistrationScopeParam": false, "openidScopeBackwardCompatibility": false, 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 d194b8d2bfb..4901b62d4c8 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 @@ -28,8 +28,8 @@ 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.JwtClaims; import io.jans.as.model.jwt.JwtClaimName; +import io.jans.as.model.jwt.JwtClaims; import io.jans.as.model.token.JsonWebResponse; import io.jans.as.model.util.JwtUtil; import io.jans.as.model.util.Util; @@ -66,7 +66,6 @@ import org.apache.commons.collections.CollectionUtils; 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; @@ -95,7 +94,7 @@ * Implementation for request authorization through REST web services. * * @author Javier Rojas Blum - * @version January 24, 2022 + * @version February 2, 2022 */ @Path("/") public class AuthorizeRestWebServiceImpl implements AuthorizeRestWebService { @@ -708,6 +707,10 @@ private Response requestAuthorization( redirectUriResponse.getRedirectUri().addResponseParameter(AuthorizeResponseParam.ACR_VALUES, acrValuesStr); } + for (Map.Entry customParam : requestParameterService.getCustomParameters(customParameters, true).entrySet()) { + redirectUriResponse.getRedirectUri().addResponseParameter(customParam.getKey(), customParam.getValue()); + } + if (sessionUser.getId() == null) { final SessionId newSessionUser = sessionIdService.generateAuthenticatedSessionId(httpRequest, sessionUser.getUserDn(), prompt); String newSessionId = newSessionUser.getId(); @@ -781,7 +784,6 @@ private Response requestAuthorization( return builder.build(); } - @Nullable private ResponseMode extractResponseMode(String request) { final Jwt jwt = Jwt.parseSilently(request); if (jwt == null) { @@ -799,6 +801,7 @@ private void fillRedirectUriResponseforJARM(RedirectUriResponse redirectUriRespo redirectUriResponse.getRedirectUri().setBaseRedirectUri(URLDecoder.decode(tempRedirectUri, "UTF-8")); } } + redirectUriResponse.getRedirectUri().setResponseMode(ResponseMode.JWT); String clientId = client.getClientId(); redirectUriResponse.getRedirectUri().setIssuer(appConfiguration.getIssuer()); redirectUriResponse.getRedirectUri().setAudience(clientId); @@ -812,7 +815,7 @@ private void fillRedirectUriResponseforJARM(RedirectUriResponse redirectUriRespo if (client.getAttributes().getAuthorizationEncryptedResponseAlg() != null && client.getAttributes().getAuthorizationEncryptedResponseEnc() != null) { if (client.getAttributes().getAuthorizationSignedResponseAlg() != null) { // Signed then Encrypted - // response + // response SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm .fromString(client.getAttributes().getAuthorizationSignedResponseAlg()); @@ -860,7 +863,7 @@ private void fillRedirectUriResponseforJARM(RedirectUriResponse redirectUriRespo log.error(e.getMessage(), e); } } - + private void validateJwtRequest(String clientId, String state, HttpServletRequest httpRequest, List responseTypes, RedirectUriResponse redirectUriResponse, JwtAuthorizationRequest jwtRequest) { try { jwtRequest.validate(); diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/service/RequestParameterService.java b/jans-auth-server/server/src/main/java/io/jans/as/server/service/RequestParameterService.java index 51b6ad74c10..ac1004a9332 100644 --- a/jans-auth-server/server/src/main/java/io/jans/as/server/service/RequestParameterService.java +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/service/RequestParameterService.java @@ -9,6 +9,7 @@ import com.google.common.collect.Lists; import io.jans.as.model.authorize.AuthorizeRequestParam; import io.jans.as.model.configuration.AppConfiguration; +import io.jans.as.model.configuration.AuthorizationRequestCustomParameter; import io.jans.as.model.util.Util; import io.jans.as.server.model.authorize.JwtAuthorizationRequest; import io.jans.model.security.Identity; @@ -23,21 +24,15 @@ import javax.inject.Named; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.Map.Entry; -import java.util.Set; import static org.apache.commons.lang3.BooleanUtils.isTrue; /** * @author Yuriy Movchan * @author Javier Rojas Blum - * @version October 7, 2019 + * @version February 2, 2022 */ @Stateless @Named @@ -85,7 +80,7 @@ private List getAllAllowedParameters() { } public Map getAllowedParameters(@Nonnull final Map requestParameterMap) { - Set authorizationRequestCustomAllowedParameters = appConfiguration.getAuthorizationRequestCustomAllowedParameters(); + Set authorizationRequestCustomAllowedParameters = appConfiguration.getAuthorizationRequestCustomAllowedParameters(); if (authorizationRequestCustomAllowedParameters == null) { authorizationRequestCustomAllowedParameters = new HashSet<>(0); } @@ -98,7 +93,9 @@ public Map getAllowedParameters(@Nonnull final Map allAllowed = getAllAllowedParameters(); final Set> set = requestParameterMap.entrySet(); for (Map.Entry entry : set) { - if (allAllowed.contains(entry.getKey()) || authorizationRequestCustomAllowedParameters.contains(entry.getKey())) { + if (allAllowed.contains(entry.getKey()) + || authorizationRequestCustomAllowedParameters.stream() + .filter(o -> StringUtils.isNotBlank(o.getParamName()) && o.getParamName().equals(entry.getKey())).findFirst().isPresent()) { result.put(entry.getKey(), entry.getValue()); } } @@ -106,7 +103,11 @@ public Map getAllowedParameters(@Nonnull final Map getCustomParameters(@Nonnull final Map requestParameterMap) { - Set authorizationRequestCustomAllowedParameters = appConfiguration.getAuthorizationRequestCustomAllowedParameters(); + return getCustomParameters(requestParameterMap, false); + } + + public Map getCustomParameters(@Nonnull final Map requestParameterMap, boolean onlyReturnInResponseParams) { + Set authorizationRequestCustomAllowedParameters = appConfiguration.getAuthorizationRequestCustomAllowedParameters(); final Map result = new HashMap<>(); if (authorizationRequestCustomAllowedParameters == null) { @@ -116,7 +117,15 @@ public Map getCustomParameters(@Nonnull final Map> set = requestParameterMap.entrySet(); for (Map.Entry entry : set) { - if (authorizationRequestCustomAllowedParameters.contains(entry.getKey())) { + + if (onlyReturnInResponseParams && authorizationRequestCustomAllowedParameters.stream() + .filter(o -> StringUtils.isNotBlank(o.getParamName()) + && o.getParamName().equals(entry.getKey()) + && o.getReturnInResponse()).findFirst().isPresent()) { + result.put(entry.getKey(), entry.getValue()); + } else if (!onlyReturnInResponseParams && authorizationRequestCustomAllowedParameters.stream() + .filter(o -> StringUtils.isNotBlank(o.getParamName()) + && o.getParamName().equals(entry.getKey())).findFirst().isPresent()) { result.put(entry.getKey(), entry.getValue()); } } @@ -213,16 +222,16 @@ public Object getTypedValue(String stringValue, String type) { * @param customParameters Custom parameters used in the authorization flow. */ public void getCustomParameters(JwtAuthorizationRequest jwtRequest, Map customParameters) { - Set authorizationRequestCustomAllowedParameters = appConfiguration.getAuthorizationRequestCustomAllowedParameters(); + Set authorizationRequestCustomAllowedParameters = appConfiguration.getAuthorizationRequestCustomAllowedParameters(); if (authorizationRequestCustomAllowedParameters == null) { return; } JSONObject jsonPayload = jwtRequest.getJsonPayload(); - for (String customParam : authorizationRequestCustomAllowedParameters) { - if (jsonPayload.has(customParam)) { - customParameters.put(customParam, jsonPayload.getString(customParam)); + for (AuthorizationRequestCustomParameter customParam : authorizationRequestCustomAllowedParameters) { + if (jsonPayload.has(customParam.getParamName())) { + customParameters.put(customParam.getParamName(), jsonPayload.getString(customParam.getParamName())); } } } diff --git a/jans-linux-setup/openbanking/templates/jans-auth/jans-auth-config.json b/jans-linux-setup/openbanking/templates/jans-auth/jans-auth-config.json index 9a725d94266..18e6720029f 100644 --- a/jans-linux-setup/openbanking/templates/jans-auth/jans-auth-config.json +++ b/jans-linux-setup/openbanking/templates/jans-auth/jans-auth-config.json @@ -283,9 +283,26 @@ "httpLoggingExludePaths": [], "externalLoggerConfiguration": "", "authorizationRequestCustomAllowedParameters" : [ - "customParam1", - "customParam2", - "customParam3" + { + "paramName": "customParam1", + "returnInResponse": false + }, + { + "paramName": "customParam2", + "returnInResponse": false + }, + { + "paramName": "customParam3", + "returnInResponse": false + }, + { + "paramName": "customParam4", + "returnInResponse": true + }, + { + "paramName": "customParam5", + "returnInResponse": true + } ], "legacyDynamicRegistrationScopeParam": false, "openidScopeBackwardCompatibility": false, diff --git a/jans-linux-setup/templates/jans-auth/jans-auth-config.json b/jans-linux-setup/templates/jans-auth/jans-auth-config.json index eaad1581f21..061fda73325 100644 --- a/jans-linux-setup/templates/jans-auth/jans-auth-config.json +++ b/jans-linux-setup/templates/jans-auth/jans-auth-config.json @@ -374,9 +374,26 @@ "httpLoggingExludePaths": [], "externalLoggerConfiguration": "", "authorizationRequestCustomAllowedParameters" : [ - "customParam1", - "customParam2", - "customParam3" + { + "paramName": "customParam1", + "returnInResponse": false + }, + { + "paramName": "customParam2", + "returnInResponse": false + }, + { + "paramName": "customParam3", + "returnInResponse": false + }, + { + "paramName": "customParam4", + "returnInResponse": true + }, + { + "paramName": "customParam5", + "returnInResponse": true + } ], "legacyDynamicRegistrationScopeParam": false, "openidScopeBackwardCompatibility": false,