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.ScopeValidatorSample 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 |
|---|---|
|
|
|
|
|
|
|
|
(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.
-
Create a new OAuth2 Validate Scope Script.
-
Name your script
Demo scope validation script.You can create either a Legacy or a Next Generation script.
-
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
loggerandscriptNamein Common bindings.You can find information about the bindings specific to scope validation scripts in the Scope validation scripting API.
-
-
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.
-
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.
-
-
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.
-
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
-
On the Advanced tab, add
Client Credentialsto the Grant Types, and save your changes. -
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
-
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.
-
Send a POST request to the /oauth2/access_token endpoint, specifying the grant type as
client_credentials, scope asaccess, 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 } -
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.