Blog · Guides

OIDC discovery: the document every OIDC client reads first

How OpenID Connect discovery works, the issuer rules that break integrations, the fields clients actually use, and how to debug a discovery error.

Casdoor Team · November 3, 2026 · 5 min read

This guide is written by the team that builds Casdoor and describes the standards as published (listed under Sources), with notes on how Casdoor implements them as of November 3, 2026. Spotted a mistake? Tell us.

OIDC discovery is the document a client fetches before it knows anything else about an OpenID Provider. From one URL, the client learns where to send the user, where to exchange the code, where to get the signing keys, which algorithms and grant types are supported, and which optional features the server implements.

In most integrations, a single wrong value in this document produces an error that looks unrelated: "issuer mismatch", "invalid signature", "unsupported response type", or a login loop. Knowing what the document should contain makes those errors much faster to diagnose.

The URL

The standard location is:

{issuer}/.well-known/openid-configuration

For https://id.example.com, that is https://id.example.com/.well-known/openid-configuration.

Two details trip people up:

  • A path-based issuer is not simple concatenation. OpenID Connect Discovery says to append the suffix to the issuer. RFC 8414, the OAuth metadata specification, says to insert /.well-known/openid-configuration between the host and the path. A server at https://example.com/tenant may therefore answer at either /tenant/.well-known/openid-configuration or /.well-known/openid-configuration/tenant. If one form 404s, try the other, and check the server's documentation.
  • The document must be JSON, served directly. A redirect or an HTML page returned with status 200 is not a discovery document. SPAs and reverse proxies that rewrite unknown paths to index.html are a common cause of "the client says the provider is not OIDC".

Required fields

OpenID Connect Discovery lists these as required:

  • issuer — the issuer identifier, an HTTPS URL with no query or fragment.
  • authorization_endpoint — where the browser is sent to authenticate.
  • token_endpoint — where the code or refresh token is exchanged.
  • jwks_uri — where the signing keys are published.
  • response_types_supported — for example code, or code id_token.
  • subject_types_supported — almost always public.
  • id_token_signing_alg_values_supported — for example RS256, ES256.

A document missing any of these is invalid, even if one particular client happens to tolerate the gap.

Fields clients actually rely on

Beyond the required set, these decide whether a feature works:

Field What it controls
userinfo_endpoint Where the client fetches profile claims
scopes_supported Which scopes the client may request
claims_supported Which claims the provider may return
grant_types_supported Whether device code, client credentials, token exchange and refresh tokens are available
token_endpoint_auth_methods_supported Whether the client may use client_secret_basic, client_secret_post, private_key_jwt or none
code_challenge_methods_supported Whether PKCE is available, and whether S256 is supported
end_session_endpoint Whether RP-initiated logout can work
backchannel_logout_supported Whether the provider can push logout events to the client
authorization_response_iss_parameter_supported Whether the authorization response carries iss (RFC 9207)
registration_endpoint Whether dynamic client registration is available
client_id_metadata_document_supported Whether URL-based client IDs (CIMD) are accepted

The last few matter more than they used to. MCP clients and AI-agent connectors read discovery to decide whether they can register themselves, whether they can use PKCE, and how logout should be performed.

The issuer rule

The single most common discovery failure is an issuer mismatch. The rules:

  • The issuer value must exactly match the iss claim inside the ID token. Not "same host", not "same after normalization" — the same string.
  • A trailing slash is a different issuer. So is http versus https, a different port, or a hostname that resolves to the same server.
  • The issuer must not contain a query string or a fragment.
  • The discovery response must not redirect. Clients generally refuse a 3xx here, because a redirect makes it easy to swap the metadata.
  • HTTPS is required for OIDC. Development setups often use plain HTTP on localhost; production clients should not.

Behind a reverse proxy, the issuer is usually built from the request's host and scheme. If the proxy forwards http internally, or rewrites the Host header, the document can advertise an issuer that no client can reach. Check the document from the public internet, not from inside the cluster.

Common failures

Symptom Likely cause Fix
404 on the discovery URL Wrong path, or a path-based issuer built the other way Try both documented forms
200 with HTML SPA fallback, proxy rewrite, or a catch-all route Serve the JSON before the fallback
"Issuer mismatch" Issuer differs by slash, scheme, port or host Make the public issuer exact everywhere
Keys not found jwks_uri points at an internal host, or the keys rotated Fetch jwks_uri from the same network as the client
"Unsupported response type" Client asks for a flow the document does not list Enable the grant, or change the client
Login loops after logout end_session_endpoint missing, or the client ignores it Check the logout flow explicitly
PKCE rejected code_challenge_methods_supported lacks S256, or the client sends plain Enable S256 and update the client

Caching and migrations

Clients cache discovery metadata, sometimes for hours, sometimes for the life of the process. After a domain migration or an endpoint change:

  • Serve a short Cache-Control during the transition, then lengthen it once stable.
  • Keep the old issuer reachable long enough for cached clients to refresh.
  • Check that the ID token's iss and the discovery issuer moved together. A new domain with an old issuer inside the token is worse than no migration at all.

Security notes

  • Do not let an arbitrary user-supplied URL drive server-side discovery fetches. That is an SSRF primitive. Allowlist the issuers you trust.
  • Validate iss against the configured issuer, not against whatever the response says.
  • Fetch and pin the signing keys over TLS, and handle key rotation by refreshing jwks_uri on an unknown kid.

Discovery in Casdoor

Casdoor serves discovery at https://<your-casdoor-host>/.well-known/openid-configuration. The issuer is the deployment origin (or the configured origin), and the document includes:

  • Authorization, token, userinfo, introspection, revocation and device authorization endpoints.
  • registration_endpoint at /api/oauth/register for dynamic client registration.
  • code_challenge_methods_supported: ["S256"] — no plain.
  • end_session_endpoint, backchannel_logout_supported: true and backchannel_logout_session_supported: true for logout.
  • authorization_response_iss_parameter_supported: true for RFC 9207.
  • client_id_metadata_document_supported, which follows the organization's dynamic client registration setting.

You can fetch the live document from the demo at https://door.casdoor.com/.well-known/openid-configuration and compare it with your own deployment. If your Casdoor sits behind a proxy, make sure the document's issuer matches the URL your clients use. Casdoor is open source, and Casdoor Cloud starts at $6.67/month billed yearly for a dedicated instance.

FAQ

Do I need discovery if I already know the endpoints? No, but most clients and SDKs use it, and MCP and AI-agent connectors increasingly require it. Serving a correct document is cheaper than supporting every client's manual configuration.

Why does the client say the issuer is wrong when the login works? The browser flow can succeed with a loosely configured endpoint while ID token validation still compares iss with the configured issuer. Both must be the same string.

Should discovery be public? Yes. It contains no secrets; it is meant to be readable by any client. Keep the endpoints behind TLS, but do not require authentication for the metadata.

What if my IdP supports both OIDC discovery and OAuth metadata? They can coexist. OIDC clients read /.well-known/openid-configuration; pure OAuth clients may read /.well-known/oauth-authorization-server. Keep the shared values consistent.