Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(jans-auth-server): specify minimum acr for clients #343 #3083

Merged
merged 3 commits into from
Nov 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
tags:
- administration
- client
- configuration
---

# Client Configuration

## ACR client configuration

There are 4 client configuration properties related to ACR:

- `default_acr_values` - string array, default acr values which are set when `acr_values` is missed in authorization request.
- `minimumAcrLevel` - integer value which sets minimum acr level.
- `minimumAcrLevelAutoresolve` - boolean value, if `false` and `minimumAcrLevel` is higher then current `acr_values` then reject request. If `true` - resolve acr according to either client's `minimumAcrPriorityList` or AS `auth_level_mapping`
- `minimumAcrPriorityList` - string array, enables client to specify the acr order of preference, rather then just the next lowest integer value

AS process properties in following order:
1. if `acr_values` is absent, set `acr_values` from `default_acr_values`
2. Otherwise if present, checking minimum acr level:
- check `minimumAcrLevel`, if current acr level is higher or equals to `minimumAcrLevel` then proceed request processing without changes
- if `minimumAcrLevel` is less then current acr level and `minimumAcrLevelAutoresolve=false` -> reject request (return bad request error)
- if `minimumAcrLevel` is less then current acr level and `minimumAcrLevelAutoresolve=true` -> pickup value from `minimumAcrPriorityList` or if it's empty take nearest acr value that satisfy `minimumAcrLevel`

For example, given:
1. `minimumAcrLevel` = 14
1. `default_acr_values` = "basic"
1. `minimumAcrPriorityList` = ["u2f", "passkey", "usb_fido_key", "super_gluu"]
1. OP `auth_level_mapping` :
```
"auth_level_mapping": {
"1": ["basic"],
"5": ["otp"],
"10": ["u2f"],
"11": ["super_gluu"],
"20": ["passkey"],
"30": ["usb_fido_key"]
}
```

- if current `acr_values=u2f` and `minimumAcrLevelAutoresolve=false` -> request is rejected
- if current `acr_values=u2f` and `minimumAcrLevelAutoresolve=true` -> `acr_values` set to `acr_values=passkey` and request continue processing
- if current `acr_values=usb_fido_key` -> current acr is higher then minimum. Thus nothing to do.

If `minimumAcrPriorityList` is missing, then the AS can pick the next highest acr in the `auth_level_mapping`. In the example above, that would be `passkey`.
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ public class RegisterRequest extends BaseRequest {
private SignatureAlgorithm tokenEndpointAuthSigningAlg;
private Integer defaultMaxAge;
private List<String> defaultAcrValues;
private Integer minimumAcrLevel;
private Boolean minimumAcrLevelAutoresolve;
private List<String> minimumAcrPriorityList;
private String initiateLoginUri;
private List<String> groups;
private List<String> postLogoutRedirectUris;
Expand Down Expand Up @@ -154,6 +157,7 @@ public RegisterRequest() {
this.grantTypes = new ArrayList<>();
this.contacts = new ArrayList<>();
this.defaultAcrValues = new ArrayList<>();
this.minimumAcrPriorityList = new ArrayList<>();
this.postLogoutRedirectUris = new ArrayList<>();
this.groups = new ArrayList<>();
this.requestUris = new ArrayList<>();
Expand Down Expand Up @@ -1039,6 +1043,60 @@ public void setDefaultMaxAge(Integer defaultMaxAge) {
this.defaultMaxAge = defaultMaxAge;
}

/**
* Gets minimum acr level
*
* @return minimum acr level
*/
public Integer getMinimumAcrLevel() {
return minimumAcrLevel;
}

/**
* Sets minimum acr level
*
* @param minimumAcrLevel minimum acr level
*/
public void setMinimumAcrLevel(Integer minimumAcrLevel) {
this.minimumAcrLevel = minimumAcrLevel;
}

/**
* Gets minimum acr level auto resolve
*
* @return minimum acr level auto resolve
*/
public Boolean getMinimumAcrLevelAutoresolve() {
return minimumAcrLevelAutoresolve;
}

/**
* Sets minimum acr level auto resolve
*
* @param minimumAcrLevelAutoresolve minimum acr level auto resolve
*/
public void setMinimumAcrLevelAutoresolve(Boolean minimumAcrLevelAutoresolve) {
this.minimumAcrLevelAutoresolve = minimumAcrLevelAutoresolve;
}

/**
* Gets minimum acr priority list
*
* @return minimum acr priority list
*/
public List<String> getMinimumAcrPriorityList() {
return minimumAcrPriorityList;
}

/**
* Sets minimum acr priority list
*
* @param minimumAcrPriorityList minimum acr priority list
*/
public void setMinimumAcrPriorityList(List<String> minimumAcrPriorityList) {
this.minimumAcrPriorityList = minimumAcrPriorityList;
}

/**
* Returns the Default requested Authentication Context Class Reference values.
*
Expand Down Expand Up @@ -1379,6 +1437,9 @@ public static RegisterRequest fromJson(JSONObject requestObject) throws JSONExce
result.setPostLogoutRedirectUris(extractListByKey(requestObject, POST_LOGOUT_REDIRECT_URIS.toString()));
result.setGroups(extractListByKey(requestObject, GROUPS.toString()));
result.setDefaultAcrValues(extractListByKey(requestObject, DEFAULT_ACR_VALUES.toString()));
result.setMinimumAcrLevel(integerOrNull(requestObject, MINIMUM_ACR_LEVEL.toString()));
result.setMinimumAcrLevelAutoresolve(requestObject.optBoolean(MINIMUM_ACR_LEVEL_AUTORESOLVE.toString()));
result.setMinimumAcrPriorityList(extractListByKey(requestObject, MINIMUM_ACR_PRIORITY_LIST.toString()));
result.setFrontChannelLogoutUri(requestObject.optString(FRONT_CHANNEL_LOGOUT_URI.toString()));
result.setFrontChannelLogoutSessionRequired(requestObject.optBoolean(FRONT_CHANNEL_LOGOUT_SESSION_REQUIRED.toString()));
result.setBackchannelLogoutUris(extractListByKey(requestObject, BACKCHANNEL_LOGOUT_URI.toString()));
Expand Down Expand Up @@ -1588,6 +1649,15 @@ public void getParameters(BiFunction<String, Object, Void> function) {
if (defaultAcrValues != null && !defaultAcrValues.isEmpty()) {
function.apply(DEFAULT_ACR_VALUES.toString(), toJSONArray(defaultAcrValues));
}
if (minimumAcrLevel != null) {
function.apply(MINIMUM_ACR_LEVEL.toString(), minimumAcrLevel.toString());
}
if (minimumAcrLevelAutoresolve != null) {
function.apply(MINIMUM_ACR_LEVEL_AUTORESOLVE.toString(), minimumAcrLevelAutoresolve.toString());
}
if (minimumAcrPriorityList != null) {
function.apply(MINIMUM_ACR_PRIORITY_LIST.toString(), toJSONArray(minimumAcrPriorityList));
}
if (StringUtils.isNotBlank(initiateLoginUri)) {
function.apply(INITIATE_LOGIN_URI.toString(), initiateLoginUri);
}
Expand Down
3 changes: 2 additions & 1 deletion jans-auth-server/common/src/test/resources/testng.xml
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >

<suite name="oxAuthCommon" parallel="false">
<test name="Audience Test" enabled="true">
<test name="Unit Tests" enabled="true">
<classes>
<class name="io.jans.as.common.AudienceTest"/>
<class name="io.jans.as.common.util.RedirectUriTest"/>
<class name="io.jans.as.common.model.registration.ClientSerializationTest"/>
<class name="io.jans.as.common.service.common.UserServiceTest"/>
<class name="io.jans.as.common.service.common.InumServiceTest"/>
</classes>
Expand Down
38 changes: 37 additions & 1 deletion jans-auth-server/docs/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1274,6 +1274,18 @@ paths:
processing requests from the Client.
items:
type: string
minimum_acr_level:
type: integer
description: Integer value which sets minimum acr level.
example: 10
minimum_acr_level_autoresolve:
type: boolean
description: boolean value, if false and minimum_acr_level is higher then current acr_values then reject request. If true - resolve acr according to either client's minimum_acr_priority_list or AS auth_level_mapping
minimum_acr_priority_list:
type: array
description: enables client to specify the acr order of preference, rather then just the next lowest integer value
items:
type: string
groups:
type: array
description: Array of client's groups.
Expand Down Expand Up @@ -1614,6 +1626,18 @@ paths:
processing requests from the Client.
items:
type: string
minimum_acr_level:
type: integer
description: Integer value which sets minimum acr level.
example: 10
minimum_acr_level_autoresolve:
type: boolean
description: boolean value, if false and minimum_acr_level is higher then current acr_values then reject request. If true - resolve acr according to either client's minimum_acr_priority_list or AS auth_level_mapping
minimum_acr_priority_list:
type: array
description: enables client to specify the acr order of preference, rather then just the next lowest integer value
items:
type: string
initiate_login_uri:
type: string
description: Specifies the URI using the https scheme that the authorization server can call to initiate a login at the client.
Expand Down Expand Up @@ -1956,6 +1980,18 @@ paths:
processing requests from the Client.
items:
type: string
minimum_acr_level:
type: integer
description: Integer value which sets minimum acr level.
example: 10
minimum_acr_level_autoresolve:
type: boolean
description: boolean value, if false and minimum_acr_level is higher then current acr_values then reject request. If true - resolve acr according to either client's minimum_acr_priority_list or AS auth_level_mapping
minimum_acr_priority_list:
type: array
description: enables client to specify the acr order of preference, rather then just the next lowest integer value
items:
type: string
initiate_login_uri:
type: string
description: Specifies the URI using the https scheme that the authorization server can call to initiate a login at the client.
Expand Down Expand Up @@ -4282,7 +4318,7 @@ paths:
- SSA
summary: Create SSA.
description: Create `SSA` for the organization with `expiration` (optional).
operationId: post-register
operationId: post-register-ssa
security:
- bearer: [ ]
requestBody:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,24 @@ public enum RegisterRequestParam {
*/
DEFAULT_ACR_VALUES("default_acr_values"),

/**
* Integer value which sets minimum acr level.
*/
MINIMUM_ACR_LEVEL("minimum_acr_level"),

/**
* Boolean value,
* - if false and minimumAcrLevel is higher then current acr_values then reject request
* - if true - resolve acr according to either client's minimumAcrPriorityList or AS auth_level_mapping
*/
MINIMUM_ACR_LEVEL_AUTORESOLVE("minimum_acr_level_autoresolve"),

/**
* Array of strings,
* - enables client to specify the acr order of preference, rather then just the next lowest integer value
*/
MINIMUM_ACR_PRIORITY_LIST("minimum_acr_priority_list"),

/**
* URI using the https scheme that the Authorization Server can call to initiate a login at the Client.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,41 @@ public class ClientAttributes implements Serializable {
@JsonProperty("allowOfflineAccessWithoutConsent")
private Boolean allowOfflineAccessWithoutConsent;

@JsonProperty("minimumAcrLevel")
private Integer minimumAcrLevel = -1;

@JsonProperty("minimumAcrLevelAutoresolve")
private Boolean minimumAcrLevelAutoresolve;

@JsonProperty("minimumAcrPriorityList")
private List<String> minimumAcrPriorityList;

public Boolean getMinimumAcrLevelAutoresolve() {
return minimumAcrLevelAutoresolve;
}

public void setMinimumAcrLevelAutoresolve(Boolean minimumAcrLevelAutoresolve) {
this.minimumAcrLevelAutoresolve = minimumAcrLevelAutoresolve;
}

public List<String> getMinimumAcrPriorityList() {
if (minimumAcrPriorityList == null) minimumAcrPriorityList = new ArrayList<>();
return minimumAcrPriorityList;
}

public void setMinimumAcrPriorityList(List<String> minimumAcrPriorityList) {
this.minimumAcrPriorityList = minimumAcrPriorityList;
}

public Integer getMinimumAcrLevel() {
if (minimumAcrLevel == null) minimumAcrLevel = -1;
return minimumAcrLevel;
}

public void setMinimumAcrLevel(Integer minimumAcrLevel) {
this.minimumAcrLevel = minimumAcrLevel;
}

public Boolean getAllowOfflineAccessWithoutConsent() {
return allowOfflineAccessWithoutConsent;
}
Expand Down Expand Up @@ -378,6 +413,9 @@ public String toString() {
", publicSubjectIdentifierAttribute=" + publicSubjectIdentifierAttribute +
", redirectUrisRegex=" + redirectUrisRegex +
", allowOfflineAccessWithoutConsent=" + allowOfflineAccessWithoutConsent +
", minimumAcrLevel=" + minimumAcrLevel +
", minimumAcrLevelAutoresolve=" + minimumAcrLevelAutoresolve +
", minimumAcrPriorityList=" + minimumAcrPriorityList +
", defaultPromptLogin=" + defaultPromptLogin +
'}';
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ private ResponseBuilder authorize(AuthzRequest authzRequest) throws AcrChangedEx
authorizeRestWebServiceValidator.validate(authzRequest, responseTypes, client);
authorizeRestWebServiceValidator.validatePkce(authzRequest.getCodeChallenge(), authzRequest.getRedirectUriResponse());

authzRequestService.setDefaultAcrsIfNeeded(authzRequest, client);
authzRequestService.setAcrsIfNeeded(authzRequest);

checkOfflineAccessScopes(responseTypes, prompts, client, scopes);
checkResponseType(authzRequest, responseTypes, client);
Expand Down
Loading