PingAM 7.5.1

Demonstrate delegation

This page demonstrates token exchange with delegation.

Prepare the demonstration

Start by preparing the demonstration:

May act script

The script adds a may_act claim to the subject token.

  1. In the AM admin UI, select Realm > Realm Name > Scripts + New Script.

  2. In the New Script window, name the script May act and set Script Type to OAuth2 May Act.

  3. In the edit window, save the following JavaScript:

    (function () {
        var frJava = JavaImporter(
            org.forgerock.json.JsonValue
        );
    
        var mayAct = frJava.JsonValue.json(frJava.JsonValue.object())
        mayAct.put('client_id', 'delegateClient')
        mayAct.put('sub', '(act!delegateClient)')
        token.setMayAct(mayAct)
    }());

    This script generates a may_act claim to permit the delegate actor client to exchange the subject token, provided it also supplies an actor token where it is the subject.

Subject client

The OAuth 2.0 client profile in this example overrides the AM OAuth 2.0 provider settings. This lets you test the script without affecting access tokens issued to other clients.

  1. Create a confidential OAuth 2.0 client account to get an original token for the subject.

    In the AM admin UI, select Realm > Realm Name > Applications > OAuth 2.0 > Clients > + Add Client, and create a new confidential client with the following settings:

    Client ID

    myClient

    Client secret

    forgerock

    Redirection URIs

    https://www.example.com:443/callback

    Scopes

    change_contract
    repair

  2. Switch to the Advanced tab, enable Implied consent, and save your work:

  3. Override OAuth 2.0 provider settings for this client.

    Switch to the OAuth2 Provider Overrides tab, update the following settings, and save your work:

    Enable OAuth2 Provider Overrides

    Enabled

    OAuth2 Access Token May Act Script

    May act

    OIDC ID Token May Act Script

    May act

Actor client

Create a confidential OAuth 2.0 client account for the service that acts on behalf of the user.

  1. In the AM admin UI, select Realm > Realm Name > Applications > OAuth 2.0 > Clients > + Add Client.

  2. Create a new confidential client with the following settings:

    Client ID

    delegateClient

    Client Secret

    forgerock

    Grant Types

    Client Credentials
    Refresh Token
    Token Exchange

    Scopes

    repair

Resource owner

An OAuth 2.0 client requests the access token on behalf of a resource owner.

  1. In the AM admin UI, select Realms > Realm Name > Identities > + Add Identity and fill the required fields.

  2. Record the username and password.

Test the demonstration

After preparing the demonstration, test your work using HTTP calls to REST endpoints.

The demonstration uses the Authorization code grant and and Client credentials grant flows followed by token exchange:

  • The resource owner authenticates to obtain an SSO token.

  • The subject client relies on Implied Consent being enabled. It assumes the resource owner grants the client access.

  • The subject client requests the authorization code and exchanges it for an access token. Your script sets the may_act claim in the access token.

  • The actor client requests an actor token with its client credentials.

  • The actor client exchanges the subject token and actor token for an ID token.

Follow these steps:

  1. Authenticate as the resource owner:

    curl \
    --request POST \
    --header 'Content-Type: application/json' \
    --header 'X-OpenAM-Username: <resource-owner-username>' \
    --header 'X-OpenAM-Password: <resource-owner-password>' \
    --header 'Accept-API-Version: resource=2.0, protocol=1.0' \
    'https://openam.example.com/openam/json/realms/root/realms/alpha/authenticate'
    {"tokenId":"<resource-owner-tokenId>","successUrl":"/openam/console","realm":"/alpha"}
  2. Request the authorization code as the subject client:

    curl \
    --dump-header - \
    --request POST \
    --cookie 'iPlanetDirectoryPro=<resource-owner-tokenId>' \
    --data 'scope=change_contract repair' \
    --data 'response_type=code' \
    --data 'client_id=myClient' \
    --data 'csrf=<resource-owner-tokenId>' \
    --data 'redirect_uri=https://www.example.com:443/callback' \
    --data 'state=abc123' \
    --data 'decision=allow' \
    'https://openam.example.com/openam/oauth2/realms/root/realms/alpha/authorize'
    …​
    location: https://www.example.com:443/callback?code=<authorization-code>&iss=https%3A%2F%2F…​
    …​
  3. Exchange the authorization code for an access token as the subject client:

    curl \
    --request POST \
    --user 'myClient:forgerock' \
    --data 'grant_type=authorization_code' \
    --data 'code=<authorization-code>' \
    --data 'redirect_uri=https://www.example.com:443/callback' \
    'https://openam.example.com/openam/oauth2/realms/root/realms/alpha/access_token'
    {
      "access_token": "<subject-access-token>",
      "refresh_token": "<refresh-token>",
      "scope": "change_contract repair",
      "token_type": "Bearer",
      "expires_in": 3599
    }

    Your script has set the may_act claim, which is not directly visible. To see the may_act claim, you must introspect the access token.

  4. Request an access token as the actor client:

    curl \
    --request POST \
    --user 'delegateClient:forgerock' \
    --data 'grant_type=client_credentials' \
    --data 'scope=repair' \
    'https://openam.example.com/openam/oauth2/realms/root/realms/alpha/access_token'
    {"access_token":"<actor-access-token>","scope":"repair","token_type":"Bearer","expires_in":3599}
  5. Request an exchanged token as the actor client:

    curl \
    --request POST \
    --user 'delegateClient:forgerock' \
    --data 'grant_type=urn:ietf:params:oauth:grant-type:token-exchange' \
    --data 'scope=repair' \
    --data 'subject_token=<subject-access-token>' \
    --data 'subject_token_type=urn:ietf:params:oauth:token-type:access_token' \
    --data 'actor_token=<actor-access-token>' \
    --data 'actor_token_type=urn:ietf:params:oauth:token-type:access_token' \
    --data 'requested_token_type=urn:ietf:params:oauth:token-type:id_token' \
    'https://openam.example.com/openam/oauth2/realms/root/realms/alpha/access_token'
    {
      "access_token": "<exchanged-id-token>",
      "refresh_token": "<new-refresh-token>,"
      "issued_token_type": "urn:ietf:params:oauth:token-type:id_token",
      "scope": "repair",
      "token_type": "Bearer",
      "expires_in": 3599
    }

    The issued_token_type shows this is an exchanged token.