PingAM

Scope evaluation

This extension point retrieves and evaluates the scope information for an OAuth2 access token.

The default scopes implementation in AM treats scopes as profile attributes for the resource owner. When a resource server or other entity uses the access token to get token information from AM, AM populates the scopes with profile attribute values. For example, if one of the scopes is mail, AM sets mail to the resource owner’s email address in the token information returned.

The plugin lets you extend or modify this behavior by writing your own scope evaluation plugin to populate the scopes with custom values.

Sample script

OAuth2 Evaluate Scope Script (Legacy)

Script bindings
Java interface

org.forgerock.oauth2.core.plugins.ScopeEvaluator

Sample Java code
/*
 * Copyright 2021-2025 Ping Identity Corporation. All Rights Reserved
 *
 * This code is to be used exclusively in connection with Ping Identity
 * Corporation software or services. Ping Identity Corporation only offers
 * such software or services to legal entities who have entered into a
 * binding license agreement with Ping Identity Corporation.
 */

package org.forgerock.openam.examples;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.forgerock.oauth2.core.AccessToken;
import org.forgerock.oauth2.core.plugins.ScopeEvaluator;

/**
 * Custom implementation of the Scope Evaluator
 * plugin interface {@link org.forgerock.oauth2.core.plugins.ScopeEvaluator}
 *
 * <li>
 * The {@code evaluateScope} method populates scope values to return.
 * </li>
 *
 */
public class CustomScopeEvaluator implements ScopeEvaluator {

    @Override
    public Map<String, Object> evaluateScope(AccessToken token) {
        return mapScopes(token);
    }

    /**
     * Set read and write permissions according to scope.
     *
     * @param token The access token presented for validation.
     * @return The map of read and write permissions,
     *         with permissions set to {@code true} or {@code false},
     *         as appropriate.
     */
    private Map<String, Object> mapScopes(AccessToken token) {
        Set<String> scopes = token.getScope();
        Map<String, Object> map = new HashMap<String, Object>();
        final String[] permissions = {"read", "write"};

        for (String scope : permissions) {
            if (scopes.contains(scope)) {
                map.put(scope, true);
            } else {
                map.put(scope, false);
            }
        }
        return map;
    }
}

Example Java plugin

This example uses the Java sample provided by AM. Complete the following steps to implement a custom scope evaluation Java plugin that sets read and write values in the access token according to the scope information.

To configure AM to use a scripted scope evaluation plugin, refer to the steps in Configure AM to use a scripted OAuth 2.0 plugin.

Find information about the available script bindings in the Scope evaluation scripting API.

Create and deploy the sample scope evaluation Java plugin

  1. Clone the sample code and build a JAR file by following the steps described in How do I access and build the sample code provided for PingAM? in the Knowledge Base.

    Files included in the sample
    pom.xml

    Apache Maven project file for the module.

    This file specifies how to build the sample, and specifies its dependencies on AM components.

    src/main/java/org/forgerock/openam/examples/CustomScopeEvaluator.java

    Sample class for the Scope Evaluation OAuth 2.0 plugin.

    After you successfully build the project, you find the openam-scope-sample-8.1.0.jar in the openam-samples/openam-scope-sample/target directory of the project.

  2. Copy the built JAR file to the /WEB-INF/lib folder where you deployed AM.

  3. Restart AM or the container in which it runs.

Configure AM to use the custom scope evaluation plugin

Perform this task to set up an OAuth 2.0 provider that uses the sample scope evaluation Java implementation.

  1. Log in to the AM admin UI as an administrator.

    For example, amAdmin.

  2. Configure the provider and ensure the following properties are set:

    • Scope Evaluation Plugin Type to JAVA.

    • Scope Evaluation Plugin Implementation Class to org.forgerock.openam.examples.CustomScopeEvaluation.

    By default, a new OAuth 2.0 provider uses the default Java implementation.

  3. Save your changes.

Create an OAuth2 client

Create an OAuth 2.0 client to use in the client credentials grant flow.

  1. In the AM admin UI, go to Realms > realm name > Applications > OAuth 2.0 > Clients, and click Add Client.

  2. Enter the following values:

    • Client ID: myClient

    • Client secret: mySecret

    • Redirection URIs: https://www.example.com:443/callback

    • Scope(s): read write

  3. Click Create.

  4. In Advanced > Grant Types, add Client Credentials.

  5. Save your changes.

AM is now configured for you to try the sample scope evaluation script.

Try the custom scope evaluation Java plugin

To try the custom scope evaluation plugin, use the Client credentials grant flow.

  1. Send a POST request to the /oauth2/access_token endpoint, specifying the grant type as client_credentials, scope as read, and your client details.

    For example:

    $ curl \
    --request POST \
    --data "grant_type=client_credentials" \
    --data "client_id=myClient" \
    --data "client_secret=mySecret" \
    --data "scope=read" \
    "https://am.example.com:8443/am/oauth2/realms/root/realms/alpha/access_token"
    {
      "access_token": "M3M2Jb2SMjvgWhzNas2SVy2LALg",
      "scope": "read",
      "token_type": "Bearer",
      "expires_in": 3599
    }
  2. Call the oauth2/tokeninfo endpoint to inspect the custom scope values. Include the access token obtained in the previous request.

    For example:

    $ curl \
    "https://am.example.com:8443/am/oauth2/realms/root/realms/alpha/tokeninfo\
    ?access_token=M3M2Jb2SMjvgWhzNas2SVy2LALg"
    {
      "access_token": "M3M2Jb2SMjvgWhzNas2SVy2LALg",
      "read": true,
      "grant_type": "client_credentials",
      "auditTrackingId": "f9a8395d-1bac-4cba-8b09-8cc336dc49e2-6810",
      "scope": ["read"],
      "realm": "/alpha",
      "token_type": "Bearer",
      "expires_in": 3583,
      "authGrantId": "l3355H89FDSWsfdKJmvWssGk_oE",
      "write": false,
      "client_id": "myClient"
    }

    As this example indicates, the requested scope read is authorized, but the write scope has not been authorized.

Example scripted plugin

Follow these steps to customize how AM returns custom user session data with a script:

Create a custom script

The example script populates scopes with attributes from the resource owner’s user profile.

  1. Create a new OAuth2 Evaluate Scope script.

  2. Name your script Demo scope evaluation script.

    You can create either a Legacy or a Next Generation script.

  3. In the script window, add the following JavaScript:

    • Legacy

    • Next-generation

    (function () {
        var map = new java.util.HashMap();
        if (identity) {
         // convert Set of scopes to an Array
            var scopes = accessToken.getScope().toArray();
            scopes.forEach(function (scope) {
                var attributes = identity.getAttribute(scope).toArray();
                map.put(scope, attributes.join(","));
            });
        } else {
            logger.error('Identity is null');
        }
        return map;
    }());
    (function () {
        var map = {};
        if (identity) {
            // returns a List of scopes
            var scopes = accessToken.getScope();
            for each (var scope in scopes) {
                var attributes = identity.getAttributeValues(scope);
                map[scope] = Array.from(attributes).join(",");
            };
        } else {
            logger.error('Identity is null');
        }
        return map;
    }());

    You can find information about the common bindings such as logger and scriptName in Common bindings.

    You can find information about the bindings specific to scope evaluation scripts in the Scope evaluation scripting API.

  4. Save your changes.

Create the resource owner

The script requires a resource owner with an email address in their user profile. Follow these steps to prepare a test user profile:

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

    Record the username and password, for example, bjensen and Ch4ng31t.

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

    Email Address

    bjensen@example.com

  3. Save your changes.

Create an OAuth 2.0 client

Create and configure a client application to override the OAuth 2.0 provider settings in the authorization flow.

  1. Create an OAuth 2.0 client with the following values:

    Client ID

    myClient

    Client secret

    mySecret

    Redirection URIs

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

    Scope(s)

    mail

  2. On the Advanced tab, enable Implied consent, and save your changes.

  3. Switch to the OAuth2 Provider Overrides tab and update the following settings:

    Enable OAuth2 Provider Overrides

    Enabled

    Scope Evaluation Plugin Type

    SCRIPTED

    Scope Evaluation Script

    Demo scope evaluation script

  4. Save your changes.

AM is now configured to use your custom scope evalution script.

Try the script

Test the example using the Authorization code grant flow:

  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://am.example.com:8443/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 \
    --header 'iPlanetDirectoryPro: <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://am.example.com:8443/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:mySecret' \
    --data 'grant_type=authorization_code' \
    --data 'code=<authorization-code>' \
    --data 'redirect_uri=https://www.example.com:443/callback' \
    'https://am.example.com:8443/am/oauth2/realms/root/realms/alpha/access_token'
    {
      "access_token": "<access-token>",
      "refresh_token": "<refresh-token>",
      "scope": "mail",
      "token_type": "Bearer",
      "expires_in": 3599
    }
  4. Get information about the access token as the client acting as a resource server:

    curl \
    --request GET \
    --header 'iPlanetDirectoryPro: AQIC5wM…​TU3OQ*' \
    'https://am.example.com:8443/am/oauth2/realms/root/realms/alpha/tokeninfo'
    {
      "access_token": "<access-token>",
      "mail": "bjensen@example.com",
      "grant_type": "authorization_code",
      "auth_level": 0,
      "auditTrackingId": "<audit-tracking-id>",
      "scope": ["mail"],
      "realm": "/alpha",
      "token_type": "Bearer",
      "expires_in": 3497,
      "authGrantId": "<auth-grant-id>",
      "client_id": "myClient"
    }

    The script added "mail": "bjensen@example.com".