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_uri when 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 use jwks_uri to use it instead of inline jwks. See client metadata.
  • Inline jwks is 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

  1. Select local private keys and expected algorithms from trusted application configuration. Never accept key paths or URLs from the response.
  2. Authenticate and decrypt the five-part compact JWE. Check its protected algorithm, content-encryption algorithm, recipient key ID and nested-JWT type.
  3. Verify the inner signed JWT using the provider's trusted signing-key contract.
  4. 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.