Add OAuth2 Access Token Modification Script (OAUTH2_ACCESS_TOKEN_MODIFICATION)#1034
Open
vharseko wants to merge 4 commits into
Open
Add OAuth2 Access Token Modification Script (OAUTH2_ACCESS_TOKEN_MODIFICATION)#1034vharseko wants to merge 4 commits into
OAUTH2_ACCESS_TOKEN_MODIFICATION)#1034vharseko wants to merge 4 commits into
Conversation
Propagate the authentication context class reference (`acr`) and the authentication modules (`authModules`, i.e. `amr`) into the stateless JWT access token, mirroring the behaviour already present in `StatelessTokenStore.createRefreshToken`. Previously these claims were only emitted into the `id_token`, the `/oauth2/userinfo` and `/oauth2/tokeninfo` responses. The stateless access token was built from a hard-coded claim set with no extension point, so PEPs / API gateways had to make an extra `/oauth2/tokeninfo` round-trip to read `acr`/`amr`. They can now be read directly from the access token payload. The values are sourced from: - the `AuthorizationCode` (authorization_code grant), and - the previous `RefreshToken` (refresh_token grant), which overrides the authorization-code values when present. Both claims are added only when a value is available, so the change is additive and backward-compatible (e.g. client_credentials grant emits neither). Changes: - openam-oauth2: StatelessTokenStore.createAccessToken now extracts and adds the `acr` and `authModules` claims. - openam-oauth2: StatelessTokenStoreTest adds 4 tests covering authorization-code source, refresh-token source, refresh-token precedence, and the no-source case. - docs: chap-openid-connect.adoc updates the decoded stateless access token example and adds a note explaining when `acr`/`authModules` appear. Refs: OpenAM Discussion OpenIdentityPlatform#1027
…ateless JWT Add a fully-fledged OAuth2 Access Token Modification script (script context `OAUTH2_ACCESS_TOKEN_MODIFICATION`), analogous to ForgeRock AM 6.5, so that custom claims (e.g. `acr`, `amr`, roles, profile attributes) can be embedded directly into stateless (JWT) access and refresh tokens at issue time — without an extra `/oauth2/tokeninfo` round-trip. Previously the stateless JWT claim set in StatelessTokenStore was hard-coded with no extension point, so the OIDC Claims Script / custom Scope Implementation Class were never invoked when issuing access tokens. Script bindings: `accessToken` (mutable, setField/getField/removeField), `scopes`, `identity`, `session`, `requestProperties`, `logger`. The script runs both in createAccessToken and createRefreshToken so custom claims survive the refresh cycle. Changes: - ScriptConstants: new ScriptContext + GlobalScript (uuid d22f9a0c-426a-4466-b95e-d0f125b0d5fa) - ScriptingGuiceModule: engine manager + named ScriptEvaluator - ScriptEngineConfigurator / ScriptConfigurationService: register + usage count - scripting.xml / scripting.properties / ScriptResource.properties: context, whitelist, default global Groovy script, i18n - groovy/access-token-modification.groovy: default script - OAuth2Constants: provider attribute + ScriptParams (accessToken, requestProperties) - ScriptableAccessToken: mutable token view exposed to scripts (new) - OAuth2AccessTokenModifier: loads & evaluates the script, merges claims (new) - StatelessTokenStore + OAuth2GuiceModule: invoke modifier for access & refresh - OAuth2Provider.xml/.properties/.section.properties: new provider setting - OAuth2ProviderUpgradeHelper + serviceDefaultValues.properties: upgrade/default - docs: dev-guide (chap-scripting, chap-client-dev), reference (chap-config-ref), admin-guide (chap-manage-scripts) - tests: StatelessTokenStoreTest (+4), ScriptableAccessTokenTest (new) No UI changes required: script contexts and the provider attribute are rendered dynamically from the service schema. Backwards compatible: when no script is selected (`[Empty]`) the token is issued unchanged. Refs: OpenAM Discussion OpenIdentityPlatform#1027
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR introduces a fully-fledged OAuth2 Access Token Modification script type
(script context
OAUTH2_ACCESS_TOKEN_MODIFICATION), analogous to ForgeRock AM 6.5.It adds a supported extension point that allows custom claims (e.g.
acr,amr, roles, profile attributes) to be embedded directly into stateless (JWT)OAuth 2.0 access tokens and refresh tokens at issue time — without an extra
call to
/oauth2/tokeninfo.Closes / addresses: OpenAM Discussion #1027
Background / Problem
The stateless JWT claim set is built in
StatelessTokenStore.createAccessToken(...)directly through
JwtClaimsSetBuilderwith a hard-coded set of fields and noextension point.
As a result, by design:
scope-to-claim mappings are never invoked when issuing a stateless JWT
access token (they only apply to
id_token,/userinfo,/tokeninfo).Access Token Modification Script.
Users therefore could not get
acr/amr/custom claims into the access tokenpayload without an additional
/tokeninforound-trip.Solution
A new script context
OAUTH2_ACCESS_TOKEN_MODIFICATIONis executed when astateless access token or refresh token is issued. Claims the script adds,
modifies, or removes are merged into the token payload before signing.
The script runs in both
createAccessTokenandcreateRefreshToken, socustom claims survive the refresh cycle.
Script bindings
accessTokenScriptableAccessTokensetField/getField/removeFieldscopesSet<String>identityAMIdentity(nullable)sessionSSOToken(nullable)requestPropertiesMap<String, Object>clientId,realm,grantType)loggerDebugOAuth2Providerdebug loggerDefault script (Groovy)
A safe default global script is shipped (UUID
d22f9a0c-426a-4466-b95e-d0f125b0d5fa). It propagatesacr/amrand includescommented examples (e.g. adding
email). When the provider setting is[Empty], the token is issued unchanged.Changes
Scripting infrastructure (
openam-scripting)ScriptConstants: newScriptContext.OAUTH2_ACCESS_TOKEN_MODIFICATION,*_NAMEconstant,GlobalScriptentry.ScriptingGuiceModule:StandardScriptEngineManager+ namedScriptEvaluator.ScriptEngineConfigurator: register engine config for the new context.ScriptConfigurationService: usage-count handling for the new context.scripting.xml/scripting.properties: choice values, context sub-configwith class whitelist, default global Groovy script.
groovy/access-token-modification.groovy: default script +pom.xmlinject.OAuth2 (
openam-oauth2/openam-core)OAuth2Constants: provider settingforgerock-oauth2-provider-access-token-modification-script, newScriptParams(accessToken,requestProperties).ScriptableAccessToken(new): mutable token view exposed to scripts.OAuth2AccessTokenModifier(new): loads & evaluates the script, buildsbindings, returns claims to merge.
StatelessTokenStore: invokes the modifier and merges claims in bothcreateAccessTokenandcreateRefreshToken.OAuth2GuiceModule: threads the new dependency into the realm-agnostic store.OAuth2Provider.xml/.properties/.section.properties: new providerattribute with
scriptSelectUI type.Upgrade / defaults
OAuth2ProviderUpgradeHelper: registers the new attribute for upgrades.serviceDefaultValues.properties:GlobalAccessTokenModificationScriptId.Documentation (
openam-documentation)chap-scripting.adoc(new API section + context list),chap-client-dev.adoc(language table + context values).chap-config-ref.adoc(provider attribute + scripting context).chap-manage-scripts.adoc(context enum).Tests
StatelessTokenStoreTest: +4 tests (claims merged into access token & refreshtoken; empty result leaves token unchanged; constructor update).
ScriptableAccessTokenTest(new): 6 tests for the binding object API.UI impact
None required. Script contexts and the OAuth2 Provider attribute are rendered
dynamically from the service schema (
ScriptsService.getAllContexts()/getContextSchema(), and thescriptSelectattribute viaScriptChoiceValues).OIDC_CLAIMSis likewise never hard-coded in the UI.Backwards compatibility
Fully backwards compatible. When no script is selected (
[Empty]), the token isissued exactly as before. The feature only applies to stateless (JWT) tokens.
How to use
OAuth2 Access Token Modification Script), or edit the global script underRealms → Realm → Scripts.
Testing
Affected modules also compile cleanly: