Discovery is the OIDC convention for "here is everything you need to know about this identity provider, in one JSON document". Your SDK fetches it once on startup and caches the result; from then on, it knows every endpoint and every supported capability without hard-coded URLs.
The endpoint
Section titled “The endpoint”GET https://<your-tenant-url>/.well-known/openid-configurationNo authentication required. Public by design — a relying party that wants to integrate needs the discovery document before it has any credentials.
A sample response
Section titled “A sample response”{ "issuer": "https://banking-cymmetri.intelliauth.local", "authorization_endpoint": "https://banking-cymmetri.intelliauth.local/oauth2/authorize", "token_endpoint": "https://banking-cymmetri.intelliauth.local/oauth2/token", "userinfo_endpoint": "https://banking-cymmetri.intelliauth.local/oauth2/userinfo", "jwks_uri": "https://banking-cymmetri.intelliauth.local/.well-known/jwks.json", "revocation_endpoint": "https://banking-cymmetri.intelliauth.local/oauth2/revoke", "introspection_endpoint": "https://banking-cymmetri.intelliauth.local/oauth2/introspect", "device_authorization_endpoint": "https://banking-cymmetri.intelliauth.local/oauth2/device_authorization", "end_session_endpoint": "https://banking-cymmetri.intelliauth.local/oauth2/logout",
"response_types_supported": ["code", "id_token", "code id_token"], "subject_types_supported": ["public"], "id_token_signing_alg_values_supported": ["RS256"], "scopes_supported": ["openid", "profile", "email", "offline_access"], "token_endpoint_auth_methods_supported": [ "client_secret_basic", "client_secret_post", "none" ], "grant_types_supported": [ "authorization_code", "refresh_token", "client_credentials", "urn:ietf:params:oauth:grant-type:device_code", "urn:ietf:params:oauth:grant-type:token-exchange" ], "code_challenge_methods_supported": ["S256"]}What the SDK does with this
Section titled “What the SDK does with this”On boot, the SDK fetches discovery and caches it. Then:
authorization_endpoint→ where to send the browser for sign-in.token_endpoint→ where to POST for token exchange.jwks_uri→ where to fetch public keys for verifying id tokens.userinfo_endpoint→ where to fetch claim data for the current user.revocation_endpoint→ where to revoke tokens on sign-out.end_session_endpoint→ where to redirect for tenant-side logout.
All hard-coded URLs in your codebase become a single hard-coded value (tenantUrl), with everything else derived from discovery.
The JWKS document
Section titled “The JWKS document”The other "well-known" document is the key set:
GET https://<your-tenant-url>/.well-known/jwks.jsonResponse:
{ "keys": [ { "kty": "RSA", "kid": "key-2026-q2", "use": "sig", "alg": "RS256", "n": "...base64url-modulus...", "e": "AQAB" } ]}When verifying an id token, your code reads the kid from the JWT header, picks the matching key from this document, and verifies the signature. Cache the document; refresh on kid miss.
Cache TTL
Section titled “Cache TTL”Discovery and JWKS responses include Cache-Control headers. Honour them. The typical pattern:
- Cache discovery for 24 hours; refresh on startup.
- Cache JWKS for 24 hours, but refresh immediately when an id token carries a
kidnot in the cache.
Refreshing on kid miss is how the platform rotates signing keys without forcing every relying party to coordinate the change.
When you would read discovery by hand
Section titled “When you would read discovery by hand”You almost never do. The SDK reads it for you. The case where you do read it: setting up federation with another identity provider — see the federation topic. The remote IDP exposes the same shape of document, and you point IntelliAuth at it.