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): support for OAuth 2.0 Rich Authorization Requests #7145

Merged
merged 15 commits into from
Dec 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
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
1 change: 1 addition & 0 deletions docs/admin/auth-server/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ FAPI-CIBA OpenID Providers for the latest results.
* [RFC 9126 OAuth 2.0 Pushed Authorization Requests](https://www.rfc-editor.org/rfc/rfc9126.html)
* [Draft - OAuth 2.0 Demonstrating Proof-of-Possession at the Application Layer (DPoP)](https://www.ietf.org/archive/id/draft-ietf-oauth-dpop-11.html)
* [Draft - JWT Response for OAuth Token Introspection](https://www.ietf.org/archive/id/draft-ietf-oauth-jwt-introspection-response-12.html)
* [OAuth 2.0 Rich Authorization Requests](https://datatracker.ietf.org/doc/html/rfc9396)

** User Managed Access (UMA) **

Expand Down
181 changes: 181 additions & 0 deletions docs/admin/auth-server/authz-details/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
---
tags:
- administration
- auth-server
- scope
- authorization-details
---

## OAuth 2.0 Rich Authorization Requests

Rich Authorization Requests introduces new `authorization_details` parameter that is used to carry fine-grained authorization data in OAuth messages.

While `scope` is used for coarse-grained access, `authorization_details` is used for fine-grained access.

`authorization_details` are associated with authorization and thus with client to limit what authorization can be granted within given client.

`authorization_details` is JSON array, example:

```json
[
{
"type": "demo_authz_detail",
"actions": [
"list_accounts",
"read_balances"
],
"locations": [
"https://example.com/accounts"
],
"ui_representation": "Read balances and list accounts at https://example.com/accounts"
},
{
"type":"financial-transaction",
"actions":[
"withdraw"
],
"identifier":"account-14-32-32-3",
"currency":"USD"
}
]
```

### Authorization Details Types

`type` - is required element in single authorization detail and specifies the authorization details type as a string.
Type defines how single authorization detail is handled by both AS and RS.
Because "shape" and structure of single authorization detail can vary a lot, validation and representation logic is externalized to `AuthzDetailType` custom scripts.

`type` defines type of authorization detail. Each such type is represented by AS `AuthzDetailType` custom scripts.
It means that for example above administrator must define two `AuthzDetailType` custom scripts with names: `demo_authz_detail` and `financial-transaction`.

If `authorization_details` parameter is absent in request then `AuthzDetailType` custom scripts are not invoked.

`demo_authz_detail` and `financial-transaction` `AuthzDetailType` custom scripts must be provided by administrator.

- `demo_authz_detail` is called for all authorization details with `"type": "demo_authz_detail"`
- `financial-transaction` is called for all authorization details with `"type": "financial-transaction"`

Sample Authorization Request
```
POST /jans-auth/restv1/authorize HTTP/1.1
Host: yuriyz-fond-skink.gluu.info

response_type=code&client_id=7a29bf35-96ec-4bbd-a05c-15e1ff9f07cc&scope=openid+profile+address+email+phone+user_name&redirect_uri=https%3A%2F%2Fyuriyz-relaxed-jawfish.gluu.info%2Fjans-auth-rp%2Fhome.htm&state=6cdc7701-178c-4653-adac-5c1e9c6c4aba&nonce=b9a1ecc4-548e-475c-8b29-f019417e1aef&prompt=&ui_locales=&claims_locales=&acr_values=&request_session_id=false&authorization_details=%5B%0A++%7B%0A++++%22type%22%3A+%22demo_authz_detail%22%2C%0A++++%22actions%22%3A+%5B%0A++++++%22list_accounts%22%2C%0A++++++%22read_balances%22%0A++++%5D%2C%0A++++%22locations%22%3A+%5B%0A++++++%22https%3A%2F%2Fexample.com%2Faccounts%22%0A++++%5D%2C%0A++++%22ui_representation%22%3A+%22Read+balances+and+list+accounts+at+https%3A%2F%2Fexample.com%2Faccounts%22%0A++%7D%0A%5D
```

Request is rejected if request's `authorization_details` has types which does not have corresponding `AuthzDetailType` custom script.

Check more details about [`AuthzDetailType` custom scripts](../../developer/scripts/authz-detail.md)

### AS Metadata (Discovery)

Metadata endpoint has `authorization_details_types_supported` which shows supported authorization details types.
Value for `authorization_details_types_supported` is populated based on valid and enabled `AuthzDetailType` insterception scripts.

For `demo_authz_detail` and `financial-transaction` `AuthzDetailType` custom scripts enabled discovery response has:

```text
{
"authorization_details_types_supported" : [ "demo_authz_detail", "financial-transaction" ],
...
}
```

### Client Registration

Client registration request has new parameter `authorization_details_types` to limit authorization details types supported by client.
If request is made with `authorization_details` that has types that are not listed in client's `authorization_details_types` the request will be rejected.

Sample registration request and response
```text
-------------------------------------------------------
REQUEST:
-------------------------------------------------------
POST /jans-auth/restv1/register HTTP/1.1
Host: yuriyz-relaxed-jawfish.gluu.info
Content-Type: application/json
Accept: application/json

{
"grant_types" : [ "authorization_code", "implicit" ],
"subject_type" : "public",
"application_type" : "web",
"authorization_details_types" : [ "demo_authz_detail" ],
"scope" : "openid profile address email phone user_name",
"minimum_acr_priority_list" : [ ],
"redirect_uris" : [ "https://yuriyz-relaxed-jawfish.gluu.info/jans-auth-rp/home.htm", "https://client.example.com/cb", "https://client.example.com/cb1", "https://client.example.com/cb2" ],
"client_name" : "jans test app",
"additional_audience" : [ ],
"response_types" : [ "code" ]
}

-------------------------------------------------------
RESPONSE:
-------------------------------------------------------
HTTP/1.1 201
Cache-Control: no-store
Connection: Keep-Alive
Content-Length: 1653
Content-Type: application/json
Date: Mon, 18 Dec 2023 17:59:13 GMT
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Keep-Alive: timeout=5, max=100
Pragma: no-cache
Set-Cookie: X-Correlation-Id=7b231c8f-5b2e-445d-b5ea-0f693c1cd7f2; Secure; HttpOnly;HttpOnly
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
X-Xss-Protection: 1; mode=block

{
"allow_spontaneous_scopes": false,
"application_type": "web",
"rpt_as_jwt": false,
"registration_client_uri": "https://yuriyz-relaxed-jawfish.gluu.info/jans-auth/restv1/register?client_id=7a29bf35-96ec-4bbd-a05c-15e1ff9f07cc",
"tls_client_auth_subject_dn": "",
"run_introspection_script_before_jwt_creation": false,
"registration_access_token": "92a40113-b27c-43b9-bf96-a222fcfe1c9c",
"client_id": "7a29bf35-96ec-4bbd-a05c-15e1ff9f07cc",
"token_endpoint_auth_method": "client_secret_basic",
"scope": "openid",
"client_secret": "1af17da1-57a3-416b-a358-c84bb0ef0fad",
"client_id_issued_at": 1702922353,
"backchannel_logout_uri": [],
"backchannel_logout_session_required": false,
"client_name": "jans test app",
"par_lifetime": 600,
"spontaneous_scopes": [],
"id_token_signed_response_alg": "RS256",
"access_token_as_jwt": false,
"grant_types": [
"authorization_code",
"implicit"
],
"subject_type": "public",
"authorization_details_types": ["demo_authz_detail"],
"additional_token_endpoint_auth_methods": [],
"keep_client_authorization_after_expiration": false,
"require_par": false,
"redirect_uris": [
"https://client.example.com/cb2",
"https://client.example.com/cb1",
"https://yuriyz-relaxed-jawfish.gluu.info/jans-auth-rp/home.htm",
"https://client.example.com/cb"
],
"redirect_uris_regex": "",
"additional_audience": [],
"frontchannel_logout_session_required": false,
"client_secret_expires_at": 0,
"access_token_signing_alg": "RS256",
"response_types": ["code"]
}

```








1 change: 1 addition & 0 deletions docs/admin/developer/interception-scripts.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ calling external APIs
1. [Introspection](./scripts/introspection.md) : Introspection scripts allows to modify response of Introspection Endpoint spec and present additional meta information surrounding the token.
1. [Post Authentication](./scripts/post-authentication.md)
1. [Authorization Challenge](./scripts/authorization-challenge.md)
1. [Authz Detail](./scripts/authz-detail.md)
1. [Select Account](./scripts/select-account.md)
1. Resource Owner Password Credentials
1. UMA 2 RPT Authorization Policies
Expand Down
185 changes: 185 additions & 0 deletions docs/admin/developer/scripts/authz-detail.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
---
tags:
- administration
- developer
- scripts
---

# Authorization Detail Custom Script (AuthzDetail)

## Overview

The Jans-Auth server implements [OAuth 2.0 Rich Authorization Requests](https://datatracker.ietf.org/doc/html/rfc9396).
This script is used to control/customize single authorization detail from `authorization_details` array.

## Behavior

In request to Authorization Endpoint and to Token Endpoint RP can specify `authorization_details` request parameter which specifies JSON array.

```json
[
{
"type": "demo_authz_detail",
"actions": [
"list_accounts",
"read_balances"
],
"locations": [
"https://example.com/accounts"
],
"ui_representation": "Read balances and list accounts at https://example.com/accounts"
},
{
"type":"financial-transaction",
"actions":[
"withdraw"
],
"identifier":"account-14-32-32-3",
"currency":"USD",
"ui_representation": "Withdraw money from account-14-32-32-3"
}
]
```

`type` defines type of authorization detail. Each such type is represented by AS `AuthzDetailType` custom scripts.
It means that for example above administrator must define two `AuthzDetailType` custom scripts with names: `demo_authz_detail` and `financial-transaction`.

If `authorization_details` parameter is absent in request then `AuthzDetailType` custom scripts are not invoked.

`demo_authz_detail` and `financial-transaction` `AuthzDetailType` custom scripts must be provided by administrator.

- `demo_authz_detail` is called for all authorization details with `"type": "demo_authz_detail"`
- `financial-transaction` is called for all authorization details with `"type": "financial-transaction"`

Sample Authorization Request
```
POST /jans-auth/restv1/authorize HTTP/1.1
Host: yuriyz-fond-skink.gluu.info

response_type=code&client_id=7a29bf35-96ec-4bbd-a05c-15e1ff9f07cc&scope=openid+profile+address+email+phone+user_name&redirect_uri=https%3A%2F%2Fyuriyz-relaxed-jawfish.gluu.info%2Fjans-auth-rp%2Fhome.htm&state=6cdc7701-178c-4653-adac-5c1e9c6c4aba&nonce=b9a1ecc4-548e-475c-8b29-f019417e1aef&prompt=&ui_locales=&claims_locales=&acr_values=&request_session_id=false&authorization_details=%5B%0A++%7B%0A++++%22type%22%3A+%22demo_authz_detail%22%2C%0A++++%22actions%22%3A+%5B%0A++++++%22list_accounts%22%2C%0A++++++%22read_balances%22%0A++++%5D%2C%0A++++%22locations%22%3A+%5B%0A++++++%22https%3A%2F%2Fexample.com%2Faccounts%22%0A++++%5D%2C%0A++++%22ui_representation%22%3A+%22Read+balances+and+list+accounts+at+https%3A%2F%2Fexample.com%2Faccounts%22%0A++%7D%0A%5D
```

## Interface
The Authorization Details script implements the [AuthzDetailType](https://github.com/JanssenProject/jans/blob/main/jans-core/script/src/main/java/io/jans/model/custom/script/type/authzdetails/AuthzDetailType.java) interface. This extends methods from the base script type in addition to adding new methods:

### Inherited Methods
| Method header | Method description |
|:-----|:------|
| `def init(self, customScript, configurationAttributes)` | This method is only called once during the script initialization. It can be used for global script initialization, initiate objects etc |
| `def destroy(self, configurationAttributes)` | This method is called once to destroy events. It can be used to free resource and objects created in the `init()` method |
| `def getApiVersion(self, configurationAttributes, customScript)` | The getApiVersion method allows API changes in order to do transparent migration from an old script to a new API. Only include the customScript variable if the value for getApiVersion is greater than 10 |

### New methods
| Method header | Method description |
|:-----|:------|
|`def validateDetail(self, context)`| Called when the request is received. Method validates single authorization detail from `authorization_details`. |
|`def getUiRepresentation(self, context)`| Called when single authorization detail from `authorization_details` has to be represented on UI as string. For example on authorization page. |

`validateDetail` method returns true/false which indicates to server whether the validation of single authorization detail (from `authorization_details` array) is passed or failed.
If at least one element from `authorization_details` array fails validation error is returned by AS.

`getUiRepresentation` method returns string and represents single authorization detail as string on UI. Authorization detail can have "ui_representation" json key which makes implementation as simple as following:

```java
@Override
public String getUiRepresentation(Object scriptContext) {
ExternalScriptContext context = (ExternalScriptContext) scriptContext;
return context.getAuthzDetail().getJsonObject().optString("ui_representation");
}
```


### Objects
| Object name | Object description |
|:-----|:------|
|`customScript`| The custom script object. [Reference](https://github.com/JanssenProject/jans/blob/main/jans-core/script/src/main/java/io/jans/model/custom/script/model/CustomScript.java) |
|`context`| [Reference](https://github.com/JanssenProject/jans/blob/main/jans-auth-server/server/src/main/java/io/jans/as/server/service/external/context/ExternalScriptContext.java) |

- Get Authz Detail - `context.getAuthzDetail()`
- Get Authz Detail Type - `context.getAuthzDetail().getType()`
- Get Authz Detail JSON Object (`org.json.JSONObject`) for manipulation - `context.getAuthzDetail().getJsonObject()`
- Get full HTTP Request - `context.getHttpRequest()`


## Simple Use Case: validate authz details is present and return string representation

### Script Type: Java

```java
/*
Copyright (c) 2023, Gluu
Author: Yuriy Z
*/

import io.jans.as.server.service.external.context.ExternalScriptContext;
import io.jans.model.SimpleCustomProperty;
import io.jans.model.custom.script.model.CustomScript;
import io.jans.model.custom.script.type.authzdetails.AuthzDetailType;
import io.jans.service.custom.script.CustomScriptManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;

/**
* @author Yuriy Z
*/
public class AuthzDetail implements AuthzDetailType {

private static final Logger log = LoggerFactory.getLogger(AuthzDetail.class);
private static final Logger scriptLogger = LoggerFactory.getLogger(CustomScriptManager.class);

/**
* All validation logic of single authorization detail must take place in this method.
* If method returns "false" AS returns error to RP. If "true" processing of request goes on.
*
* @param scriptContext script context. Authz detail can be taken as "context.getAuthzDetail()".
* @return whether single authorization detail is valid or not
*/
@Override
public boolean validateDetail(Object scriptContext) {
ExternalScriptContext context = (ExternalScriptContext) scriptContext;
return context.getAuthzDetail() != null;
}

/**
* Method returns single authorization detail string representation which is shown on authorization page by AS.
*
* @param scriptContext script context. Authz detail can be taken as "context.getAuthzDetail()".
* @return returns single authorization details string representation which is shown on authorization page by AS.
*/
@Override
public String getUiRepresentation(Object scriptContext) {
ExternalScriptContext context = (ExternalScriptContext) scriptContext;
return context.getAuthzDetail().getJsonObject().optString("ui_representation");
}

@Override
public boolean init(Map<String, SimpleCustomProperty> configurationAttributes) {
scriptLogger.info("Initialized AuthzDetail Java custom script.");
return true;
}

@Override
public boolean init(CustomScript customScript, Map<String, SimpleCustomProperty> configurationAttributes) {
scriptLogger.info("Initialized AuthzDetail Java custom script.");
return true;
}

@Override
public boolean destroy(Map<String, SimpleCustomProperty> configurationAttributes) {
scriptLogger.info("Destroyed AuthzDetail Java custom script.");
return true;
}

@Override
public int getApiVersion() {
return 11;
}
}

```


## Sample Scripts
- [AuthzDetails](../../../script-catalog/authz_detail/AuthzDetail.java)
Loading