Clients
A client is an OAuth/OIDC registration under an issuer — the client_id, credentials, redirect URIs, and grant types your application uses to send users to the issuer and get tokens back. You register clients with create_client and manage them with the client tools; all mutations are owner only.
Confidential vs. public
The single most important choice is how the client authenticates at the token endpoint, set by token_endpoint_auth_method:
| Confidential | Public / PKCE | |
|---|---|---|
token_endpoint_auth_method | client_secret_basic or client_secret_post | none |
| Secret | Generated once by create_client, returned once | None |
| For | Servers that can keep a secret (web backends, M2M) | SPAs and native/mobile apps |
| Proof at token exchange | The client secret (plus PKCE) | PKCE only |
PKCE is required for every client, confidential or public — the issuer enforces it globally. A confidential client sends both its secret and the PKCE verifier; a public client sends only the verifier.
A confidential client's client_secret is returned exactly once — by create_client (for a confidential client) and by rotate_client_secret. It is never stored in plaintext, never logged, and never returned again, not even masked by get_client. Store it in your secret manager the moment you see it. Lost it? rotate_client_secret issues a new one — but the old one stops working immediately, so update your integration in the same change.
Redirect URIs
For the authorization-code flow, redirect_uris lists the exact callback URLs the issuer will return codes to. update_client_redirect_uris replaces the whole set — it is not additive, so any URI you omit is removed and stops being accepted immediately.
In production, redirect URIs must be https. http://localhost is accepted only in local development. This mirrors the issuer's own policy — a plain-http non-loopback URI is rejected.
Grant types
Choose the grant_types your client needs:
authorization_code— the interactive login flow (needsredirect_uris).refresh_token— long-lived sessions via refresh. Also requires theoffline_accessscope, and per OIDC Core that scope only yields a refresh token when the authorization request carriesprompt=consent.client_credentials— machine-to-machine, no user, no browser.
End-user self-signup
By default a client's login page only lets existing users sign in. To offer a "Create account" option, two flags must both be on:
- The client's
registration_enabledset totrue(viacreate_clientorupdate_client). - The issuer's
registrationModeset toopen(notinvite_only) — see Issuers.
Setting registration_enabled=true on a client is not enough on its own. If the issuer's registrationMode is invite_only, self-signup stays off everywhere regardless of the client flag. You need both.
Focused mutations
Beyond create_client/update_client/delete_client, each sensitive area has its own tool so a change is deliberate:
| Tool | Changes |
|---|---|
update_client_redirect_uris | The redirect-URI set (full replace) |
rotate_client_secret | Issues a new confidential secret (old one dies) |
toggle_client_passkeys | The passkey second-factor offer |
set_client_providers | Which federation providers this client shows |
update_client_token_ttls | id_token (60–14,400 s) and refresh_token (60–4,838,400 s) lifetimes |
apply_client_theme | A per-client theme override |
Token-TTL bounds mirror the issuer exactly — a value outside the range is rejected, not silently clamped.
Dynamic client registration
The OIDC dynamic-client-registration endpoint (RFC 7591 / 7592) exists only on the root issuer (login.auth.is), where MCP hosts self-register to connect. It is deliberately limited:
- Rate-limited to 5 registrations per hour per IP.
- Privileged properties (
first_party,passkeys_enabled,enabled_providers,resource_scopes,registration_enabled) are stripped, not honored — a DCR client can never self-elevate. - In production,
http/loopback redirect URIs are rejected.
Your own tenant issuers do not expose DCR at all. Register your applications with the owner-only create_client tool, which has none of these restrictions.
DCR is for the MCP connect flow, not for provisioning your app's clients. If a script hits POST /oidc/reg in a loop it will get a 429 after five registrations in an hour.