Include acr and amr claims in stateless JWT access tokens#1033
Merged
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
maximthomas
approved these changes
Jun 3, 2026
acr and amr claims in stateless JWT access tokens (Discussion #1027)acr and amr claims in stateless JWT access tokens
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 adds the authentication context class reference (
acr) and theauthentication modules (
authModules, i.e. theamrvalue) to the statelessJWT access token.
Until now these claims were only available in the
id_token, the/oauth2/userinfoendpoint and the/oauth2/tokeninfoendpoint. The statelessaccess token was assembled in
StatelessTokenStore.createAccessToken(...)froma hard-coded claim set with no extension point, so neither the OIDC Claims
Script nor a custom Scope Implementation Class could influence it. As a result,
PEPs / API gateways that need
acr/amrwere forced to make an extra/oauth2/tokeninforound-trip per request.With this change the claims are written directly into the access token payload,
exactly the same way they are already written into the refresh token in
createRefreshToken(...).Motivation
acr/amrfor the refreshtoken but not for the access token — an inconsistency inherited from the
original ForgeRock code.
/oauth2/tokeninfofor downstreamservices that authorize on
acr/amr.Changes
OAuth2 (server)
StatelessTokenStore.createAccessToken(...):JwtClaimsSetBuilder, extractacr(
getAuthenticationContextClassReference()) andauthModules(
getAuthModules()) from:AuthorizationCodepresent in the request (authorization_code grant), andRefreshTokenpresent in the request (refresh_token grant),which overrides the authorization-code values when present.
acrandauthModulesclaims are added only when a value isavailable, keeping the change additive and backward-compatible.
authCodelookup (already read for therealm_access.roleslogic); no new imports required —ACRandAUTH_MODULESconstants were already imported.Documentation
admin-guide/chap-openid-connect.adoc: the decoded stateless access tokenexample now shows the
acrandauthModulesclaims, with aNOTEexplainingthat they appear only when the originating authorization code / refresh token
carries them (e.g. omitted for the
client_credentialsgrant).Tests
StatelessTokenStoreTest(new tests):whenAuthorizationCodePresentAcrAndAmrGetAddedToAccessTokenwhenRefreshTokenPresentAcrAndAmrGetAddedToAccessTokenwhenRefreshTokenPresentItOverridesAuthorizationCodeAcrAndAmrwhenNoAcrOrAmrAvailableTheyAreNotAddedToAccessTokengivenBaseProviderSettings().Behavioral / compatibility notes
acr/authModulesis available (for example, theclient_credentialsgrant), theclaims are simply omitted — existing tokens are unaffected.
acr,authModules) matchcreateRefreshToken(...).How to test
Result:
Tests run: 6, Failures: 0, Errors: 0, Skipped: 0.Manual:
authorization_codegrant afterauthenticating through a chain that yields an
acr/ auth modules./oauth2/tokeninfo) and confirm theacrandauthModulesclaims are present in the payload.acr/authModulesare preserved from therefresh token.
Related