diff --git a/jans-auth-server/client/src/main/java/io/jans/as/client/RegisterRequest.java b/jans-auth-server/client/src/main/java/io/jans/as/client/RegisterRequest.java index 4ca99821857..3bb4e38ead4 100644 --- a/jans-auth-server/client/src/main/java/io/jans/as/client/RegisterRequest.java +++ b/jans-auth-server/client/src/main/java/io/jans/as/client/RegisterRequest.java @@ -39,7 +39,7 @@ * * @author Javier Rojas Blum * @author Yuriy Zabrovarnyy - * @version March 2, 2022 + * @version March 17, 2022 */ public class RegisterRequest extends BaseRequest { @@ -114,6 +114,7 @@ public class RegisterRequest extends BaseRequest { private String softwareVersion; private String softwareStatement; private Boolean defaultPromptLogin; + private List authorizedAcrValues; private BackchannelTokenDeliveryMode backchannelTokenDeliveryMode; private String backchannelClientNotificationEndpoint; private AsymmetricSignatureAlgorithm backchannelAuthenticationRequestSigningAlg; @@ -157,6 +158,7 @@ public RegisterRequest() { this.scope = new ArrayList<>(); this.claims = new ArrayList<>(); this.customAttributes = new HashMap<>(); + this.authorizedAcrValues = new ArrayList<>(); } /** @@ -1171,6 +1173,14 @@ public void setDefaultPromptLogin(Boolean defaultPromptLogin) { this.defaultPromptLogin = defaultPromptLogin; } + public List getAuthorizedAcrValues() { + return authorizedAcrValues; + } + + public void setAuthorizedAcrValues(List authorizedAcrValues) { + this.authorizedAcrValues = authorizedAcrValues; + } + public String getHttpMethod() { return httpMethod; } @@ -1396,9 +1406,11 @@ public Map getParameters() { if (defaultPromptLogin != null) { parameters.put(DEFAULT_PROMPT_LOGIN.getName(), defaultPromptLogin.toString()); } - + if (authorizedAcrValues != null && !authorizedAcrValues.isEmpty()) { + parameters.put(AUTHORIZED_ACR_VALUES.getName(), toJSONArray(authorizedAcrValues).toString()); + } if (redirectUrisRegex != null) { - parameters.put(REDIRECT_URIS_REGEX.toString(), redirectUrisRegex.toString()) ; + parameters.put(REDIRECT_URIS_REGEX.toString(), redirectUrisRegex.toString()); } // Custom params @@ -1488,6 +1500,7 @@ public static RegisterRequest fromJson(JSONObject requestObject) throws JSONExce result.setBackchannelUserCodeParameter(booleanOrNull(requestObject, BACKCHANNEL_USER_CODE_PARAMETER.toString())); result.setRedirectUrisRegex(requestObject.optString(REDIRECT_URIS_REGEX.toString())); result.setDefaultPromptLogin(requestObject.optBoolean(DEFAULT_PROMPT_LOGIN.getName())); + result.setAuthorizedAcrValues(extractListByKey(requestObject, AUTHORIZED_ACR_VALUES.getName())); return result; } @@ -1701,12 +1714,15 @@ public JSONObject getJSONParameters() throws JSONException { } if (redirectUrisRegex != null) { - parameters.put(REDIRECT_URIS_REGEX.toString(), redirectUrisRegex) ; + parameters.put(REDIRECT_URIS_REGEX.toString(), redirectUrisRegex); } if (defaultPromptLogin != null) { parameters.put(DEFAULT_PROMPT_LOGIN.getName(), defaultPromptLogin); } + if (authorizedAcrValues != null && !authorizedAcrValues.isEmpty()) { + parameters.put(AUTHORIZED_ACR_VALUES.toString(), toJSONArray(authorizedAcrValues)); + } // Custom params if (customAttributes != null && !customAttributes.isEmpty()) { diff --git a/jans-auth-server/client/src/test/java/io/jans/as/client/ws/rs/AuthorizedAcrValuesTest.java b/jans-auth-server/client/src/test/java/io/jans/as/client/ws/rs/AuthorizedAcrValuesTest.java new file mode 100644 index 00000000000..bd7a5e0ba64 --- /dev/null +++ b/jans-auth-server/client/src/test/java/io/jans/as/client/ws/rs/AuthorizedAcrValuesTest.java @@ -0,0 +1,117 @@ +/* + * 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.client.ws.rs; + +import io.jans.as.client.*; +import io.jans.as.client.client.AssertBuilder; +import io.jans.as.model.common.ResponseType; +import io.jans.as.model.register.ApplicationType; +import io.jans.as.model.util.StringUtils; +import org.testng.annotations.Parameters; +import org.testng.annotations.Test; + +import java.util.Arrays; +import java.util.List; +import java.util.UUID; + +/** + * @author Javier Rojas Blum + * @version March 17, 2022 + */ +public class AuthorizedAcrValuesTest extends BaseTest { + + @Parameters({"userId", "userSecret", "redirectUris", "redirectUri", "sectorIdentifierUri"}) + @Test + public void authorizedAcrValues( + final String userId, final String userSecret, final String redirectUris, final String redirectUri, + final String sectorIdentifierUri) { + showTitle("authorizedAcrValues"); + + List responseTypes = Arrays.asList(ResponseType.CODE, ResponseType.ID_TOKEN); + + // 1. Register client + RegisterRequest registerRequest = new RegisterRequest(ApplicationType.WEB, "jans test app", + StringUtils.spaceSeparatedToList(redirectUris)); + registerRequest.setResponseTypes(responseTypes); + registerRequest.setSectorIdentifierUri(sectorIdentifierUri); + registerRequest.setAuthorizedAcrValues(Arrays.asList( + "acr_1", "acr_2", "acr_3", "basic" + )); + + RegisterClient registerClient = newRegisterClient(registerRequest); + RegisterResponse registerResponse = registerClient.exec(); + + showClient(registerClient); + AssertBuilder.registerResponse(registerResponse) + .created() + .check(); + + String clientId = registerResponse.getClientId(); + + List scopes = Arrays.asList("openid", "profile", "address", "email"); + String state = UUID.randomUUID().toString(); + String nonce = UUID.randomUUID().toString(); + + AuthorizationRequest authorizationRequest = new AuthorizationRequest(responseTypes, clientId, scopes, redirectUri, nonce); + authorizationRequest.setState(state); + authorizationRequest.setAcrValues(Arrays.asList("basic")); + + AuthorizationResponse authorizationResponse = authenticateResourceOwnerAndGrantAccess(authorizationEndpoint, + authorizationRequest, userId, userSecret); + + AssertBuilder.authorizationResponse(authorizationResponse) + .responseTypes(responseTypes) + .check(); + } + + @Parameters({"userId", "userSecret", "redirectUris", "redirectUri", "sectorIdentifierUri"}) + @Test + public void authorizedAcrValuesFail( + final String userId, final String userSecret, final String redirectUris, final String redirectUri, + final String sectorIdentifierUri) { + showTitle("authorizedAcrValuesFail"); + + List responseTypes = Arrays.asList(ResponseType.CODE, ResponseType.ID_TOKEN); + + // 1. Register client + RegisterRequest registerRequest = new RegisterRequest(ApplicationType.WEB, "jans test app", + StringUtils.spaceSeparatedToList(redirectUris)); + registerRequest.setResponseTypes(responseTypes); + registerRequest.setSectorIdentifierUri(sectorIdentifierUri); + registerRequest.setAuthorizedAcrValues(Arrays.asList( + "acr_1", "acr_2", "acr_3" + )); + + RegisterClient registerClient = newRegisterClient(registerRequest); + RegisterResponse registerResponse = registerClient.exec(); + + showClient(registerClient); + AssertBuilder.registerResponse(registerResponse) + .created() + .check(); + + String clientId = registerResponse.getClientId(); + + List scopes = Arrays.asList("openid", "profile", "address", "email"); + String state = UUID.randomUUID().toString(); + String nonce = UUID.randomUUID().toString(); + + AuthorizationRequest authorizationRequest = new AuthorizationRequest(responseTypes, clientId, scopes, redirectUri, nonce); + authorizationRequest.setState(state); + authorizationRequest.setAcrValues(Arrays.asList("basic")); + + AuthorizeClient authorizeClient = new AuthorizeClient(authorizationEndpoint); + authorizeClient.setRequest(authorizationRequest); + + AuthorizationResponse authorizationResponse = authorizeClient.exec(); + + showClient(authorizeClient); + AssertBuilder.authorizationResponse(authorizationResponse) + .status(302) + .check(); + } +} diff --git a/jans-auth-server/model/src/main/java/io/jans/as/model/register/RegisterRequestParam.java b/jans-auth-server/model/src/main/java/io/jans/as/model/register/RegisterRequestParam.java index e415abcaaa4..55a1f4ddb57 100644 --- a/jans-auth-server/model/src/main/java/io/jans/as/model/register/RegisterRequestParam.java +++ b/jans-auth-server/model/src/main/java/io/jans/as/model/register/RegisterRequestParam.java @@ -13,7 +13,7 @@ * * @author Yuriy Zabrovarnyy * @author Javier Rojas Blum - * @version March 2, 2022 + * @version March 17, 2022 */ public enum RegisterRequestParam { @@ -351,7 +351,9 @@ public enum RegisterRequestParam { REDIRECT_URIS_REGEX("redirect_uris_regex"), - DEFAULT_PROMPT_LOGIN("default_prompt_login"); + DEFAULT_PROMPT_LOGIN("default_prompt_login"), + + AUTHORIZED_ACR_VALUES("authorized_acr_values"); /** * Parameter name diff --git a/jans-auth-server/persistence-model/src/main/java/io/jans/as/persistence/model/ClientAttributes.java b/jans-auth-server/persistence-model/src/main/java/io/jans/as/persistence/model/ClientAttributes.java index 73daf99e2b7..0f7ccf449fa 100644 --- a/jans-auth-server/persistence-model/src/main/java/io/jans/as/persistence/model/ClientAttributes.java +++ b/jans-auth-server/persistence-model/src/main/java/io/jans/as/persistence/model/ClientAttributes.java @@ -17,7 +17,7 @@ /** * @author Yuriy Zabrovarnyy * @author Javier Rojas Blum - * @version March 2, 2022 + * @version March 17, 2022 */ @JsonIgnoreProperties(ignoreUnknown = true) public class ClientAttributes implements Serializable { @@ -93,8 +93,11 @@ public class ClientAttributes implements Serializable { private String publicSubjectIdentifierAttribute; @JsonProperty("redirectUrisRegex") - private String redirectUrisRegex ; - + private String redirectUrisRegex; + + @JsonProperty("jansAuthorizedAcr") + private List authorizedAcrValues; + @JsonProperty("jansDefaultPromptLogin") private Boolean defaultPromptLogin = false; @@ -305,7 +308,18 @@ public String getRedirectUrisRegex() { public void setRedirectUrisRegex(String redirectUrisRegex) { this.redirectUrisRegex = redirectUrisRegex; - } + } + + public List getAuthorizedAcrValues() { + if (authorizedAcrValues == null) { + return Lists.newArrayList(); + } + return authorizedAcrValues; + } + + public void setAuthorizedAcrValues(List authorizedAcrValues) { + this.authorizedAcrValues = authorizedAcrValues; + } public Boolean getDefaultPromptLogin() { if (defaultPromptLogin == null) { 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 86f5598665e..23f7e2d8ead 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 @@ -99,7 +99,7 @@ * Implementation for request authorization through REST web services. * * @author Javier Rojas Blum - * @version March 15, 2022 + * @version March 17, 2022 */ @Path("/") public class AuthorizeRestWebServiceImpl implements AuthorizeRestWebService { @@ -313,11 +313,16 @@ private Response requestAuthorization( Client client = authorizeRestWebServiceValidator.validateClient(clientId, state, isPar); String deviceAuthzUserCode = deviceAuthorizationService.getUserCodeFromSession(httpRequest); redirectUri = authorizeRestWebServiceValidator.validateRedirectUri(client, redirectUri, state, deviceAuthzUserCode, httpRequest); - checkAcrChanged(acrValuesStr, prompts, sessionUser); // check after redirect uri is validated - RedirectUriResponse redirectUriResponse = new RedirectUriResponse(new RedirectUri(redirectUri, responseTypes, responseMode), state, httpRequest, errorResponseFactory); redirectUriResponse.setFapiCompatible(appConfiguration.isFapi()); + if (!client.getAttributes().getAuthorizedAcrValues().isEmpty() && + !client.getAttributes().getAuthorizedAcrValues().containsAll(acrValues)) { + throw redirectUriResponse.createWebException(AuthorizeErrorResponseType.INVALID_REQUEST, + "Restricted acr value request, please review the list of authorized acr values for this client"); + } + checkAcrChanged(acrValuesStr, prompts, sessionUser); // check after redirect uri is validated + Set scopes = scopeChecker.checkScopesPolicy(client, scope); if (Boolean.TRUE.equals(appConfiguration.getForceSignedRequestObject()) && StringUtils.isBlank(request) && StringUtils.isBlank(requestUri)) { diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/register/ws/rs/RegisterRestWebServiceImpl.java b/jans-auth-server/server/src/main/java/io/jans/as/server/register/ws/rs/RegisterRestWebServiceImpl.java index 303f7582e84..212a8d51bdc 100644 --- a/jans-auth-server/server/src/main/java/io/jans/as/server/register/ws/rs/RegisterRestWebServiceImpl.java +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/register/ws/rs/RegisterRestWebServiceImpl.java @@ -84,7 +84,7 @@ * @author Javier Rojas Blum * @author Yuriy Zabrovarnyy * @author Yuriy Movchan - * @version March 2, 2022 + * @version March 17, 2022 */ @Path("/") public class RegisterRestWebServiceImpl implements RegisterRestWebService { @@ -310,7 +310,7 @@ private Response registerClientImpl(String requestParams, HttpServletRequest htt builder.entity(jsonObjectToString(jsonObject)); - log.info("Client registered: clientId = {}, applicationType = {}, clientName = {}, redirectUris = {}, sectorIdentifierUri = {}, redirectUrisRegex = {}" , + log.info("Client registered: clientId = {}, applicationType = {}, clientName = {}, redirectUris = {}, sectorIdentifierUri = {}, redirectUrisRegex = {}", client.getClientId(), client.getApplicationType(), client.getClientName(), client.getRedirectUris(), client.getSectorIdentifierUri(), client.getAttributes().getRedirectUrisRegex()); oAuth2AuditLog.setClientId(client.getClientId()); @@ -853,6 +853,12 @@ private void updateClientFromRequestObject(Client client, RegisterRequest reques client.getAttributes().setDefaultPromptLogin(requestObject.getDefaultPromptLogin()); } + List authorizedAcrValues = requestObject.getAuthorizedAcrValues(); + if (authorizedAcrValues != null && !authorizedAcrValues.isEmpty()) { + authorizedAcrValues = new ArrayList<>(new HashSet<>(authorizedAcrValues)); // Remove repeated elements + client.getAttributes().setAuthorizedAcrValues(authorizedAcrValues); + } + cibaRegisterClientMetadataService.updateClient(client, requestObject.getBackchannelTokenDeliveryMode(), requestObject.getBackchannelClientNotificationEndpoint(), requestObject.getBackchannelAuthenticationRequestSigningAlg(), requestObject.getBackchannelUserCodeParameter());