---
title: "OIDC endpoints"
description: "The OpenID Provider endpoints each directory serves at https://<alias>.sso.ezgh.app, and the parameters they take."
---

> Documentation Index
> Fetch the complete documentation index at: https://docs.ezghcloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# OIDC endpoints

Each directory is an OpenID Provider (OpenID Connect Core 1.0, OAuth 2.0) with its own issuer,
`https://<alias>.sso.ezgh.app`, its own signing keys and its own clients. These endpoints are
defined by the standards, so they aren't part of the [management API reference](/sso-api). They
never accept EZGH Cloud API keys, logins or console sessions.

| Endpoint | Path | Methods |
| --- | --- | --- |
| [Discovery](#discovery) | `/.well-known/openid-configuration`, `/.well-known/oauth-authorization-server` | GET |
| [JWKS](#jwks) | `/jwks` | GET |
| [Authorization](#authorization) | `/oauth2/authorize` | GET, POST |
| [Token](#token) | `/oauth2/token` | POST |
| [Userinfo](#userinfo) | `/oauth2/userinfo` | GET, POST |
| [Introspection](#introspection) | `/oauth2/introspect` | POST |
| [Revocation](#revocation) | `/oauth2/revoke` | POST |
| [Logout](#logout) | `/oauth2/logout` | GET, POST |
| [Registration](#registration) | `/oauth2/register` | POST |

Discovery, JWKS, token, userinfo, introspection and revocation answer CORS requests from any
origin, without credentials, so single-page apps can call them.

## Discovery

`GET /.well-known/openid-configuration` (also at `/.well-known/oauth-authorization-server`, the
same document) describes the provider:

| Field | Value |
| --- | --- |
| `issuer` | `https://<alias>.sso.ezgh.app` |
| `response_types_supported` | `code` |
| `response_modes_supported` | `query` |
| `grant_types_supported` | `authorization_code`, `refresh_token` |
| `code_challenge_methods_supported` | `S256` |
| `scopes_supported` | `openid`, `profile`, `email`, `groups`, `offline_access`, `mcpcentral:connect` |
| `subject_types_supported` | `public` |
| `id_token_signing_alg_values_supported` | `RS256`, `ES256` |
| `token_endpoint_auth_methods_supported` | `client_secret_basic`, `client_secret_post`, `private_key_jwt`, `none` |
| `acr_values_supported` | `urn:ezgh:sso:acr:mfa`, `urn:ezgh:sso:acr:sfa` |
| `authorization_response_iss_parameter_supported` | `true` |
| `backchannel_logout_supported`, `backchannel_logout_session_supported` | `true` |
| `request_parameter_supported`, `request_uri_parameter_supported` | `false` |
| `client_id_metadata_document_supported` | `true` |

## JWKS

`GET /jwks` is the directory's public signing keys: an RSA key (RS256) and a P-256 key (ES256),
each with a `kid`. Keys rotate every 90 days; a new key appears 7 days before it signs, and an old
key stays 7 days after it stops. Fetch keys by `kid` when you see one you don't have.

## Authorization

`/oauth2/authorize` starts a sign-in. Parameters:

| Parameter | Notes |
| --- | --- |
| `response_type` | `code` only. |
| `client_id`, `redirect_uri` | `redirect_uri` must exactly match one the application registered (a native app's loopback IP may use any port). |
| `scope` | Space-separated; include `openid` for an ID token. Scopes the provider doesn't support are left out. |
| `state`, `nonce` | Returned in the redirect, and in the ID token (`nonce`). |
| `code_challenge`, `code_challenge_method` | PKCE with `S256`. Required for `spa` and `native` applications. |
| `prompt` | `none` (fails with `login_required` instead of showing a page), `login` (sign in again), `select_account` (the same as `login`: a browser has one session), `consent` (satisfied by the assignment). |
| `max_age` | Sign in again if the session's sign-in is older. |
| `id_token_hint` | If it names another user than the signed-in one, they sign in again. |
| `response_mode` | `query` only. |

Not supported: `request` and `request_uri` (`request_not_supported`,
`request_uri_not_supported`), the `claims` parameter, other response types and modes.

- Every authorization response, including errors, carries `iss` (RFC 9207). Check it.
- There's no consent screen for your applications: assigning a user is your consent.
- Without a session the person signs in at the portal first, under the directory's
  [policy](/sso/directory#sign-in-policy), and comes back.
- A person who isn't [assigned](/sso/assignments) sees an error page; with `prompt=none` the app
  gets `access_denied`. An unknown client or an unregistered redirect URI shows an error page too,
  and never redirects.

## Token

`POST /oauth2/token`, form-encoded. Grants: `authorization_code` (with `code`, `redirect_uri`,
and `code_verifier` when PKCE was used) and `refresh_token` (with `refresh_token`, and optionally
a narrower `scope`).

Client authentication, by the application's `tokenEndpointAuthMethod`: HTTP Basic with the client
ID and secret (`client_secret_basic`), `client_id` and `client_secret` in the form
(`client_secret_post`; a secret client may use either), a `client_assertion` JWT
(`private_key_jwt`), or `client_id` alone (public clients).

The answer has `access_token`, `token_type` (`Bearer`), `expires_in`, `scope`, `id_token` (with
`openid`) and `refresh_token` (with `offline_access`). Errors are OAuth's: `invalid_grant` for a
used, expired or revoked code or refresh token, or a user who is no longer active or assigned;
`invalid_client` for failed client authentication.

## Userinfo

`GET` or `POST /oauth2/userinfo` with `Authorization: Bearer <access token>` returns the claims
the token's scopes grant (see [Scopes and claims](/sso/applications#scopes-and-claims)). A token
that isn't active is `401` with `WWW-Authenticate: Bearer error="invalid_token"`; a token without
`openid` is `403`.

## Introspection

`POST /oauth2/introspect` (RFC 7662), with `token`, for confidential (`web`) applications,
authenticated as at the token endpoint. An application can introspect its own refresh tokens, and
access tokens issued to it or for it: an API registered as its own application, named in the
token's `aud` through the issuing application's `accessTokenAudience`. The answer is
`{ "active": false }` for anything revoked, expired, belonging to a suspended or deleted user, or
not the caller's.

## Revocation

`POST /oauth2/revoke` (RFC 7009), with `token`, authenticated as at the token endpoint (public
clients send `client_id`). Revoking a refresh token ends its whole grant; revoking an access token
ends that token.

## Logout

`/oauth2/logout` (OpenID Connect RP-Initiated Logout 1.0) ends the person's portal session.

| Parameter | Notes |
| --- | --- |
| `id_token_hint` | An ID token this directory issued (expired is fine). With it, the session ends at once. Without it, a signed-in person is asked to confirm. |
| `client_id` | Must be in the hint's `aud`, when both are sent. |
| `post_logout_redirect_uri` | Followed only when the client registered it exactly in `postLogoutRedirectUris`. |
| `state` | Returned with the redirect. |

Applications with a `backchannelLogoutUri` are told through
[back-channel logout](/sso/applications#logout).

## Registration

`POST /oauth2/register` (RFC 7591) registers a public client for EZGH resource tokens, the way MCP
clients sign in to EZGH products. Clients registered here can't sign in to your applications and
get no ID tokens. See [EZGH apps and MCP clients](/sso/ezgh-apps#mcp-clients).

Source: https://docs.ezghcloud.com/sso/oidc-endpoints/index.mdx
