PingOne Advanced Identity Cloud

Scope validation

Use this extension point to change how PingOne Advanced Identity Cloud validates requested OAuth 2.0 scopes.

The extension defines these functions to customize scope validation by OAuth 2.0 endpoint:

Function Endpoints

validateAuthorizationScope

validateAccessTokenScope

validateRefreshTokenScope

/access_token for token refresh

validateBackChannelAuthorizationScope

For additional details, refer to the sample scope validation script.

Prepare the demonstration

Start by preparing the demonstration:

Sample script

The sample reproduces the default validation behavior and adds a custom scope on successful validation. It uses the scope-related bindings.

  1. Create the script.

    In the Advanced Identity Cloud admin UI, select Scripts > Auth Scripts > + New Script, and create a new OAuth2 Validate Scope script.

  2. Name the script Demo scope validator.

  3. Add the following JavaScript and save your work:

    function validateScopes() {
      var frJava = JavaImporter(
        org.forgerock.oauth2.core.exceptions.InvalidScopeException
      )
    
      var scopes
      if (requestedScopes) {
        scopes = new java.util.HashSet(allowedScopes)
        scopes.retainAll(requestedScopes)
        if (requestedScopes.size() > scopes.size()) {
          var invalidScopes = new java.util.HashSet(requestedScopes)
          invalidScopes.removeAll(allowedScopes)
          throw new frJava.InvalidScopeException('Unknown/invalid scope(s)')
        }
      } else {
        scopes = defaultScopes
      }
    
      if (scopes) {
          // Validation succeeded. Add a custom scope.
          scopes.add('custom')
          return scopes
      } else {
          throw new frJava.InvalidScopeException('No scope requested and no default scope configured')
      }
    }
    
    function validateAuthorizationScope() {
      return validateScopes()
    }
    
    function validateAccessTokenScope() {
      return validateScopes()
    }
    
    function validateRefreshTokenScope() {
      return validateScopes()
    }
    
    function validateBackChannelAuthorizationScope() {
      return validateScopes()
    }

OAuth 2.0 client

The OAuth 2.0 client profile in this example overrides the 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.

    In the Advanced Identity Cloud admin UI, select Applications > + Add Application, and create a new Web client with the following settings:

    Client ID

    myClient

    Client Secret

    forgerock

  2. Add the following settings in the client profile and save your work:

    Sign-in URLs

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

    Scopes

    custom
    mail

    In this demonstration, the client requests only the mail scope. For successful validation, PingOne Advanced Identity Cloud must allow the custom scope even though the client does not request it.

  3. Override OAuth 2.0 provider settings for this client.

    Under Native Consoles > Access Management, select Realms > alpha > Applications > OAuth 2.0 > Clients > myClient. Switch to the OAuth2 Provider Overrides tab, update the following settings and save your work:

    Enable OAuth2 Provider Overrides

    Enabled

    Scope Validation Plugin Type

    SCRIPTED

    Scope Validation Script

    Demo scope validator

Resource owner

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

  1. Create the OAuth 2.0 resource owner account.

    In the Advanced Identity Cloud admin UI, select Identities > Manage > Alpha Realm - Users > + New Alpha Realm - User and fill the required fields.

    Record the username and password.

  2. Update the following settings in the new user profile and save your work:

    Email Address

    user@example.com

Test the demonstration

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

The demonstration uses the Authorization code grant flow:

  • The resource owner authenticates to obtain an SSO token.

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

  • The client requests the authorization code and exchanges it for an access token your script modified.

  • The client introspects the access 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://<tenant-env-fqdn>/am/json/realms/root/realms/alpha/authenticate'
    {"tokenId":"<resource-owner-tokenId>","successUrl":"/enduser/?realm=/alpha","realm":"/alpha"}
  2. Request the authorization code as the client:

    curl \
    --dump-header - \
    --request POST \
    --cookie '<session-cookie-name>=<resource-owner-tokenId>' \
    --data 'scope=mail' \
    --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://<tenant-env-fqdn>/am/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 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://<tenant-env-fqdn>/am/oauth2/realms/root/realms/alpha/access_token'
    {
      "access_token": "<access-token>",
      "refresh_token": "<refresh-token>",
      "scope": "mail custom",
      "token_type": "Bearer",
      "expires_in": 3599
    }

    Notice the response contains both the requested scope and the additional scope, "scope": "mail custom".

  4. Introspect the access token as the client:

    curl \
    --request POST \
    --user 'myClient:forgerock' \
    --data 'token=zPwX1RzsUo7XYSSSjr7xw4P7Av0' \
    'https://<tenant-env-fqdn>/am/oauth2/realms/root/realms/alpha/introspect'
    {
      "active": true,
      "scope": "mail custom",
      "realm": "/alpha",
      "client_id": "myClient",
      "user_id": "014c54bd-6078-4639-8316-8ce0e7746fa4",
      "username": "014c54bd-6078-4639-8316-8ce0e7746fa4",
      "token_type": "Bearer",
      "exp": 1670257212,
      "sub": "014c54bd-6078-4639-8316-8ce0e7746fa4",
      "subname": "014c54bd-6078-4639-8316-8ce0e7746fa4",
      "iss": "https://<tenant-env-fqdn>/am/oauth2/realms/root/realms/alpha",
      "auth_level": 0,
      "authGrantId": "LtuYkc-HLKN58cCao_hmNfMeGZw",
      "auditTrackingId": "b187bfdc-c0ff-4942-b4be-34a8c7134c40-1943612"
    }

    Notice the response contains both scopes.

Use a validated script

Test your scope validation scripts as you did for the demonstration. After validating your script with OAuth 2.0 provider overrides in your test client, you can update the OAuth 2.0 provider configuration to use the script.

  1. In the Advanced Identity Cloud admin UI, add the script to the target realm if you have not done so.

  2. Select Realms > Realm Name > Services > OAuth2 Provider, switch to the Plugins tab, and edit these settings before saving your work:

    • Scope Validation Plugin Type

    • Scope Validation Script

Available objects

PingOne Advanced Identity Cloud injects the following objects into the execution context of an OAuth 2.0 scope validation script:

Binding Information

allowedScopes

The set of scope strings PingOne Advanced Identity Cloud allows this client to request.

defaultScopes

The set of scope strings configured as defaults for this client.

httpClient

An HTTP client for making external HTTP requests.

logger

Write a message to the PingOne Advanced Identity Cloud debug log. Always present in all extension scripts.

In Advanced Identity Cloud, this corresponds to the am-core log source.

The logger identifier takes the form scripts.script-type.script-id.

For details, refer to Debug.

requestedScopes

The set of scope strings in the client request.

scriptName

The display name of the script.