How to Audit an OpenID Connect Discovery Document: 9 Fields to Check in .well-known/openid-configuration (Auth0, Okta, Keycloak, Entra ID)
Audit any OIDC provider's openid-configuration: PKCE, implicit flow, signing algorithms, JWKS, client auth and ID-JAG, for Auth0, Okta, Keycloak and Entra ID.
Every OpenID Connect provider publishes a JSON document at /.well-known/openid-configuration. It lists the provider's endpoints and, more usefully for a security review, what it claims to support: grant types, signing algorithms, client authentication methods, PKCE modes. Reading it takes five minutes and tells you more about a provider's posture than most dashboards.
This post is a field-by-field audit guide: nine things to check, what a good value looks like, and what to flag. It works for Auth0, Okta, Keycloak, Microsoft Entra ID, AWS Cognito, Google, or an in-house provider, because it only reads standard metadata. One caveat up front: the document shows what a provider advertises, not what a specific client registration enforces. Treat a flag as a question to answer, not a verdict.
Fetch the document:
curl -s https://auth.example.com/.well-known/openid-configuration | jq .
1. issuer — Does It Match Exactly?
The issuer value must be identical to the URL you used to fetch the document, minus the /.well-known/openid-configuration suffix. A trailing slash, a different hostname, or http instead of https is a failure. Clients validate the iss claim in every ID token against this value, so a mismatch produces signature or issuer errors that look unrelated.
Common cause: a provider behind a reverse proxy that builds issuer from the internal hostname. Common in self-hosted Keycloak.
2. code_challenge_methods_supported — PKCE
You want S256 in this list. PKCE (explained here) stops authorization code interception, and it is required for public clients such as SPAs, mobile apps, and CLI tools, and is the direction OAuth 2.1 is taking for all clients.
- Good:
["S256"] - Flag: the field is missing entirely (PKCE may be unsupported, or supported but unadvertised, so test it), or
plainis listed.plainsends the verifier unhashed and defeats the purpose.
3. response_types_supported — Implicit Flow
"response_types_supported": ["code", "id_token", "token id_token"]
Any value containing token (such as token or id_token token) means the implicit flow is available, which returns access tokens in the URL fragment. The OAuth 2.0 Security Best Current Practice (RFC 9700) recommends against it. Having it advertised isn't automatically a bug, since it can be disabled per client, but it should be a conscious choice. code alone is the cleanest answer.
4. grant_types_supported — Legacy Grants
Look for password (the resource owner password credentials grant). RFC 9700 says it must not be used: it teaches users to type their passwords into third-party apps and bypasses MFA. Also note whether refresh_token and client_credentials are present, since those define how long-lived and machine-to-machine access works.
5. id_token_signing_alg_values_supported — Signing Algorithms
The OpenID Connect Discovery spec requires RS256 to be included. Beyond that:
- Flag
none. An unsigned ID token is only acceptable in narrow code-flow cases and shouldn't be advertised by a provider you rely on. - Flag
HS256and the other HMAC algorithms when clients are public. Symmetric signing means the verifier holds the signing secret, so any app that can verify can also forge. - Prefer
RS256,ES256, orEdDSA. ES256 keys and signatures are much smaller than RSA at comparable strength.
Whatever your client library accepts must be pinned to an explicit allowlist. Never let a token's own alg header choose the verification algorithm.
6. jwks_uri and the Keys Behind It
Fetch the key set:
curl -s "$(curl -s https://auth.example.com/.well-known/openid-configuration | jq -r .jwks_uri)" | jq '.keys[] | {kty, alg, use, kid}'
Check that:
- The URL returns
200JSON and is served over HTTPS. - Each key has a
kid,useofsig, and analg. Missingkidvalues make rotation fragile because clients can't tell which key signed a token. - Key types are what you expect:
RSA,EC, orOKP. RSA keys should be at least 2048 bits. - More than one key appears during rotation. A well-run provider publishes the new key before it starts signing with it and keeps the old one until old tokens expire. A single key with no overlap means a rotation will break tokens in flight.
The classic production incident here is a client that caches the JWKS forever and starts rejecting every token after the provider rotates. Your client should re-fetch on an unknown kid, with a rate limit.
7. token_endpoint_auth_methods_supported — How Clients Prove Who They Are
| Method | Strength | Notes |
|---|---|---|
client_secret_basic / client_secret_post | Baseline | Shared secret; keep out of public clients |
private_key_jwt | Stronger | Client signs an assertion with its private key; no shared secret sent |
tls_client_auth | Strong | Mutual TLS |
none | Public clients only | Relies entirely on PKCE |
For confidential server-side clients, private_key_jwt or mutual TLS is worth turning on where available. If the list contains only client_secret_*, secrets rotation becomes your problem.
8. PAR, Issuer Identification, and Request Hardening
Three optional fields show how modern the provider's security model is:
pushed_authorization_request_endpoint(PAR, RFC 9126): the client sends authorization parameters directly to the server instead of through the browser URL, so they can't be tampered with.require_pushed_authorization_requests: truemakes it mandatory.authorization_response_iss_parameter_supported: true(RFC 9207): the response carries anissparameter, which defends against mix-up attacks when a client talks to multiple providers.request_uri_parameter_supported: iftrue, the provider accepts request objects by reference, which widens the attack surface if misconfigured.
None are required for compliance. Their presence is a good sign; their absence is a roadmap item, not an emergency.
9. Agent-Era Metadata: Cross-App Access (ID-JAG)
A newer pattern matters as AI agents start acting on behalf of users across several apps. The IETF draft Identity Assertion JWT Authorization Grant (ID-JAG), which Okta ships as Cross App Access, lets an enterprise identity provider broker access between applications without a fresh consent prompt per app. Support is signaled in the same discovery document, through authorization_grant_profiles_supported and identity_chaining_requested_token_types_supported. If you're planning agent integrations against an enterprise IdP, these fields tell you whether it can participate. The draft is still evolving, so treat support as something to verify with the vendor, not assume.
Don't Forget the Providers You Didn't Know You Had
Auditing the provider you know about is half the job. Organizations often run more than one: a legacy SSO on sso., a Keycloak instance on id., a vendor-hosted login on login.. The discovery document is public by design, which makes unmanaged ones easy to find, and also easy for someone else to find first.
Automate the Whole Audit
Running curl | jq against nine fields across several providers gets tedious fast. OIDC Inspector fetches the discovery document for a domain, optionally scanning eleven common auth subdomains in parallel (auth., login., sso., id., oidc. and others), parses endpoints and supported scopes, grants and response modes, checks PKCE advertisement, inspects the JWKS (key type, algorithm, kid, use), and flags Cross-App Access support. Use it as the first pass before a pentest, a compliance review, or an integration with a provider you haven't used before.
If the provider sits behind an MCP server's auth, see how to tell whether it's OpenID Connect or bare OAuth 2.0, and for provider-specific quirks in how Auth0, Okta, and Cognito publish this document, OIDC discovery differences.
Audit Checklist
issuerequals the fetch URL exactly, over HTTPS.code_challenge_methods_supportedincludesS256and notplain.response_types_supportedhas notoken-bearing values, or you know why.grant_types_supportedhas nopassword.id_token_signing_alg_values_supportedhas nonone, and your client pins the algorithm.jwks_urireturns keys withkid,use, andalg, and your client refetches on unknownkid.token_endpoint_auth_methods_supportedoffersprivate_key_jwtor mTLS for confidential clients.- PAR and
issresponse parameter are available where your risk level calls for them. - Agent-facing fields (ID-JAG) are present if you're building cross-app agent access.
Follow Trango Compute on LinkedIn
We post updates on new tools, context engineering patterns, and LLM cost research.