PingAM

Scope validation

Use this plugin to configure the OAuth2 provider to validate the set of requested scopes against the allowed scopes and, optionally, to modify the list of valid scopes.

Sample script

OAuth2 Validate Scope Script (Legacy)

Script bindings
Java interface

org.forgerock.oauth2.core.plugins.ScopeValidator

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.HashSet;
import java.util.Set;

import org.forgerock.oauth2.core.ClientRegistration;
import org.forgerock.oauth2.core.OAuth2Request;
import org.forgerock.oauth2.core.exceptions.ServerException;
import org.forgerock.oauth2.core.plugins.ScopeValidator;

/**
 * Custom implementation of the Scope Validator
 * plugin interface {@link org.forgerock.oauth2.core.plugins.ScopeValidator}
 *
 * <li>
 * The {@code validateAuthorizationScope} method
 * adds default scopes, or any allowed scopes provided.
 * </li>
 *
 * <li>
 * The {@code validateAccessTokenScope} method
 * adds default scopes, or any allowed scopes provided.
 * </li>
 *
 * <li>
 * The {@code validateRefreshTokenScope} method
 * adds the scopes from the access token,
 * or any requested scopes provided that are also in the access token scopes.
 * </li>
 *
 *  * <li>
 *  * The {@code validateBackChannelAuthorizationScope} method
 *  * adds default scopes, or any allowed scopes provided.
 *  * </li>
 *
 */
public class CustomScopeValidator implements ScopeValidator {

    @Override
    public Set<String> validateAuthorizationScope(ClientRegistration clientRegistration, Set<String> scope,
            OAuth2Request oAuth2Request) throws ServerException {

        if (scope == null || scope.isEmpty()) {
            return clientRegistration.getDefaultScopes();
        }

        Set<String> scopes = new HashSet<String>(clientRegistration.getAllowedScopes());
        scopes.retainAll(scope);
        return scopes;
    }

    @Override
    public Set<String> validateAccessTokenScope(ClientRegistration clientRegistration,
            Set<String> scope, OAuth2Request request) throws ServerException {

        if (scope == null || scope.isEmpty()) {
            return clientRegistration.getDefaultScopes();
        }

        Set<String> scopes = new HashSet<String>(clientRegistration.getAllowedScopes());
        scopes.retainAll(scope);
        return scopes;
    }

    @Override
    public Set<String> validateRefreshTokenScope(ClientRegistration clientRegistration,
            Set<String> requestedScope, Set<String> tokenScope, OAuth2Request request) {

        if (requestedScope == null || requestedScope.isEmpty()) {
            return tokenScope;
        }

        Set<String> scopes = new HashSet<String>(tokenScope);
        scopes.retainAll(requestedScope);
        return scopes;
    }

    @Override
    public Set<String> validateBackChannelAuthorizationScope(ClientRegistration clientRegistration,
            Set<String> requestedScopes, OAuth2Request request) throws ServerException {

        if (requestedScopes == null || requestedScopes.isEmpty()) {
            return clientRegistration.getDefaultScopes();
        }

        Set<String> scopes = new HashSet<>(clientRegistration.getAllowedScopes());
        scopes.retainAll(requestedScopes);
        return scopes;
    }
}

The plugin comprises four functions or methods that let you customize the validation of scopes at the following endpoints:

Function / Method Endpoint

validateAuthorizationScope

/authorize

validateAccessTokenScope

/authorize and /access_token

validateRefreshTokenScope (1)

/access_token with grant_type=refresh_token

validateBackChannelAuthorizationScope

/bc_authorize

(1) To make sure the refresh token inherits the scopes currently granted to the access token, call scopeValidatorHelper.inheritAccessTokenScopesOnRefresh() from this method in your next-generation script.

Example scope validation plugin

Follow these steps to implement a scope validation script that modifies the list of valid scopes.

Learn about how to implement a Java scope validation plugin in Configure AM to use a Java OAuth 2.0 plugin.

Create a custom script

This example script adds a customscope to the requested scopes.

  1. Create a new OAuth2 Validate Scope Script.

  2. Name your script Demo scope validation 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 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('customscope')
          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();
    }
    function validateScopes() {
    
        var scopes = [];
    
        if (!requestedScopes || requestedScopes.isEmpty()) {
            scopes = defaultScopes;
        } else {
            // Filter requestedScopes to only those found in allowedScopes
            for each (var requested in requestedScopes) {
                if (allowedScopes.contains(requested)) {
                    scopes.push(requested);
                }
            }
    
            // At least one scope was invalid if more scopes requested than allowed
            if (requestedScopes.size() > scopes.length) {
                // Find which specific scopes were invalid for the error log
                for each (var checkScope in requestedScopes) {
                    if (!allowedScopes.contains(checkScope)) {
                        logger.error("Invalid scope detected: " + checkScope);
                    }
                }
                logger.error("Unknown/invalid scope(s)");
            }
        }
        if (scopes) {
          // Validation succeeded. Add a custom scope.
          scopes.add('customscope')
        } else {
          logger.error("No scope requested and no default scope configured");
        }
        return scopes;
    }
    function validateAuthorizationScope() {
        return validateScopes();
    }
    
    function validateAccessTokenScope() {
        return validateScopes();
    }
    
    function validateRefreshTokenScope() {
        return validateScopes();
    }
    
    function validateBackChannelAuthorizationScope() {
        return validateScopes();
    }

    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 validation scripts in the Scope validation scripting API.

  4. Save your changes.

Configure AM to use the custom scope validation script

Perform this task to set up the OAuth2 provider to use the scope validation script.

  1. Configure the provider and make sure the following properties are set:

    • Scope Validation Plugin Type to SCRIPTED.

    • Scope Validation Script to Demo scope validation script.

  2. Save your changes.

Create an OAuth2 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, add Client Credentials to the Grant Types, and save your changes.

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

    Enable OAuth2 Provider Overrides

    Enabled

    Scope Validation Plugin Type

    SCRIPTED

    Scope Validation Script

    Demo scope validation script

  4. Save your changes.

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

Try the script

To try your custom script, 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 access, and your client details.

    For example:

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

    For example:

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

    Verify that the response contains both the requested scope and the additional scope, customscope.