OpenID Connect Discovery Document
## What It Is
The OpenID Connect Discovery Document is a standard JSON endpoint that acts as the **service manual** for an OIDC provider. One URL tells a client application everything it needs to know about the provider - where its endpoints live, what algorithms it supports, what scopes are available, and more.
Instead of hardcoding URLs or maintaining manual configuration, a client fetches this document at startup and dynamically discovers every endpoint and capability.
---
## The URL
```
GET https://{provider}/.well-known/openid-configuration
```
The `/.well-known/` prefix is defined by **RFC 5785** - it is a convention that means "this path is always in the same place on every server." No guessing, no custom configuration, no vendor-specific setup.
| Provider | Discovery URL |
|----------|--------------|
| Google | `https://accounts.google.com/.well-known/openid-configuration` |
| Auth0 | `https://{tenant}.auth0.com/.well-known/openid-configuration` |
| Microsoft | `https://login.microsoftonline.com/{tenant}/v2.0/.well-known/openid-configuration` |
| Amazon Cognito | `https://cognito-idp.{region}.amazonaws.com/{poolId}/.well-known/openid-configuration` |
---
## What It Returns
The response is a JSON document with a standard set of fields defined by the [OpenID Connect Discovery spec](https://openid.net/specs/openid-connect-discovery-1_0.html). Here is a representative example with annotations:
```json
{
"issuer": "https://auth.example.com",
"authorization_endpoint": "https://auth.example.com/authorize",
"token_endpoint": "https://auth.example.com/token",
"userinfo_endpoint": "https://auth.example.com/userinfo",
"jwks_uri": "https://auth.example.com/.well-known/jwks.json",
"registration_endpoint": "https://auth.example.com/register",
"scopes_supported": [
"openid",
"profile",
"email",
"address",
"phone"
],
"response_types_supported": [
"code",
"id_token",
"token id_token"
],
"response_modes_supported": [
"query",
"fragment",
"form_post"
],
"grant_types_supported": [
"authorization_code",
"implicit",
"refresh_token"
],
"id_token_signing_alg_values_supported": [
"RS256",
"RS384",
"RS512",
"ES256",
"ES384",
"ES512"
],
"subject_types_supported": [
"public"
],
"claims_supported": [
"sub",
"iss",
"aud",
"exp",
"iat",
"name",
"email",
"picture"
}
}
```
### Field Reference
| Field | Type | What It Tells You |
|-------|------|-------------------|
| `issuer` | string | The unique identifier for this provider. Your app must verify this matches the `iss` claim in every token to prevent token confusion attacks. |
| `authorization_endpoint` | URL | Where to redirect the user's browser for login. |
| `token_endpoint` | URL | Where to exchange authorization codes for tokens. |
| `userinfo_endpoint` | URL | Where to fetch standard profile claims using an access token. |
| `jwks_uri` | URL | Where to download public keys for signature verification. |
| `registration_endpoint` | URL | Where clients can register themselves dynamically (optional). |
| `scopes_supported` | array | What access scopes the server recognizes. |
| `response_types_supported` | array | Which OAuth response types the server supports (code, token, id_token, etc.). |
| `response_modes_supported` | array | How the server sends responses back to the client (query params, fragment, form POST). |
| `grant_types_supported` | array | Which OAuth grant types are available. |
| `id_token_signing_alg_values_supported` | array | Which signing algorithms the server uses for ID tokens. |
| `subject_types_supported` | array | Whether subject identifiers are `public` (same across all clients) or `pairwise` (different per client). |
| `claims_supported` | array | The full list of claims the server can return about a user. |
---
## How Clients Use It (Bootstrapping)
A client only needs **one piece of information** to configure itself: the provider's issuer URL. Here is how bootstrapping works:
1. The developer configures the client with the issuer URL (e.g., `https://auth.example.com`)
2. At startup, the client appends `/.well-known/openid-configuration` and fetches the document
3. It extracts all endpoint URLs and supported features from the response
4. The client configures its internal OIDC library automatically - no hardcoded URLs
**No manual URL entry, no admin copy-paste, no mismatch errors.**
Most OIDC SDKs do this automatically. When you configure a library with just `issuer: "https://accounts.google.com"`, it fetches the Discovery Document under the hood and wires everything up.
---
## Zero-Downtime Changes
The Discovery Document enables providers to change their infrastructure without breaking existing clients:
- **Endpoint migration**: If the provider moves its token endpoint from `/token` to `/v2/token`, it updates the Discovery Document. Clients pick up the new URL on their next fetch. No code deploy needed.
- **New algorithm support**: When the provider adds a stronger signing algorithm, it adds it to `id_token_signing_alg_values_supported`. Clients can start using it immediately.
- **Deprecation**: The provider can signal deprecated features by removing them from the `*_supported` arrays.
Clients typically cache the Discovery Document for a configurable period (hours to days), so changes propagate gradually - no thundering herd on the provider's infrastructure.
---
## Key Rotation Tie-In
The `jwks_uri` field in the Discovery Document points to the JWKS (JSON Web Key Set) endpoint - the server's public key directory. This is how key rotation works seamlessly:
1. The provider generates a new signing key pair and adds the public key to its JWKS endpoint
2. New tokens are signed with the new key (identified by a unique `kid` in the token header)
3. The JWKS endpoint now serves both old and new public keys
4. Clients fetch keys at verification time and match by `kid`
5. Old keys are removed from JWKS once all tokens signed with them have expired
Since the Discovery Document always points to the current `jwks_uri`, providers can rotate `jwks_uri` itself (e.g., moving to a new CDN-backed endpoint) with the same zero-downtime mechanism.
**No manual key rollover. No shared secrets. No downtime.**
---
## Real-World Examples
### Google
```
https://accounts.google.com/.well-known/openid-configuration
```
Google's Discovery Document is one of the most complete in the industry. It advertises:
- 30+ supported scopes (including Google-specific ones like `https://www.googleapis.com/auth/userinfo.email`)
- Multiple signing algorithms (RS256 is primary)
- Pairwise subject identifiers for privacy
- A `claims_parameter_supported` flag
### Auth0
```
https://{your-tenant}.auth0.com/.well-known/openid-configuration
```
Auth0 uses tenant-specific subdomains. Each tenant gets its own Discovery Document. Key differences from Google:
- Supports both RS256 and HS256 (symmetric) signing
- Exposes a `registration_endpoint` for dynamic client registration
- Uses public subject type by default
### Key Differences Between Providers
| Aspect | Google | Auth0 |
|--------|--------|-------|
| Subject type | Pairwise (privacy) | Public (default) |
| Signing algorithms | RS256 + ES256 | RS256 + HS256 |
| Dynamic registration | Not exposed | Available |
| Scope count | 30+ | Configurable |
| Tenant structure | Single global | Multi-tenant per subdomain |
---
## Dynamic Client Registration
If the Discovery Document includes a `registration_endpoint`, clients can register themselves programmatically - no admin portal, no copy-paste of client IDs.
The client sends a POST request with its metadata:
```json
{
"client_name": "My App",
"redirect_uris": [
"https://myapp.com/callback",
"https://myapp.com/alt-callback"
],
"grant_types": ["authorization_code", "refresh_token"],
"token_endpoint_auth_method": "client_secret_basic",
"scope": "openid profile email"
}
```
The server responds with a registered client ID:
```json
{
"client_id": "a1b2c3d4e5f6g7h8",
"client_secret": "p9q8r7s6t5u4v3w2",
"client_id_issued_at": 1699000000,
"client_secret_expires_at": 0
}
```
- `client_secret_expires_at: 0` means the secret never expires
- The client stores these credentials and uses them for all future requests
- Registration can be authenticated (using an initial access token) or open
This is how developer portals and API marketplaces work - when you create a new app, the platform registers a client for you behind the scenes.
---
## How SDKs Use It
Every major OIDC library fetches the Discovery Document under the hood. It is the foundation of "one-click" provider configuration.
| Library / SDK | How It Uses Discovery |
|--------------|----------------------|
| **NextAuth.js** | `provider: "google"` fetches Google's Discovery Document to wire up endpoints |
| **Spring Security** | `spring.security.oauth2.client.provider.google.issuer-uri` triggers auto-discovery |
| **oauth2-proxy** | `--provider=google` uses Discovery to find all endpoints |
| **node-openid-client** | `Issuer.discover("https://accounts.google.com")` fetches and parses the document |
| **AppAuth (mobile)** | Service configuration fetched from Discovery to bootstrap native auth flows |
When you configure a social login provider with just a name or URL, the SDK is doing this:
```
issuer URL + "/.well-known/openid-configuration"
→ fetch JSON
→ extract authorization_endpoint, token_endpoint, jwks_uri
→ configure OIDC client
```
All of this happens in milliseconds at app startup or on first authentication attempt.
---
## One-Liner Summary
| Field | What It Tells You |
|-------|-------------------|
| `issuer` | Who runs this server - your app checks this to prevent token confusion |
| `*_endpoint` | The actual URLs for each OIDC operation |
| `jwks_uri` | Where to find public keys for signature verification |
| `*_supported` | What this server supports (scopes, algorithms, claims, etc.) |