---
title: Advanced OIDC and capability reference
description: Use signed requests, signed UserInfo, Form Post, and native OIDC callbacks.
editUrl: true
head: []
template: doc
sidebar:
  order: 5
  hidden: false
  attrs: {}
pagefind: true
draft: false
---

Start with [Code + PKCE](/en-us/oidc/flow/). Use this reference when your RP needs another response type, signed messages, or native callbacks. Read deployed discovery before configuring a library.

## Response types and grants

| `response_type` | Authorization response | Required grants | Front-channel ID Token hashes | Refresh capability |
|---|---|---|---|---|
| `code` | Code | `authorization_code` | No front-channel ID Token | With `refresh_token` and consented `offline_access` |
| `id_token` | ID Token with permitted profile claims | `implicit` | Neither | None |
| `id_token token` | ID Token and access token | `implicit` | `at_hash` | None |
| `code id_token` | Code and ID Token | `authorization_code`, `implicit` | `c_hash` | With `refresh_token` and consented `offline_access` |
| `code token` | Code and access token | `authorization_code`, `implicit` | No front-channel ID Token | None |
| `code id_token token` | Code, ID Token, and access token | `authorization_code`, `implicit` | `c_hash`, `at_hash` | None |

Enable every selected type explicitly. `code` defaults to `query`; Implicit and Hybrid default to `fragment`. All six support `form_post`. Implicit and Hybrid reject `query`. Every front-channel ID Token requires a nonempty request `nonce`.

Public code-bearing flows require PKCE S256. Confidential code-bearing flows should use it. Pure Implicit ignores PKCE. Pure Implicit and access-token-bearing Hybrid remove `offline_access` before consent. Token-endpoint ID Tokens carry `at_hash` for the paired access token and omit `c_hash`.

## Authorization parameters

| Parameter | Use |
|---|---|
| `client_id`, `response_type`, `scope` | Identify the client and selected flow. Scope must contain `openid`. |
| `redirect_uri` | Exact registered callback. |
| `state` | Bind the callback to your authorization transaction. |
| `nonce` | Bind an ID Token to the request; mandatory for front-channel ID Tokens. |
| `code_challenge`, `code_challenge_method` | PKCE binding for code-bearing flows. Only S256 is supported. |
| `response_mode` | Select `query`, `fragment`, or `form_post` within the flow's restrictions. |
| `prompt`, `max_age` | Control interaction and proof freshness. See [interaction behavior](/en-us/oidc/flow/#choose-the-interaction-behavior). |
| `acr_values` | Space-separated authentication context preferences. Verify the returned `acr` before accepting a sensitive action. |
| `id_token_hint` | Supply a valid ID Token for this client. It does not by itself establish a remembered login. |
| `ui_locales` | Preferred interface languages; `en-US` and `zh-CN` are supported. |
| `request`, `request_uri` | Select one signed Request Object transport. |

`default_max_age` and `default_acr_values` apply when the corresponding request parameter is absent. `require_auth_time` requests `auth_time` in ID Tokens. Managed rules use the camelCase equivalents documented in [ReturnRules](/en-us/application-rules/return-rules/).

## Signed Request Object

Configure `request_object_signing_alg="RS256"` for dynamic registration, or `requestObjectSigningAlg="RS256"` in a managed OIDC rule. Configure the selected [RP key source](/en-us/oidc/relying-party-keys/). The example below uses the private key generated in that guide.

Save this as `signed-request.mjs`. Set `CLIENT_ID` and `REDIRECT_URI` to your registered values. `ISSUER` defaults to the production issuer.

```js
import { createHash, randomBytes, sign } from 'node:crypto';
import { readFileSync, writeFileSync } from 'node:fs';

const issuer = process.env.ISSUER ?? 'https://oidc.sudomimus.com';
const clientId = process.env.CLIENT_ID;
const redirectUri = process.env.REDIRECT_URI;
if (!clientId || !redirectUri) throw new Error('Set CLIENT_ID and REDIRECT_URI');
const now = Math.floor(Date.now() / 1000);
const verifier = randomBytes(32).toString('base64url');
const state = randomBytes(32).toString('base64url');
const nonce = randomBytes(32).toString('base64url');
const encode = value => Buffer.from(JSON.stringify(value)).toString('base64url');
const header = { alg: 'RS256', kid: 'rp-signing-1', typ: 'JWT' };
const payload = {
  iss: clientId,
  aud: issuer,
  iat: now,
  exp: now + 120,
  client_id: clientId,
  response_type: 'code',
  scope: 'openid email profile',
  redirect_uri: redirectUri,
  state,
  nonce,
  code_challenge: createHash('sha256').update(verifier).digest('base64url'),
  code_challenge_method: 'S256',
};
const input = `${encode(header)}.${encode(payload)}`;
const signature = sign('RSA-SHA256', Buffer.from(input),
  readFileSync('rp-private.pem')).toString('base64url');
const request = `${input}.${signature}`;
writeFileSync('pending-authorization.json', JSON.stringify({
  issuer, clientId, redirectUri, state, nonce, verifier,
}), { mode: 0o600, flag: 'wx' });
writeFileSync('request.jwt', request, { mode: 0o600, flag: 'wx' });
const url = new URL(`${issuer}/authorize`);
url.search = new URLSearchParams({
  client_id: clientId,
  response_type: 'code',
  scope: 'openid email profile',
  request,
}).toString();
console.log(url.toString());
```

Send the user's browser to the printed URL. Keep the pending transaction on the backend. On return, validate `state` and `iss`, exchange the code with the saved verifier, and validate the ID Token nonce. The files illustrate one transaction; a deployed RP needs a store that isolates concurrent transactions and removes expired entries.

For `request_uri`, serve the compact contents of `request.jwt` from a public HTTPS URL. Send outer `client_id`, `response_type`, and `scope` containing `openid`, plus `request_uri`, instead of `request`. A nonempty registered `request_uris` allowance restricts references; URI fragments do not change the fetched reference.

Signed inner values override other outer parameters. Inner `client_id` and `response_type`, when supplied, must agree with the outer values. If supplied, `iss` must identify the client and `aud` must include the issuer. Time claims are validated. Unsigned, encrypted, and nested Request Objects are rejected. Do not reuse the token-endpoint client assertion as a Request Object: their audiences and contents differ.

## Signed UserInfo

Set `userinfo_signed_response_alg="RS256"` in dynamic registration, or `userinfoSignedResponseAlg="RS256"` in the managed rule. Then call the discovered `userinfo_endpoint`:

```bash
curl "$USERINFO_ENDPOINT" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
```

The response is `application/jwt`, not JSON. Verify RS256 with the provider's discovered `jwks_uri`, select the exact `kid`, and validate `iss`, `aud`, and expiry. Match `sub` to the ID Token subject before using profile claims. Do not use the application's access-token JWKS to verify signed UserInfo.

The signed response reflects current scope, policy, and consent at issuance. It is a snapshot; consent revocation cannot erase an already delivered JWT. An `Accept` header does not override the client's registered response format.

## Form Post callback

Add `response_mode=form_post` to your normal authorization request. For example:

```text
https://oidc.sudomimus.com/authorize
  ?client_id=example-rp
  &redirect_uri=https%3A%2F%2Fapp.example.com%2Foidc%2Fcallback
  &response_type=code
  &response_mode=form_post
  &scope=openid
  &state=<transaction state>
  &nonce=<transaction nonce>
  &code_challenge=<S256 challenge>
  &code_challenge_method=S256
```

Your HTTP(S) callback receives a form POST such as:

```http
POST /oidc/callback HTTP/1.1
Host: app.example.com
Content-Type: application/x-www-form-urlencoded

code=<authorization-code>&state=<transaction-state>&iss=https%3A%2F%2Foidc.sudomimus.com
```

Parse the form, then validate the saved transaction's `state` and `iss`. Exchange the code using the original redirect URI and verifier. For Implicit or Hybrid, validate returned ID Tokens, nonce, and applicable hashes before accepting tokens.

If a cookie identifies the pending transaction, it must support cross-site POST with `SameSite=None; Secure`. Do not exempt the callback from transaction validation. Native custom schemes cannot receive Form Post.

## Native OIDC callbacks

Configure a managed native client with a rule such as:

```json
{
  "returnMethod": "OIDC",
  "payload": {
    "applicationType": "native",
    "redirectUris": ["com.example.app:/oidc/callback", "http://127.0.0.1:49152/oidc/callback"],
    "postLogoutRedirectUris": [],
    "allowedResponseTypes": ["code"],
    "allowedGrantTypes": ["authorization_code"],
    "allowedScopes": ["openid", "email", "profile"],
    "tokenEndpointAuthMethod": "none"
  }
}
```

Start the system browser with Code + PKCE. Receive the response through the registered custom scheme or a loopback listener. Validate `state` and `iss`, then exchange the code with the same exact redirect URI and verifier. Do not embed a client secret or platform private key in the installed application.

Native HTTP redirects must use localhost or a loopback address. HTTPS redirects are rejected for `application_type=native`. Exact matching includes the port; the example does not permit an arbitrary loopback port. Dynamic registration of a custom scheme with no host also requires a sector document for approved placement. See [registration metadata](/en-us/oidc/dynamic-registration/#metadata-rules).

## Capability limits

The provider supports pairwise subjects, RS256 signing, and the response types above. It does not support OAuth-only `response_type=token`, anonymous dynamic registration, RAT-authorized metadata updates, or encrypted ID Tokens, UserInfo, and Request Objects.

Third-party initiation uses the RP's registered `initiate_login_uri`. The RP accepts GET and POST, validates `iss`, and starts its normal authorization transaction. Issuer-host WebFinger is available at `/.well-known/webfinger`; use it for issuer discovery rather than assuming an email-domain mapping.

## Related

- [OIDC flow](/en-us/oidc/flow/)
- [Dynamic registration](/en-us/oidc/dynamic-registration/)
- [RP keys](/en-us/oidc/relying-party-keys/)
- [Troubleshooting](/en-us/oidc/troubleshooting/)