Encrypt ID tokens, JARM and UserInfo
M7 signs each selected response, then encrypts the signed JWT for your application's registered public RSA key. Your application retains the private decryption key. The supported profile is RSA-OAEP-256 with A256GCM; signing algorithms are configured independently. Access, refresh and session token formats and their validation requirements remain unchanged.
This feature is deployed and advertised. On 2026-09-16, a dedicated PHP receiver
passed live encrypted ID-token login, form_post.jwt JARM, encrypted UserInfo,
strict refresh and authorization cancellation against the production provider.
That campaign used inline JWKS and RS256 signing. Remote JWKS, rotation and
other encrypted transports require acceptance in the consuming application.
Register the public key and response policies
Use Dynamic Registration with
exactly one standard public jwks or jwks_uri source. For an existing app,
use the application management API
or application registration settings.
Management keeps response-encryption keys separate from authentication and JAR keys.
| Response | Algorithm field | Content-encryption field | Delivery |
|---|---|---|---|
| ID token | id_token_encrypted_response_alg |
id_token_encrypted_response_enc |
Compact JWE in id_token after authorization-code, refresh or device issuance |
| JARM | authorization_encrypted_response_alg |
authorization_encrypted_response_enc |
Compact JWE in the sole response field for query.jwt, form_post.jwt, fragment.jwt or jwt |
| Signed UserInfo | userinfo_encrypted_response_alg |
userinfo_encrypted_response_enc |
Compact JWE with Content-Type: application/jwt and no-store headers |
Both fields absent leaves that artifact's encryption off. Set the pair explicitly
to RSA-OAEP-256 and A256GCM to require it. Partial or unsupported pairs fail;
there is no implicit content-encryption default in this profile. UserInfo also
requires userinfo_signed_response_alg=RS256 or RS512. JARM requires an explicit
registered JWT response mode; a plain mode or omitted response_mode is rejected.
For example, add this metadata to a complete Dynamic Registration request:
{
"jwks_uri": "https://app.example.com/.well-known/encryption-jwks.json",
"id_token_encrypted_response_alg": "RSA-OAEP-256",
"id_token_encrypted_response_enc": "A256GCM",
"authorization_encrypted_response_alg": "RSA-OAEP-256",
"authorization_encrypted_response_enc": "A256GCM",
"userinfo_signed_response_alg": "RS256",
"userinfo_encrypted_response_alg": "RSA-OAEP-256",
"userinfo_encrypted_response_enc": "A256GCM",
"response_modes": ["form_post.jwt"]
}
The registered policy is authoritative. Authorization, token and UserInfo requests
cannot replace recipient keys, change algorithms or disable required encryption.
Register only public keys; encryption does not change how the client authenticates.
Every recipient, including a public client registered with none, must control
and protect the matching private decryption key. Registering an encryption key
does not establish confidential-client authentication. Do not embed an
application-wide private key in browser assets or distributed native binaries.
Browser/native recipient provisioning and custody require their own acceptance;
the tested receiver is server-side PHP.
The PHP web session/profile bridge requires confidential client authentication
for its introspection calls; a public client with none cannot use that path.
Choose a key source
- Use
jwks_uriwhen your application can operate a reliable public HTTPS key endpoint. It lets you publish replacement public keys at the registered URL. OpenID Connect registration directs clients that can usejwks_urito use it instead of inlinejwks. See client metadata. - Inline
jwksis supported when hosting that endpoint is unavailable. Replacing its keys requires an explicit registration update through app management. The live inline-key fixture establishes coverage, not a product-wide default.
For a remote source, endpoint availability becomes an issuance dependency: M7 fetches it for each issuance and fails closed on retrieval or validation failure. Test the hosted endpoint, key transition and outage behavior before requiring encryption. Both source types need deliberate key-ID selection and retained private keys, as described below.
Key requirements and rotation
A JWKS contains 1–32 keys and is limited to 64 KiB. Encryption recipients use
ordinary RSA public keys with 2048–8192-bit moduli and unique, nonempty kid
values. Public n and e use canonical unpadded base64url integers. Optional
use must be enc, alg must be RSA-OAEP-256, and key_ops may contain
encrypt and/or wrapKey. Private or symmetric material is rejected, including
in keys that would not be selected. Duplicate IDs are rejected across the set.
For supported combined authentication/encryption registration, mark signing keys
use=sig and recipients use=enc; each subset must satisfy its own registration
rules. A signing key is never an implicit encryption fallback. Every encryption
candidate must be valid; a set with no usable recipient fails.
Among eligible recipients, the bytewise lexicographically smallest kid wins,
regardless of array order. Choose IDs deliberately when changing the preferred
recipient. Retain old private keys until all responses and outstanding
authorization/cancellation transactions have expired. Outstanding cancellation
transactions may retain their previous policy and source for up to ten minutes.
Remote JWKS must use a public DNS HTTPS hostname on port 443, without credentials, fragments, IP literals or local names. Registration checks URL syntax; issuance fetches and validates the actual key set. Fetches require verified TLS and public destination addresses, disallow redirects, and have a 3-second connection and 5-second total timeout. Only successful JSON/JWKS responses are accepted. Remote keys are fetched for each issuance; an unavailable host causes issuance failure.
Receive and validate
- Select local private keys and expected algorithms from trusted application configuration. Never accept key paths or URLs from the response.
- Authenticate and decrypt the five-part compact JWE. Check its protected algorithm, content-encryption algorithm, recipient key ID and nested-JWT type.
- Verify the inner signed JWT using the provider's trusted signing-key contract.
- Validate issuer, audience, time and artifact-specific claims before acting: saved state for JARM, saved nonce for the ID token, and the established subject for UserInfo. Decryption alone does not authenticate the identity.
Use a receiving library that supports this nested profile. The PHP SDK encryption guide distinguishes tested candidates from stable downloadable versions and explains its optional native runtime. A client that only parses three-part JWTs must be upgraded before encryption is required.
Confidentiality boundary
Encrypted JARM conceals the code, state and error fields carried through the
browser from parties that lack the recipient key. form_post.jwt carries the
response in a POST body rather than a callback URL; ciphertext and POST bodies
can still be captured by diagnostics. Continue to exclude responses from logs.
In the supported authorization-code flow, ID tokens arrive from the token
endpoint and UserInfo is fetched separately; those are distinct from the
front-channel JARM response.
Keep HTTPS, PKCE, redirect validation and all signature/claim checks enabled. Encryption does not protect plaintext after the recipient decrypts it or compensate for a compromised receiving application. Inbound encrypted Request Objects remain unsupported. No OpenID or FAPI conformance claim follows from this feature or its scoped live tests; profile validation and certification remain separate.
Failure behavior and size limits
Invalid policy, unusable recipients, unsafe/unavailable JWKS and encryption failures fail closed. Required encryption never retries as readable JWS, JSON UserInfo or a plaintext authorization redirect. Encrypted JARM carries no plaintext code, state or error siblings; success and eligible cancellation/error results are handled only after the envelope and normal JARM validation pass. If safe delivery is impossible, the failure stays on M7.
SSO limits encrypted JARM to 32 KiB, compared with 16 KiB for signed-only JARM.
Encrypted ID tokens and UserInfo are limited to 256 KiB. Receivers, proxies and
browsers can impose lower limits. Prefer form_post.jwt for larger authorization
responses and check the receiver's complete transport budget.
Application acceptance
Enable one artifact at a time on the provider and receiver, then exercise all three together. Check login, refresh, UserInfo subject binding and cancellation. For the paths your app uses, also test wrong keys, tampering, plaintext downgrade, rotation, remote-key outages, other JWT response modes and device/ACK lifecycles. Retain only safe status evidence; never log tokens, decrypted claims or private keys. The scoped production campaign above does not certify these additional application-specific cases or OpenID conformance.