> For the complete documentation index, see [llms.txt](https://docs.openiam.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.openiam.com/administration-guide/federation/oauth-and-oidc/oauth-and-oidc-reference.md).

# OAuth endpoints, grants, scopes and signing

Every OAuth and OIDC endpoint, the tenant metadata document, Client ID Metadata Documents, the resource parameter, the six grant flows against the wire grant\_type values, the client authentication and

## What this covers

The value catalogues behind [OAuth 2.0 and OpenID Connect](/administration-guide/federation/oauth-and-oidc.md): the endpoints OpenIAM exposes, what the **Authorization Grant Flow** drop-down offers and how those values relate to the `grant_type` a client puts on the wire, how the client presents its secret, what each signing algorithm publishes at the JWKS endpoint, and the scopes that ship. Scan it while you fill the client form.

## Every endpoint

All OAuth and OIDC paths are built from `org.openiam.oauth.path`, which is `${org.openiam.idp.context.path}/oauth2`, and `org.openiam.idp.context.path` defaults to `/idp`, so the concrete base is `/idp/oauth2`. Neither key has a row on **Administration → System Configuration** — both come from the deployment's own configuration, so a base other than `/idp/oauth2` is something only the deployment owner can confirm. The observable check is a discovery document: the URLs it publishes are built from the running values.

These are the values you hand the application team. Nothing here is a call you make to administer OpenIAM — the client itself is configured on **Access Control → Authentication Providers**.

| Endpoint                         | Concrete URL                                                                                                                                                                                                                                                                           |
| -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Authorization                    | `https://<OPENIAM_HOST>/idp/oauth2/authorize`                                                                                                                                                                                                                                          |
| Authorization (public app key)   | `https://<OPENIAM_HOST>/idp/oauth2/authorize/{application_key}`                                                                                                                                                                                                                        |
| Token                            | `https://<OPENIAM_HOST>/idp/oauth2/token`                                                                                                                                                                                                                                              |
| Token introspection              | `https://<OPENIAM_HOST>/idp/oauth2/token/info` — answers anonymous callers unless `org.openiam.oauth.token.info.public` is `false`, which makes it require OAuth client authentication exactly as at `/token` (2026.10.1 and later)                                                    |
| Token validation                 | `https://<OPENIAM_HOST>/idp/oauth2/token/validate`                                                                                                                                                                                                                                     |
| Token refresh, legacy            | `https://<OPENIAM_HOST>/idp/oauth2/token/refresh` — authenticates no client; answers only while `org.openiam.oauth.token.refresh.legacy.enabled` is `true` (2026.10.2 and later). Refresh through `POST /idp/oauth2/token` with `grant_type=refresh_token` instead                     |
| Revocation (RFC 7009)            | `https://<OPENIAM_HOST>/idp/oauth2/token/revoke`                                                                                                                                                                                                                                       |
| Revocation / OIDC logout         | `https://<OPENIAM_HOST>/idp/oauth2/revoke`                                                                                                                                                                                                                                             |
| UserInfo                         | `https://<OPENIAM_HOST>/idp/oauth2/userinfo`                                                                                                                                                                                                                                           |
| Implicit handler                 | `https://<OPENIAM_HOST>/idp/oauth2/handler`                                                                                                                                                                                                                                            |
| Discovery document, per client   | `https://<OPENIAM_HOST>/idp/oauth2/<JWT_ISSUER>/.well-known/openid-configuration`                                                                                                                                                                                                      |
| Metadata document, tenant        | `https://<OPENIAM_HOST>/idp/oauth2/.well-known/openid-configuration` and `https://<OPENIAM_HOST>/idp/oauth2/.well-known/oauth-authorization-server` — one document, independent of any client (2026.10.2 and later); see [The tenant metadata document](#the-tenant-metadata-document) |
| JWKS, tenant                     | `https://<OPENIAM_HOST>/idp/oauth2/.well-known/jwks` — an empty key set by design, because signing keys belong to individual clients (2026.10.2 and later)                                                                                                                             |
| JWKS                             | `https://<OPENIAM_HOST>/idp/oauth2/<JWT_ISSUER>/.well-known/jwks`                                                                                                                                                                                                                      |
| JWKS, deprecated per-client form | `https://<OPENIAM_HOST>/idp/oauth2/.well-known/jwks/<CLIENT_ID>` — deprecated, kept for back compatibility                                                                                                                                                                             |

The discovery document is the cheapest one to open in a browser, because it will not answer at all until the client row is live in the cache.

`<JWT_ISSUER>` is the value of the client's **JWT Issuer** attribute (`JwtIssue`), not the client ID. The issuer claim OpenIAM publishes is `https://<OPENIAM_HOST>` + `org.openiam.idp.openid.issuer.base.path` + the **JWT Issuer** value, and `org.openiam.idp.openid.issuer.base.path` is `${org.openiam.idp.context.path}/oauth2/`, so the issuer is `https://<OPENIAM_HOST>/idp/oauth2/<JWT_ISSUER>`.

{% hint style="info" %}
**The `jwks_uri` in the discovery document is correct from 2026.10.1.** Releases before 2026.10.1 prefixed the raw **JWT Issuer** name onto the already-absolute URL, so the published value looked like `<JWT_ISSUER>https://<OPENIAM_HOST>/idp/oauth2/<JWT_ISSUER>/.well-known/jwks` and a relying party that followed it blindly failed. If you run an earlier release, give the application team `https://<OPENIAM_HOST>/idp/oauth2/<JWT_ISSUER>/.well-known/jwks` explicitly. On 2026.10.1 and later the document can be used as-is: open it in a browser and confirm `jwks_uri` starts with `https://`.
{% endhint %}

## The tenant metadata document

From 2026.10.2 the IdP also publishes one authorization-server metadata document (RFC 8414) for the tenant as a whole, at both paths a client derives from an issuer that carries a path:

{% code overflow="wrap" %}

```
https://<OPENIAM_HOST>/idp/oauth2/.well-known/openid-configuration
https://<OPENIAM_HOST>/idp/oauth2/.well-known/oauth-authorization-server
```

{% endcode %}

It exists so a client that has never been registered, such as a Model Context Protocol (MCP) client that identifies itself by a [Client ID Metadata Document](#client-id-metadata-documents), can discover the endpoints and the server's capabilities before its first authorization request. It names no client and does not wait for the client sweep, so it answers as soon as the IdP is up.

| Member                                                                                 | Value                                                                                                                                                                                         |
| -------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `issuer`                                                                               | `https://<OPENIAM_HOST>/idp/oauth2`, derived from the request's scheme and host. Set `org.openiam.oauth.issuer` to publish one fixed value when the IdP is reachable under several host names |
| `authorization_endpoint`, `token_endpoint`, `userinfo_endpoint`, `revocation_endpoint` | the endpoints above, built on the same host as the issuer                                                                                                                                     |
| `jwks_uri`                                                                             | `<issuer>/.well-known/jwks`, an empty key set: signing keys belong to individual clients                                                                                                      |
| `scopes_supported`                                                                     | `openid` followed by the name of every `OAUTH_SCOPE` resource, re-read from the backend every `org.openiam.oauth.metadata.cache.seconds` (60) seconds                                         |
| `response_types_supported`                                                             | `code`                                                                                                                                                                                        |
| `grant_types_supported`                                                                | `authorization_code`, `refresh_token`                                                                                                                                                         |
| `token_endpoint_auth_methods_supported`                                                | `none`, `client_secret_basic`, `client_secret_post`, `private_key_jwt`                                                                                                                        |
| `code_challenge_methods_supported`                                                     | `S256`; `plain` is added only when `org.openiam.oauth.pkce.plain.allowed` is `true`                                                                                                           |
| `client_id_metadata_document_supported`                                                | `true` while `org.openiam.oauth.cimd.allowed.origins` lists at least one origin, otherwise `false`                                                                                            |
| `authorization_response_iss_parameter_supported`                                       | `true`                                                                                                                                                                                        |
| `subject_types_supported`, `id_token_signing_alg_values_supported`                     | `public`; `RS256`, `RS512`, `HS256`, `HS512`                                                                                                                                                  |

Claude uses metadata-document registration only when this document advertises both `client_id_metadata_document_supported: true` and `none` among the token-endpoint authentication methods, so those two members are the first thing to check when Claude asks for a client id instead.

The per-client discovery document is unchanged except for two members: `scopes_supported` lists scope names rather than the resource ids it emitted before 2026.10.2, and `code_challenge_methods_supported` is present.

One more path is an artefact rather than an endpoint. `org.openiam.oauth.refresh.url` holds `/idp/oauth2/refresh`, and nothing serves it. Do not pass that value on: `/idp/oauth2/token/refresh` is the one that answers.

## Grant flows and `grant_type` are two different lists

The client row stores **one** flow, in the `OAuthAuthorizationGrantFlow` attribute — the field labelled **Authorization Grant Flow**. The values ship as metadata rows grouped under `OAUTH_AUTH_GRANT`, which is why the drop-down is closed and why you cannot invent a flow. OpenIAM spells the concept "grand flow" in some of its own error text; that is not a typo you can fix.

The six stored values, with the label shown in the drop-down and the release each became available. If a value is missing from the drop-down, the deployment is on an earlier release than the one in the last column:

| Stored value              | Drop-down label                    | Since   |
| ------------------------- | ---------------------------------- | ------- |
| `AUTHORIZATION_CODE`      | Authorization Code                 | 4.0.0.0 |
| `AUTHORIZATION_CODE_PKCE` | Proof Key for Code Exchange (PKCE) | 4.2.1.0 |
| `IMPLICIT`                | Implicit                           | 4.0.0.0 |
| `RESOURCE_OWNER`          | Resource Owner                     | 4.0.0.0 |
| `CLIENT_CREDENTIALS`      | Client Credentials                 | 4.0.0.0 |
| `HL7`                     | Health Level Seven International   | 4.1.9.0 |

The `grant_type` string a client asks for is a *different* set of literals from the drop-down values, and the mapping is not one-to-one. Use this table when an application team tells you which grant they intend to use and you have to decide what to set on the row:

| The grant the application asks for             | Accepted when **Authorization Grant Flow** is                    |
| ---------------------------------------------- | ---------------------------------------------------------------- |
| `authorization_code`                           | `AUTHORIZATION_CODE` or `AUTHORIZATION_CODE_PKCE`                |
| `password`                                     | `RESOURCE_OWNER`                                                 |
| `client_credentials`                           | `CLIENT_CREDENTIALS` or `HL7`                                    |
| `urn:ietf:params:oauth:grant-type:device_code` | any — the flow is not checked                                    |
| `refresh_token`                                | anything except `IMPLICIT`, and **Use Refresh Token** must be on |

Anything else is rejected with `invalid_request` and the description `Client doesn't support <grant_type> grant_type`.

Notes that matter in practice:

* **Device code** has no stored flow value of its own, and no drop-down entry. Any client can ask for it, whatever you set on the row; OpenIAM resolves it against the device record, takes the user from it, and issues a token with refresh disabled however the client is configured.
* The device-code grant is also **not** subject to the entitlement check, so it can never fail with `insufficient_rights`. If you are relying on entitlement to fence off a client, this grant is a hole in it — see [Fence the client](/administration-guide/federation/oauth-and-oidc.md#fence-the-client-oauth-entitlement-enforcement-is-off-out-of-the-box).
* **Resource owner password** authenticates through the normal login provider, so account lockout applies: a locked login produces `invalid_request` / `Authentication fails for blocked login`.
* `client_credentials` on a client bound to an AI Agent non-human identity that owns a service user issues, from 2026.10.2, a token whose subject is that service user, with the client's scopes filtered by the service user's entitlements, and only while the identity is Active and unexpired: otherwise the request is refused with `invalid_client` and `The client's agent is pending approval`, `is suspended`, `is a draft`, `is decommissioned`, `has expired` or `has no service user` as the description. The binding is described in [Govern AI agents as non-human identities](/administration-guide/federation/mcp-server/mcp-agents.md). A client that is not bound still receives a token whose subject is the literal user ID `"0001"`, the built-in administrator, with every scope the client is configured with, while `org.openiam.oauth.client_credentials.legacy.subject` is `true`, and the IdP logs a warning once per client that does; 2026.10.2 ships it `true` and the next release ships it `false`, after which an unbound client is refused with `invalid_client` and `The client is not bound to an agent record`. No application should make an authorization decision on the `0001` subject.
* A client bound to an AI Agent identity that is not Active gets no token of any grant from 2026.10.2, with the same `invalid_client` descriptions, and the authorization endpoint sends its sign-ins back with `error=access_denied` and the description before any consent is shown.
* `AUTHORIZATION_CODE_PKCE` makes PKCE mandatory. A client library that does not implement it cannot use the row, and the only supported challenge methods are `S256` and `plain`. From 2026.10.2 a **public** client, one on this flow with no **Client Secret**, must use `S256`: a `plain` or missing `code_challenge_method` is refused with `invalid_request` at the authorization endpoint, and a code issued with a `plain` challenge is refused with `invalid_grant` at the token endpoint, unless `org.openiam.oauth.pkce.plain.allowed` is `true`. A client with a secret may keep using either.
* Authorization codes live five minutes, are single use, and the lifetime is not configurable.
* **A refresh token is single use** since 2026.10.1. Redeeming it returns the token it belongs to and removes that token in the same database transaction, so presenting the same refresh token a second time fails as an unknown refresh token; the client must keep the refresh token the `refresh_token` response carried. Before 2026.10.1 the removal never reached the database, so a client with **Multiple Active Tokens** allowed could redeem one refresh token as often as it liked while the token was still valid. From 2026.10.2 the `refresh_token` grant also checks that the token was issued to the client presenting it: a token issued to another client, and a token that is unknown or already redeemed, are refused with `invalid_grant`, and the refusal is the failure reason on the `GET_TOKEN` audit event.

## Client authentication

Two separate settings are involved and they are frequently conflated.

**How the shared secret is presented** — exactly two values, selected by the **OAuth Client** field and stored in `OAuthClientAuthType`, shipped as metadata rows grouped under `OAUTH_CLIENT_AUTH_TYPE`:

| Value          | Label                | How the application presents the secret             |
| -------------- | -------------------- | --------------------------------------------------- |
| `BASIC_HTTP`   | Basic Authentication | In the HTTP Basic authentication header             |
| `REQUEST_BODY` | Request body         | In the body of the request, alongside the client ID |

**Assertion-based authentication** — a separate axis, three values, selected by the **Client Assertion Type** field:

| Value            | `client_assertion_type` string                                                   |
| ---------------- | -------------------------------------------------------------------------------- |
| `JWT_BEARER`     | `urn:ietf:params:oauth:client-assertion-type:jwt-bearer`                         |
| `DEVICE_TOKEN`   | `org:openiam:params:oauth:client-assertion-type:x-openiam-firebase-device-token` |
| `DEFAULT_CLIENT` | `default-client`                                                                 |

What the client asks for wins: OpenIAM applies the assertion type the client names, falls back to the value stored on the row when the client names none, and treats anything it does not recognise — including `DEFAULT_CLIENT` — as ordinary client-secret authentication.

Under `AUTHORIZATION_CODE_PKCE` that ordinary path **skips secret validation entirely** when the client supplies no secret at all. That is deliberate — RFC 7636 public clients — but it means a PKCE row with a secret set does not force the client to send it. Confirm it on your own deployment before you treat **Client Secret** as a control: have the application team complete a sign-in with the secret omitted, and it still succeeds.

A client that has no secret at all on that flow is a **public client**, authenticating with the method RFC 7591 calls `none`; every client provisioned from a Client ID Metadata Document is one. From 2026.10.2 the tenant metadata document advertises `none` for that reason, and public clients are held to the `S256` rule above.

The discovery document advertises the following **unconditionally**, and the lists do not narrow to what the client is actually configured for — so a relying party that negotiates from the document alone may pick a method this client will reject. When an integration fails on the authentication method, compare the document against the **OAuth Client** field on the row rather than trusting the document:

* authentication methods at the token endpoint: `client_secret_post`, `client_secret_basic`, `private_key_jwt`
* authentication methods at the revocation endpoint: `private_key_jwt` only
* response types: `code`, `token`, `id_token`, `code id_token`, `token id_token`
* subject types: `public`

## Signing algorithms and JWKS

The client's **Signing Algorithm** field, stored in the `JwtAlgorithm` attribute, decides how the JWT is signed. Four values ship as metadata rows grouped under `OAUTH_SIGN_ALG`:

| Value   | Label                           | Since   |
| ------- | ------------------------------- | ------- |
| `HS256` | HMAC using SHA-256              | 4.1.6.0 |
| `HS512` | HMAC using SHA-512              | 4.0.0.0 |
| `RS256` | RS-256                          | 4.1.8.0 |
| `RS512` | RSASSA-PKCS-v1\_5 using SHA-512 | 4.0.0.0 |

What the JWKS endpoint publishes depends on which family you chose, and the difference is a security decision, not a formatting one:

* **HMAC (`HS256`, `HS512`)** — the only key an HS-signed client has is the shared secret, so from 2026.10.1 the JWKS URL publishes an **empty key set**, `{"keys":[]}`, for it. Only a party that already holds the secret can verify its tokens, which is what RFC 7518 intends for HMAC; use `RS256` or `RS512` for anything a third party validates. Releases before 2026.10.1 published the secret itself as an octet key. If a relying party was reading it from there, hand the secret over out of band or move the client to an RSA algorithm.
* **RSA (`RS256`, `RS512`)** — the published key is the RSA **public** key, identified by the provider's own ID in `kid`, with `use` set to `sig` and `alg` set to the configured algorithm. From 2026.10.1 no private member is published and nothing is written to the IdP log. Releases before 2026.10.1 included the private exponent in the key set and logged it at INFO level, so treat the JWKS URL and the IdP log of an earlier release as private material, and regenerate the client's key pair when you upgrade if either was ever reachable by a third party.
* **Anything else, or no algorithm set** — the endpoint fails instead of answering. The message is `OAuth provider '<client_id>' does not have a valid signing algorithm: <alg>`, or, when the provider or its algorithm is missing entirely, `OAuth provider does not exists or have a signing algorithm` .

The discovery document's two signing-algorithm lists each contain exactly the one algorithm configured on that client, which makes the document the fastest way to confirm **Signing Algorithm** saved.

## Client ID Metadata Documents

A Client ID Metadata Document is the JSON an OAuth client publishes at an `https` URL and then uses as its `client_id`. The MCP authorization specification of 2026-07-28 uses it in place of dynamic client registration, and Claude's hosted apps and Claude Code ship their own, for example `https://claude.ai/oauth/claude-code-client-metadata`. From 2026.10.2 the IdP accepts such a `client_id` on the authorization endpoint and registers the client itself, so you do not create a row by hand for every MCP client. The procedure is in [Let an MCP client register itself](/administration-guide/federation/oauth-and-oidc.md#let-an-mcp-client-register-itself); this section is what happens underneath.

On the first authorization request that carries an `https` URL as `client_id`:

1. The URL's origin (scheme and host) must be in `org.openiam.oauth.cimd.allowed.origins`, which ships as `https://claude.ai`. Any other origin is refused with `invalid_client`. An empty list disables the feature, and the tenant metadata document then advertises `client_id_metadata_document_supported: false`.
2. The document is fetched, under a guard: the URL must be `https` with no user information or fragment; the host is resolved first and every address must be public (loopback, private, link-local, carrier-grade NAT, multicast, reserved and the IPv6 equivalents are refused); the connection goes to the resolved addresses only; redirects are not followed; connecting, the socket and the response each time out after five seconds; the body is capped at 64 KB and must be `application/json`. The fetch happens on the authorization endpoint only, never on the token endpoint.
3. The document is validated: its `client_id` must equal the URL; it must list at least one `redirect_uris` entry, each an `https` URL or an `http` URL on `localhost`, `127.0.0.1` or `::1`; and `token_endpoint_auth_method`, when present, must be `none`.
4. An `OAUTH_CLIENT` authentication provider is created with the attributes below, and its resource is made public so the entitlement check does not refuse every user.

The document is cached per URL for the `Cache-Control: max-age` its origin sends, clamped to between 60 seconds and `org.openiam.oauth.cimd.cache.max.seconds` (86400); a failed fetch is remembered for 60 seconds. On later requests the client's name and redirect URIs are brought in line with the document when it changed, and a document that cannot be fetched leaves the client as registered.

| Attribute                     | Value                                                                                                                            |
| ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| Name                          | `<client_name> (<host>)` from the document, or the host alone, within 50 characters                                              |
| `OAuthClientID`               | the document URL                                                                                                                 |
| `OAuthAuthorizationGrantFlow` | `AUTHORIZATION_CODE_PKCE`                                                                                                        |
| `OAuthClientAuthType`         | `REQUEST_BODY`; no `OAuthClientSecret` is created                                                                                |
| `OAuthRedirectUrl`            | the document's `redirect_uris`                                                                                                   |
| `OAuthClientScopes`           | the ids of the scope names in `org.openiam.oauth.cimd.default.scopes`; a name the deployment does not have is skipped and logged |
| `OAuthUseRefreshToken`        | `true`                                                                                                                           |
| `OAuthClientSkipAllowDialog`  | `false`, so the consent dialog is shown                                                                                          |
| `IsMultiActiveTokenAllowed`   | `true`, so a user may hold one token per device                                                                                  |
| `OAuthTokenExpiration`        | `org.openiam.oauth.cimd.token.expiration.seconds` (3600) converted to minutes                                                    |
| `JwtAlgorithm`                | `RS256`                                                                                                                          |
| `JwtIssue`                    | a slug of the URL's host and path plus eight characters of its SHA-256, so the per-client discovery URL answers                  |
| Managed system                | the deployment's default managed system                                                                                          |

Two things differ for these clients on the authorization endpoint. A `redirect_uri` on a loopback host matches the registered entry whatever its port, because a native client such as Claude Code listens on a port chosen at run time; every other redirect URI is matched exactly as before. And the consent page names the host the document came from next to the client name, because the name is self-asserted, and warns when every registered redirect URI is local.

## The `resource` parameter

From 2026.10.2 both the authorization and the token endpoint accept the RFC 8707 `resource` parameter, which an MCP client sends to say which server the token is for. The resources the IdP knows are configured, not stored:

| Property                                | Shipped value                        | Meaning                                                                                   |
| --------------------------------------- | ------------------------------------ | ----------------------------------------------------------------------------------------- |
| `org.openiam.oauth.resources`           | `mcp`                                | the names of the configured resources, comma-separated                                    |
| `org.openiam.oauth.resource.mcp.url`    | empty                                | the resource's canonical URL; empty derives `https://<OPENIAM_HOST>/mcp` from the request |
| `org.openiam.oauth.resource.mcp.scopes` | the four `openiam:mcp:*:read` scopes | the scope names the resource accepts                                                      |

A `resource` that names none of the configured resources, compared with scheme and host case-insensitively and a trailing slash ignored, is refused with `invalid_target`. One that does narrows the authorization to that resource's scope names: the consent dialog asks only for them, and a request that would be left with no scope is refused with `invalid_scope`. The token endpoint validates the value the same way, and from the second part of 2026.10.2 it also compares it with the authorization's: the code stores the resource it was issued for, a code exchange that names a different one is refused with `invalid_target` and `The resource parameter differs from the one the authorization was granted for`, and one that names none inherits the code's. The token carries the resource as its audience, which the token-info endpoint reports as `audience`, and a refreshed token keeps the audience and the scope IDs of the token it replaces. The MCP server does not yet compare the audience with its own URL, as [the MCP reference](/administration-guide/federation/mcp-server/mcp-server-reference.md) notes under what 2026.10.2 does not do.

## Authorization responses: `iss` and encoded parameters

From 2026.10.2 every redirect back to the client, on success and on error, carries `iss=<issuer>` (RFC 9207), the issuer the tenant metadata document advertises, so a client can tell which authorization server answered. Every parameter on that redirect, `code`, `state`, `error`, `id_token` and the implicit-flow tokens, is `application/x-www-form-urlencoded` as RFC 6749 requires. Before 2026.10.2 the values were appended raw, so a `state` containing a space or an ampersand came back altered.

## The scopes OpenIAM ships

Scopes are resource rows of type `OAUTH_SCOPE`. A fully migrated deployment has **21** of them, arriving in three batches:

| Since   | Scope names                                                                                                                                                                                    |
| ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 4.0.0.0 | `name`, `given_name`, `family_name`, `middle_name`, `nickname`, `preferred_username`, `email`, `gender`, `birthdate`, `zoneinfo`, `locale`, `phone_number`, `address`, `updated_at`, `picture` |
| 4.1.5.0 | `user_name`                                                                                                                                                                                    |
| 4.2.1.0 | `profile`, `phone`, `phone_number_verified`, `email_verified`, `website`                                                                                                                       |

**`profile` is present**, since 4.2.1.0. Older published documentation lists only the fifteen 4.0 scopes; the scope picker on the client row is authoritative for your deployment, so open it and count. On MySQL the phone scope you will see is `phone_number`.

**`openid` is not a resource row.** OpenIAM advertises `openid` in the per-client discovery document unconditionally, on the assumption that every OAuth client supports it, then appends the client's own configured scopes, by name from 2026.10.2 and by resource id before. So the advertised list is always `openid` plus whatever is in the client's **Default Scopes**, never the full catalogue; the tenant metadata document is the one that lists every scope.

**What a requested `scope` is checked against**, since 2026.10.1. On an authorization request OpenIAM splits the `scope` parameter on spaces, ignores `openid`, and resolves every other name to a scope resource:

* A name that is not a scope resource — a typo, or a scope the deployment does not have — ends the request with `error=invalid_scope` on the redirect. Before 2026.10.1 such a request silently fell back to the client's **Default Scopes**, so a misspelt scope went unnoticed and the client received scopes it had not asked for.
* A scope that exists but is not in the client's **Default Scopes** is dropped from the request. If nothing is left — every requested scope is one the client was not registered for — the request also ends with `invalid_scope`. A token therefore never carries a scope the client was not configured with.
* A request with no `scope`, or with `openid` alone, still receives the client's **Default Scopes**, as before.

Whatever survives is then filtered by the user's entitlement to each scope resource, exactly as a request without `scope` is — see the note on public scope resources below.

Whether an ID token is issued is decided by what the *client asks for*, not by the client row. From 2026.10.2 the requested scopes are read space-separated on a refresh as well as on the initial sign-in, and a comma-separated list is still accepted on a refresh, so a client that followed the earlier advice keeps receiving its ID token. Before 2026.10.2 a refresh read the scopes comma-separated only, and a client that reused its space-separated sign-in string got no ID token back.

**Which scopes a refreshed token carries** follows the client's **Authorization Grant Flow**, from 2026.10.2. Under `AUTHORIZATION_CODE` and `AUTHORIZATION_CODE_PKCE` the refreshed token carries the scopes the user consented to on the authorization page and is still entitled to; a consented scope the user has since lost the entitlement to is dropped, and the consent to it is removed. `RESOURCE_OWNER`, `CLIENT_CREDENTIALS` and `HL7` clients never collect consent, so their refreshed tokens keep receiving the client's **Default Scopes** — every one of them for `CLIENT_CREDENTIALS` and `HL7`, and the ones the user is entitled to for `RESOURCE_OWNER`; a `CLIENT_CREDENTIALS` client bound to an AI Agent identity is created without refresh tokens, and its scopes are filtered by its service user's entitlements. From the second part of 2026.10.2 the scope IDs a token was issued with are recorded on the token, and a refresh returns exactly that recorded set; a token issued before the upgrade carries no recorded set and is refreshed by the rules above. Before 2026.10.2 every refresh returned the **Default Scopes** the user was entitled to whatever the flow, so a scope the user had declined on the consent page came back on the first refresh.

Every scope resource ships public, and was re-set public as recently as 4.2.1.0, so **per-scope entitlement is not enforced in the shipped state**. Check the public flag on the scope resource before you design around it.

## Properties added in 2026.10.2

All of these live in the IdP's properties file, `idp.ui.properties` under the deployment's `conf/properties` directory, next to `org.openiam.oauth.token.info.public`; none has a row on **Administration → System Configuration**. The shipped file carries every key with the value below.

| Property                                                                        | Shipped value                        | What it does                                                                                                                                                                                |
| ------------------------------------------------------------------------------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `org.openiam.oauth.issuer`                                                      | empty                                | the issuer the tenant metadata document advertises and `iss` carries; empty derives `https://<host>/idp/oauth2` from each request                                                           |
| `org.openiam.oauth.metadata.cache.seconds`                                      | `60`                                 | how long the tenant document holds the scope list before asking the backend again                                                                                                           |
| `org.openiam.oauth.cimd.allowed.origins`                                        | `https://claude.ai`                  | origins whose `https` client ids are accepted as Client ID Metadata Documents; empty disables them                                                                                          |
| `org.openiam.oauth.cimd.default.scopes`                                         | the four `openiam:mcp:*:read` scopes | scope names granted to a provisioned client                                                                                                                                                 |
| `org.openiam.oauth.cimd.token.expiration.seconds`                               | `3600`                               | access-token lifetime of a provisioned client                                                                                                                                               |
| `org.openiam.oauth.cimd.cache.max.seconds`                                      | `86400`                              | longest a fetched document is reused                                                                                                                                                        |
| `org.openiam.oauth.pkce.plain.allowed`                                          | `false`                              | whether a public client may use the `plain` PKCE transform                                                                                                                                  |
| `org.openiam.oauth.resources`                                                   | `mcp`                                | the configured resources for the `resource` parameter                                                                                                                                       |
| `org.openiam.oauth.resource.mcp.url`                                            | empty                                | the `mcp` resource's URL; empty derives `https://<host>/mcp`                                                                                                                                |
| `org.openiam.oauth.resource.mcp.scopes`                                         | the four `openiam:mcp:*:read` scopes | the scopes the `mcp` resource accepts                                                                                                                                                       |
| `org.openiam.oauth.token.refresh.legacy.enabled`                                | `true`                               | whether the legacy `/token/refresh` endpoint, which authenticates no client, still answers; set it to `false` once every client refreshes through `/token`                                  |
| `org.openiam.oauth.cimd.agent.owner`                                            | empty                                | the user ID that owns and sponsors the AI Agent record created for every client that registers itself; while empty, such a client is refused at registration and the log names the property |
| `org.openiam.oauth.cimd.agent.approval`                                         | `auto`                               | `auto` creates the record Active; `required` creates it Pending Approval, and the MCP server refuses the client until it is approved                                                        |
| `org.openiam.oauth.cimd.agent.expiry.days`                                      | `365`                                | the record's expiry, counted from registration                                                                                                                                              |
| `org.openiam.oauth.cimd.agent.environment`, `org.openiam.oauth.cimd.agent.risk` | `PROD`, `MEDIUM`                     | the record's environment and risk classification                                                                                                                                            |
| `org.openiam.oauth.client_credentials.legacy.subject`                           | `true`                               | whether a `client_credentials` client with no agent record still receives the `0001` subject; the next release ships `false`                                                                |

## Related

* [OAuth 2.0 and OpenID Connect](/administration-guide/federation/oauth-and-oidc.md) — the procedure these values configure
* [Govern AI agents as non-human identities](/administration-guide/federation/mcp-server/mcp-agents.md) — the agent record behind the `client_credentials` subject and the `cimd.agent` properties
* [Enumerated value reference](/administration-guide/reference/enums.md) — the OAuth constants as declared, alongside every other enumeration
* [System property reference](/administration-guide/reference/system-properties.md) — `org.openiam.authorization.oauth.disabled` and the rest of the `System` category

*Applies to OpenIAM 2026.10.2. Last reviewed 20 October 2026.*


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.openiam.com/administration-guide/federation/oauth-and-oidc/oauth-and-oidc-reference.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
