AM 7.2.0

UMA grant flow

The UMA grant flow issues an RPT to the requesting party to allow access to a resource.

The implementation in AM covers the following scenarios:

  • The requesting party wants to perform an action over a resource (for example, downloading it), and the resource owner has already granted them that permission.

    During the UMA grant flow, AM issues the requesting party an RPT that they can present to the resource server to access the resource.

  • The requesting party wants to perform an action over a resource (for example, downloading it), and the resource owner has not granted them that permission.

    During the UMA grant flow, AM denies access to the resource, and sends the resource owner a request for permission on behalf of the requesting party.

    If the resource owner grants their permission (which happens asynchronously), the requesting party requests a new permission ticket and applies for another RPT.

This diagram shows the first scenario:

uma-grant-flow
Grant flow explained
  • A requesting party, using a client application, requests access to an UMA-protected resource (labeled 1 and 2 in the diagram above).

  • The resource server checks the existing token (3) and determines that the requesting party does not have the correct privileges to access the resource. The resource server returns a permission ticket (4) to the client.

  • The client uses the permission ticket and a claim token to send an RPT from AM (5) and (6).

  • AM makes a policy decision using the requested scopes, the scopes permitted in the registered resource, and the user-created policy, and if successful returns an RPT (7 and 8).

  • The client presents the RPT to the resource server (9), which must verify the token is valid using the AM introspection endpoint (10). If the RPT is confirmed to be valid and not expired (10), the resource server can return the protected resource to the client for access by the requesting party (11).

Perform the steps in the following procedures to issue an RPT to a requesting party, and have it rejected as in the second scenario. The resource owner will then grant permission to the resource, and the requesting party will attempt the flow again:

Acquire a PAT on behalf of a resource owner

This example assumes that a confidential client called UMA-Resource-Server is registered in AM with, at least, the following configuration:

  • Client Secret: password

  • Scopes: uma_protection

  • Grant Types: Resource Owner Password Credentials

The example uses the Resource Owner Password Credentials grant, but you can use any grant type to obtain the PAT, except the Client Credentials grant. The example also assumes that an identity for the resource owner,alice, exists in AM.

  1. Send a POST request to the OAuth 2.0 access_token endpoint.

    This example uses the Resource Owner Password Credentials grant:

    $ curl \
    --request POST \
    --data 'grant_type=password' \
    --data 'scope=uma_protection' \
    --data 'username=alice' \
    --data 'password=Ch4ng31t' \
    --data 'client_id=UMA-Resource-Server' \
    --data 'client_secret=password' \
    "https://openam.example.com:8443/openam/oauth2/realms/root/realms/alpha/access_token"
    {
      "access_token": "oMsRVDXHYsWAC0KClr6dmX2_cIc",
      "scope": "uma_protection",
      "token_type": "Bearer",
      "expires_in": 3599
    }
  2. The value returned in access_token is the Protection API Token, or PAT Bearer token.

To use the Resource Owner Password Credentials grant type, as described in RFC 6749, the default authentication chain in the relevant realm must allow authentication using only a username and password, for example by using a DataStore module. Attempting to use the Resource Owner Password Credentials grant type with a chain that requires any additional input returns an HTTP 500 Server Error message.

Create a permission ticket

When the resource server receives a request for access to a resource, it contacts the authorization server to acquire a permission ticket. The permission ticket associates a request for a particular resource with the corresponding scopes. The resource owner’s PAT bearer token is used to map the request to the correct identity.

The permission ticket and the claim token are used to obtain an RPT. A new permission ticket must be used for each attempt to acquire an RPT.

This example assumes that a confidential client called UmaClient is registered in AM with, at least, the following configuration:

  • Client Secret: password

  • Scopes: openid, download

  • Grant Types: Resource Owner Password Credentials, UMA

    This example uses the Resource Owner Password Credentials grant, but you can use any grant type, except the Client Credentials one.

  • Token Endpoint Authentication Method: client secret post

    Confidential OpenID Connect clients can use several methods to authenticate, but this example uses client secret post for clarity. For more information, see OpenID Connect client authentication.

The example also assumes that an identity for the resource owner, bob, exists in AM.

  1. Send a POST request to the UMA permission_request endpoint:

    $ curl \
    --request POST \
    --header 'authorization: Bearer 057ad16f-7dba-4049-9f34-e609d230d43a' \ (1)
    --header 'cache-control: no-cache' \
    --header 'content-type: application/json' \
    --data '[
        {
            "resource_id" : "ef4d750e-3831-483b-b395-c6f059b5e15d0", (2)
            "resource_scopes" : ["download"]
        }
    ]' \
    https://openam.example.com:8443/openam/uma/realms/root/permission_request
    {
        "ticket": "eyJ0eXAiOiJ…​XPeJi3E" (3)
    }
    1 Use the PAT Bearer Token previously acquired on behalf of the resource owner.
    2 Specify ID of the registered resource for which this permission ticket will maintain permission state information. See Register an UMA resource (REST).
    3 The value returned in the ticket property is the permission ticket, which is used to obtain an RPT. See Obtain an RPT.

    The default lifetime for an UMA permission ticket is 120 seconds. Attempting to obtain a requesting party token after the permission ticket has expired will fail with the following error message:

    {
      "error_description": "The provided access grant is invalid, expired, or revoked.",
      "error": "invalid_grant"
    }

    To alter the default lifetime of a permission ticket, go to Realms > Realm Name > Services > UMA Provider, and edit the Permission Ticket Lifetime (seconds) property.

Gather claims

The UMA specification lets a requesting party (Bob, in our example) identify themselves in multiple ways during an UMA grant flow. This process of identification is also called claims gathering.

The authorization server must gather claims from the requesting party to create a claim token.

  1. Send a POST request to the OAuth 2.0 access_token endpoint.

    The value returned in the id_token property is the claim token required to obtain an RPT, along with the permission ticket acquired earlier:

    $ curl \
    --request POST \
    --data 'client_id=UmaClient' \
    --data 'client_secret=password' \
    --data 'grant_type=password' \
    --data 'scope=openid' \
    --data 'username=bob' \
    --data 'password=Ch4ng31t' \
    https://openam.example.com:8443/openam/oauth2/realms/root/access_token
    {
      "access_token": "f09f55e5-5e9c-48fe-aeaa-d377de88e8e6",
      "refresh_token": "ee2d35f6-5819-4734-8b3e-9af77a545563",
      "scope": "openid",
      "id_token": "eyJ0eXA...FBznEB5A", (1)
      "token_type": "Bearer",
      "expires_in": 4999
    }
    1 The value returned in the id_token property is the claim token, which is used to obtain an RPT. See Obtain an RPT.

To use the Resource Owner Password Credentials grant type, as described in RFC 6749, the default authentication chain in the relevant realm must allow authentication using only a username and password, for example by using a DataStore module. Attempting to use the Resource Owner Password Credentials grant type with a chain that requires any additional input returns an HTTP 500 Server Error message.

Obtain an RPT

The requesting party makes a request using the permission ticket and the claim token, in exchange for an RPT.

  1. Send a POST request to the OAuth 2.0 access_token endpoint.

    You must include the permission ticket, ticket, and the claim_token.

    The following example results in an error description, indicating that "The client is not authorised to access the requested resource set." The authorization server then sends a request to the resource owner to allow or deny access to the requesting party.

    $ curl \
    --request POST \
    --data 'client_id=UmaClient' \
    --data 'client_secret=password' \
    --data 'grant_type=urn:ietf:params:oauth:grant-type:uma-ticket' \
    --data 'ticket=eyJ0eXAiOiJ...XPeJi3E' \ (1)
    --data 'claim_token=eyJ0eXA...FBznEB5A' \ (2)
    --data 'claim_token_format=http://openid.net/specs/openid-connect-core-1_0.html#IDToken' \
    https://openam.example.com:8443/openam/oauth2/realms/root/access_token
    {
      "ticket": "eyJ0eXAiOiJ...XPeJi3E",
      "error_description": "The client is not authorised to access the requested resource set. A request has been submitted to the resource owner requesting access to the resource",
      "error": "request_submitted"
    }
    1 Specify the permission ticket acquired earlier. See Create a permission ticket.
    2 Specify the claim token acquired earlier. See Gather claims.

    The default lifetime for an UMA permission ticket is 120 seconds. Attempting to obtain a requesting party token after the permission ticket has expired will fail with an error message as follows:

    {
      "error_description": "The provided access grant is invalid, expired, or revoked.",
      "error": "invalid_grant"
    }

    If the ticket has expired, obtain another by repeating the steps in Create a permission ticket.

    To change the default lifetime of a permission ticket, go to Realms > Realm Name > Services > UMA Provider, and edit Permission Ticket Lifetime (seconds).

  2. The resource owner, Alice, logs into AM to view the access request.

    Alice clicks Shares > Requests, and clicks Allow to grant download access to Bob, the requesting party.

    The consent screen requires the resource owner to allow or deny access.
    Figure 1. Consent Screen Presented to the Resource Owner
  3. Because each permission token can only be used once, request a new permission token by performing the steps in Create a permission ticket.

  4. Resubmit the previous POST request for the RPT, with the new permission ticket obtained in the previous step and the original claim token:

    $ curl \
    --request POST \
    --data 'client_id=UmaClient' \
    --data 'client_secret=password' \
    --data 'grant_type=urn:ietf:params:oauth:grant-type:uma-ticket' \
    --data 'ticket=eyJ0efBiOiJ...XPeJc2A' \ (1)
    --data 'claim_token=eyJ0eXA...FBznEB5A' \ (2)
    --data 'claim_token_format=http://openid.net/specs/openid-connect-core-1_0.html#IDToken' \
    https://openam.example.com:8443/openam/oauth2/realms/root/access_token
    {
        "access_token": "Aw4a92ZoKsjadWKw2d4Rmcjv7DM",
        "token_type": "Bearer",
        "expires_in": 3599
    }
    1 Specify a refreshed permission ticket acquired earlier, otherwise you will receive a response such as: The provided access grant is invalid, expired, or revoked. See Create a permission ticket.
    2 Specify the same claim token as the first request for an RPT.

    The access_token is the RPT, which lets the requesting party access the resource through a client.

  5. You can use the /oauth2/introspect endpoint to inspect the properties of the RPT.

    Use the PAT issued to the resource owner for authenticating to the authorization server, and specify the RPT token in a query parameter named token, as follows:

    $ curl \
    --header 'authorization: Bearer 057ad16f-7dba-4049-9f34-e609d230d43a' \
    "https://openam.example.com:8443/openam/oauth2/realms/root/realms/alpha/introspect?token=Aw4a92ZoKsjadWKw2d4Rmcjv7DM"
    {
        "active": true,
        "permissions": [
            {
                "resource_id": "ef4d750e-3831-483b-b395-c6f059b5e15d0",
                "resource_scopes": [
                    "download"
                ],
                "exp": 1522334692
            }
        ],
        "token_type": "access_token",
        "exp": 1522334692,
        "iss": "https://openam.example.com:8443/openam/oauth2"
    }

Gather claims interactively

When claims are gathered with no user interaction, AM accepts an id_token as the provided claim_token. The identity of the requesting party is the subject of the id_token.

AM 7.2.0 also supports interactive claims gathering. In this case, AM redirects the client to a claims_gathering endpoint, to request additional claims. The claims gathering endpoint also offers the requesting party the option of a PCT, which persists claims across authorization processes. If the client wants to request another RPT later, they can simply send the PCT, along with the access token request. The authorization server can opt to re-use the claims from the PCT (provided they have not expired) instead of forcing the user through yet another interactive claims gathering flow.

The claims gathering process can be initiated two ways:

  • The client sends an access token request without providing a claim_token. AM returns a need_info error, and a redirect_user hint. The client then constructs a claims gathering request, using the hint, and displays that page to the end user.

  • The client locates the URL of the claims gathering endpoint in the UMA discovery metadata (/uma/.well-known/uma2-configuration) and initiates the claims gathering flow without first going to the token endpoint.

The interactive claims gathering process can take longer than the lifetime of the permission ticket. You should adjust the permission ticket lifetime so that end users are able to authenticate during that lifetime.

This diagram shows an interactive claims gathering scenario:

uma-ic-grant-flow
Interactive claims gathering grant flow
  • A requesting party, using a client application, requests access to an UMA-protected resource (labeled 1 and 2 in the diagram above).

  • The resource server checks the existing token (3) and determines that the requesting party does not have the privileges to access the resource. The resource server returns a 401 Unauthorized response, along with a permission ticket (4) to the client.

  • The client uses the permission ticket and a claim token to send an RPT to AM (5).

  • AM determines that additional claims are required and returns a 403 response, with the rotated permission ticket and a redirect to the claims gathering endpoint (6).

  • The client redirects the user to the claims gathering endpoint (7 and 8]).

  • AM determines that the user is not yet authenticated, and redirects the user to the claims gathering tree (9).

  • The user authenticates and is redirected back to the claims gathering endpoint (10, 11, 12).

  • The user provides the additional claims and is redirected to the claims redirect URI, with the updated permission ticket (13 and 14).

  • The client requests an RPT, using the updated permission ticket (15).

  • AM makes a policy decision using the requested scopes, the scopes permitted in the registered resource, and the user-created policy. If successful, AM returns an RPT and, optionally, a PCT (16).

  • The client presents the RPT to the resource server (17), which must verify the token is valid using the AM introspection endpoint (18). If the RPT is confirmed to be valid and non-expired, the resource server returns the protected resource to the client for access by the requesting party (19).

Enable interactive claims gathering

Interactive claims gathering is disabled by default.

To enable interactive claims gathering across all realms, go to Configure > Global Services > UMA Provider > Claims Gathering and set Interactive Claims Gathering Enabled.

To enable interactive claims gathering for a specific realm, go to Realms > Realm Name > Services > UMA Provider > Claims Gathering and set Interactive Claims Gathering Enabled.

For details of the other configuration options on this tab, see Claims gathering.

When interactive claims gathering is enabled:

  • the claims_gathering endpoint is accessible

  • the UMA grant type handler returns a redirect_user error, redirecting the user to the claims_gathering endpoint

  • the UMA provider validates PCTs

  • the UMA provider does not consult the claim_token parameter to gather claims non-interactively

Protect permission tickets with PKCE

When claims are gathered interactively, the permission ticket can hold sensitive information about the requesting party. You should therefore protect permission tickets obtained in an interactive claims gathering flow, in the same way you would protect other sensitive resources.

You can protect permission tickets with PKCE, in a similar flow to the Authorization code grant with PKCE flow. In this case, the code_challenge and code_challenge_method are submitted with the permission ticket, in the request to the claims_gathering endpoint.

For example:

$ curl --request GET \
--Cookie "iPlanetDirectoryPro=AQIC5wM…​TU3OQ*" \
--data "claims_redirect_uri=https://www.example.com:443/callback" \
--data "ticket=eyJ0eXA…​jpY" \
--data "client_id=UmaClient" \
--data "csrf=AQIC5wM…​TU3OQ*" \
--data "state=abc123" \
--data "code_challenge=j3wKnK2Fa_mc2tgdqa6GtUfCYjdWSA5S23JKTTtPF8Y" \
--data "code_challenge_method=S256" \
"https://openam.example.com:8443/openam/uma/realms/root/realms/alpha/claims_gathering"

To specify that clients must include PKCE challenges in their requests, go to Services > OAuth2 Provider > Advanced, and set the Code Verifier Parameter Required property, either for the specific realm, or as a global service parameter. Note that the UMA provider inherits this setting from the OAuth 2.0 provider. As such, it affects all OAuth 2.0 flows, and not just UMA flows.

Protect PCTs with certificate-bound proof-of-possession

Persisted claims tokens (PCTs) let clients obtain an RPT without having to go through the interactive claims gathering process multiple times. They are similar to OAuth 2.0 refresh tokens and, as such, should be protected.

Proof-of-possession (PoP) lets you ensure that the client to whom the PCT was issued is the client that subsequently attempts to use it.

In UMA flows, AM supports certificate-bound proof-of-possession only. JWK-based proof-of-possession is not supported.

You can protect PCTs with PoP in a similar flow to the OAuth 2.0 certificate-bound proof-of-possession flow.

The confirmation key (cnf_key) is sent alongside the request for the RPT, for example:

$ curl \
--request POST \
--header "content-type: application/json" \
--data "client_id=UmaClient" \
--data "client_secret=password" \
--data "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket" \
--data "ticket=eyJ0eXA…​jpY" \
--data "cnf_key=eyJ4NXQ…​==" \
"https://openam.example.com:8443/openam/oauth2/realms/root/realms/alpha/access_token" \
{
    "access_token": "HexwGYRSK4Dy1yI_aFXirdFT1-I",
    "pct": "eyJ0eXA…​jpY",
    "token_type": "Bearer",
    "expires_in": 3599
}
Copyright © 2010-2022 ForgeRock, all rights reserved.