---
title: Scope validation
description: Use this extension point to change how Advanced Identity Cloud validates requested OAuth 2.0 scopes.
component: pingoneaic
page_id: pingoneaic:am-oauth2:plugins-scope-validator
canonical_url: https://docs.pingidentity.com/pingoneaic/am-oauth2/plugins-scope-validator.html
keywords: ["OAuth 2.0", "Customization", "Plugins", "Authorization", "Scripting", "Scope"]
page_aliases: ["oauth2-guide:plugins-scope-validator.adoc"]
section_ids:
  prepare_the_demonstration: Prepare the demonstration
  sample-scope-validator-script: Sample script
  scope-validator-oauth2-client: OAuth 2.0 client
  scope-validator-resource-owner: Resource owner
  test_the_demonstration: Test the demonstration
  use_a_validated_script: Use a validated script
---

# Scope validation

Use this extension point to change how 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`            | [`/authorize`](oauth2-authorize-endpoint.html)                                                          |
| `validateAccessTokenScope`              | [`/authorize`](oauth2-authorize-endpoint.html) and [`/access_token`](oauth2-access_token-endpoint.html) |
| `validateRefreshTokenScope` (1)         | [`/access_token`](oauth2-refresh-tokens.html) for token refresh                                         |
| `validateBackChannelAuthorizationScope` | [`/bc_authorize`](oauth2-bc-authorize-endpoint.html)                                                    |

(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.

* Template script

  [OAuth2 Validate Scope Script](../am-scripting/sample-scripts.html#oauth2-validate-scope-js)

* Script bindings

  [Scope validation scripting API](../am-scripting/scope-validation-api.html)

## Prepare the demonstration

Start by preparing the demonstration:

* [Create a sample script](#sample-scope-validator-script).

* [Create an OAuth 2.0 client](#scope-validator-oauth2-client).

* [Create a resource owner account](#scope-validator-resource-owner).

### Sample script

The sample reproduces the default validation behavior and adds a `custom` scope on successful validation.

1. In the Advanced Identity Cloud admin console, [create a new](../developer-docs/scripting-auth.html#create-a-new-auth-script) OAuth2 Validate Scope script.

   |   |                                                                                                                                                                                                                                                                                           |
   | - | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
   |   | This creates a legacy script. You also have the option to create a [next-generation](../am-scripting/next-generation-scripts.html) scope validation script. Under Native Consoles > Access Management, go to Scripts > + New Script, and select Next Generation as the evaluator version. |

2. Name the script `Demo scope validator`.

3. Add the following JavaScript and save your work:

   * Legacy

   * Next-generation

   ```javascript
   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();
   }
   ```

   ```javascript
   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('custom');
       } 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](../am-scripting/script-bindings.html).You can find information about the bindings specific to scope validation scripts in the [Scope validation scripting API](../am-scripting/scope-validation-api.html). |

### 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 console, select Applications > + Add Application, and create a new Web client with the following settings:

   * Client ID

     `myClient`

   * Client Secret

     `mySecret`

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, 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 console, 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](oauth2-authz-grant.html) 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:

   ```bash
   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:

   ```bash
   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:

   ```bash
   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://<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:

   ```bash
   curl \
   --request POST \
   --user 'myClient:mySecret' \
   --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 console, 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
