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): draft for - improve dcr / ssa validation for dynamic registration #2980 #3109

Merged
merged 3 commits into from
Dec 1, 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
124 changes: 124 additions & 0 deletions docs/admin/auth-server/endpoints/client-registration.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Dynamic client registration refers to the process by which a client submits a re
1. For OpenID Connect relying parties - [OpenID Connect Dynamic Client Registration 1.0](https://openid.net/specs/openid-connect-registration-1_0.html).
1. For OAuth 2.0 client (without OpenID Connect features) - [OAuth 2.0 Dynamic Client Registration Protocol - RFC 7591](https://tools.ietf.org/html/rfc7591).
1. CRUD operations on client - [OAuth 2.0 Dynamic Client Registration Management Protocol - RFC 7592](https://tools.ietf.org/html/rfc7592).
1. [OpenBanking OpenID Dynamic Client Registration](https://openbanking.atlassian.net/wiki/spaces/DZ/pages/36667724/OpenBanking+OpenID+Dynamic+Client+Registration+Specification+-+v1.0.0-rc2#OpenBankingOpenIDDynamicClientRegistrationSpecification-v1.0.0-rc2-ClientRegistrationRequest)

### Client Registration endpoint
The URI to dynamically register a client to a Janssen Auth Server can be found by checking the `registration_endpoint` claim of the OpenID Connect configuration reponse, typically deployed at `https://<my.jans.server>/.well-known/openid-configuration`
Expand Down Expand Up @@ -296,6 +297,129 @@ Output:
"description": "string"
}
```

### Signed DCR and SSA validation

In OpenBanking case DCR (Dynamic Client Request) is signed and must contain SSA (Software Statement Assertion) inside it.

Non-Normative Example:
```curl
POST /register HTTP/1.1
Content-Type: application/jwt
Accept: application/json
Host: auth.bankone.com
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJJREFtYX...
```

Decoded DCR Example:

```json
{
"typ": "JWT",
"alg": "ES256",
"kid": "ABCD1234"
}
{
"iss": "Amazon TPPID",
"iat": 1492760444,
"exp": 1524296444,
"aud": "https://authn.gluu.org",
"scope": "openid makepayment",
"token_endpoint_auth_method": "private_key_jwt",
"grant_types": ["authorization_code", "refresh_token", "client_credentials"],
"response_types": ["code"],
"id_token_signed_response_alg": "ES256",
"request_object_signing_alg": "ES256",
"software_id": "65d1f27c-4aea-4549-9c21-60e495a7a86f",
"software_statement": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJlbXB0eSIsInN1YiI6IjEyMzQ1Njc4OTAiLCJuYW1lIjoiSm9obiBEb2UiLCJhZG1pbiI6dHJ1ZSwic2NvcGUiOiJzY29wZTEgc2NvcGUyIiwiY2xhaW1zIjoiY2xhaW0xIGNsYWltMiIsImlhdCI6MTY2OTgwNjc2MywiZXhwIjoxNjY5ODEwMzYzfQ.db0WQh2lmHkNYCWT8tSW684hqWTPJDTElppy42XM_lc"
}
{
Signature
}
```

AS has `dcrSsaValidationConfigs` configuration value which holds json array. It can be used to validated both DCR and SSA.

Single item of this array has following properties:

* **id** - REQUIRED primary key for the entity
* **type** - REQUIRED either `ssa` or `dcr`
* **displayName** - Human friendly name in case we build an admin GUI for this
* **description** - Human friendly details
* **scope** - For SSA only -- list of allowed scopes the issuer can enable the client to request automatically. If not present, all scopes are allowed.
* **allowed_claims** - Any claims not listed in this list will be dropped. If not present, all claims are allowed.
* **jwks** - Public key
* **jwks_uri** - URI of public key
* **issuers** - For MTLS, list of issuers trusted
* **configuration_endpoint** - points to discovery page, e.g. `https://examle.com/.well-known/openid-configuration`
* **configuration_endpoint_claim** - e.g. `ssa_jwks_endpoint`
* **shared_secret** - for MTLS HMAC

One of `jwks`, `jwks_uri` or `issuers` or `configuration_endpoint` is required.

Non-normative example of `dcrSsaValidationConfigs`

```json
[ {
"id" : "735ee1c0-895d-4398-9c1b-9ad852257cc0",
"type" : "DCR",
"scopes" : [ "read", "write" ],
"allowedClaims" : [ "exp", "iat" ],
"jwks" : "{jwks here}",
"issuers" : [ "Acme" ],
"sharedSecret" : "secret"
}, {
"id" : "7907cd0b-0f9f-4b4f-aaa2-0d0614546246",
"type" : "SSA",
"scopes" : [ "my_read", "my_write" ],
"allowedClaims" : [ "test_exp", "test_iat" ],
"jwks" : "{jwks here}",
"issuers" : [ "jans-auth" ],
"sharedSecret" : "secret"
}, {
"id" : "1e95c9b1-04d0-4440-9362-88f4e1e62d76",
"type" : "SSA",
"jwks" : "{jwks here}",
"issuers" : [ "empty" ],
"sharedSecret" : "secret"
} ]
```

#### Signed DCR validation

DCR can be validated with two approaches.

**Via `dcrSsaValidationConfigs` configuration property - RECOMMENDED**

When create entry in `dcrSsaValidationConfigs` configuration property :
- `type` MUST be equal to `DCR` value
- `issuers` MUST have value which equals to `iss` claim in DCR.

**Via other configuration properties**

* **dcrSignatureValidationJwks** - specifies JWKS for DCR's validations
* **dcrSignatureValidationJwksUri** - specifies JWKS URI for DCR's validations
* **dcrIssuers** - List of issues if MTLS private key is used to sign DCR JWT
* **dcrSignatureValidationSharedSecret** - if HMAC is used, this is the shared secret
* **dcrSignatureValidationEnabled** - boolean value enables DCR signature validation. Default is false
* **dcrSignatureValidationSoftwareStatementJwksURIClaim** - specifies claim name inside software statement. Value of claim should point to JWKS URI
* **dcrSignatureValidationSoftwareStatementJwksClaim** - specifies claim name inside software statement. Value of claim should point to inlined JWKS
* **dcrAuthorizationWithClientCredentials** - boolean value indicating if DCR authorization to be performed using client credentials

#### SSA Validation

SSA is validated based on `softwareStatementValidationType` which is enum.

1. **softwareStatementValidationType**=*builtin* - validation is performed against `dcrSsaValidationConfigs` configuration property, where
1. `type` MUST be equal to `SSA` value
1. `issuers` MUST have value which equals to `iss` claim in DCR.
1. **softwareStatementValidationType**=*script* - jwks and hmac secret are returned by dynamic client registration script
1. **softwareStatementValidationType**=*jwks_uri*, allows to specify jwks_uri claim name from software_statement. Claim name specified by `softwareStatementValidationClaimName` configuration property.
1. **softwareStatementValidationType**=*jwks*, allows to specify jwks claim name from software_statement. Claim name specified by `softwareStatementValidationClaimName` configuration property.
1. **softwareStatementValidationType**=*none*, no validation.


### Customizing the behavior of the AS using Interception script
Janssen's allows developers to register a client with the Authorization Server (AS) without any intervention by the administrator. By default, all clients are given the same default scopes and attributes. Through the use of an interception script, this behavior can be modified. These scripts can be used to analyze the registration request and apply customizations to the registered client. For example, a client can be given specific scopes by analyzing the [Software Statement](https://www.rfc-editor.org/rfc/rfc7591.html#section-2.3) that is sent with the registration request.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
*/
public enum SoftwareStatementValidationType {
NONE("none"),
BUILTIN("builtin"),
JWKS("jwks"),
JWKS_URI("jwks_uri"),
SCRIPT("script");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.google.common.collect.Lists;

import io.jans.agama.model.EngineConfig;
import io.jans.as.model.common.*;
import io.jans.as.model.error.ErrorHandlingMethod;
import io.jans.as.model.jwk.KeySelectionStrategy;
import io.jans.as.model.ssa.SsaConfiguration;
import io.jans.as.model.ssa.SsaValidationConfig;
import io.jans.doc.annotation.DocProperty;

import java.util.ArrayList;
Expand Down Expand Up @@ -803,9 +803,17 @@ public class AppConfiguration implements Configuration {
@DocProperty(description = "Engine Config which offers an alternative way to build authentication flows in Janssen server")
private EngineConfig agamaConfiguration;

@DocProperty(description = "")
@DocProperty(description = "DCR SSA Validation configurations used to perform validation of SSA or DCR")
private List<SsaValidationConfig> dcrSsaValidationConfigs;

@DocProperty(description = "SSA Configuration")
private SsaConfiguration ssaConfiguration;

public List<SsaValidationConfig> getDcrSsaValidationConfigs() {
if (dcrSsaValidationConfigs == null) dcrSsaValidationConfigs = new ArrayList<>();
return dcrSsaValidationConfigs;
}

public Boolean getRequireRequestObjectEncryption() {
if (requireRequestObjectEncryption == null) requireRequestObjectEncryption = false;
return requireRequestObjectEncryption;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package io.jans.as.model.ssa;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

import java.util.ArrayList;
import java.util.List;

/**
* @author Yuriy Z
*/
@JsonIgnoreProperties(
ignoreUnknown = true
)
public class SsaValidationConfig {

private String id;
private SsaValidationType type;
private String displayName;
private String description;
private List<String> scopes;
private List<String> allowedClaims;
private String jwks;
private String jwksUri;
private List<String> issuers;
private String configurationEndpoint;
private String configurationEndpointClaim;
private String sharedSecret;

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

public SsaValidationType getType() {
return type;
}

public void setType(SsaValidationType type) {
this.type = type;
}

public String getDisplayName() {
return displayName;
}

public void setDisplayName(String displayName) {
this.displayName = displayName;
}

public String getDescription() {
return description;
}

public void setDescription(String description) {
this.description = description;
}

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

public void setScopes(List<String> scopes) {
this.scopes = scopes;
}

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

public void setAllowedClaims(List<String> allowedClaims) {
this.allowedClaims = allowedClaims;
}

public String getJwks() {
return jwks;
}

public void setJwks(String jwks) {
this.jwks = jwks;
}

public String getJwksUri() {
return jwksUri;
}

public void setJwksUri(String jwksUri) {
this.jwksUri = jwksUri;
}

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

public void setIssuers(List<String> issuers) {
this.issuers = issuers;
}

public String getConfigurationEndpoint() {
return configurationEndpoint;
}

public void setConfigurationEndpoint(String configurationEndpoint) {
this.configurationEndpoint = configurationEndpoint;
}

public String getConfigurationEndpointClaim() {
return configurationEndpointClaim;
}

public void setConfigurationEndpointClaim(String configurationEndpointClaim) {
this.configurationEndpointClaim = configurationEndpointClaim;
}

public String getSharedSecret() {
return sharedSecret;
}

public void setSharedSecret(String sharedSecret) {
this.sharedSecret = sharedSecret;
}

@Override
public String toString() {
return "SsaValidationConfig{" +
"id='" + id + '\'' +
", type=" + type +
", displayName='" + displayName + '\'' +
", description='" + description + '\'' +
", scopes=" + scopes +
", allowedClaims=" + allowedClaims +
", jwks='" + jwks + '\'' +
", jwksUri='" + jwksUri + '\'' +
", issuers=" + issuers +
", configurationEndpoint='" + configurationEndpoint + '\'' +
", configurationEndpointClaim='" + configurationEndpointClaim + '\'' +
", sharedSecret='" + sharedSecret + '\'' +
'}';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package io.jans.as.model.ssa;

/**
* @author Yuriy Z
*/
public enum SsaValidationType {
NONE("none"),
SSA("ssa"),
DCR("dcr");

private final String value;

SsaValidationType(String value) {
this.value = value;
}

public String getValue() {
return value;
}

public static SsaValidationType of(String value) {
for (SsaValidationType t : values()) {
if (t.value.equalsIgnoreCase(value)) {
return t;
}
}
return NONE;
}
}
Loading