OpenID Connect Discovery 1.0 Deep Dive: OP's 'Self-Introduction' and Dynamic Configuration Retrieval
Source: Dev.to
Introduction
You probably use OIDC (OpenAI Connect) every day to integrate Google Login or other authentication flows into your applications.
When doing so, have you ever experienced simply setting
issuer: "https://accounts.google.com"
in your library initialization code, and it automatically resolves the Authorization Endpoint, Token Endpoint, and even the location of the public keys (JWKS)?
- “Why does just providing the Issuer URL reveal all the endpoints?”
- “How can it follow public‑key (JWKS) rotation without any downtime?”
- “In the first place, how does it identify the provider to authenticate with from an email‑like ID such as
alice@example.com?”
The answer to these questions is OpenID Connect Discovery 1.0.
In the past OAuth 2.0 world, developers often read the documentation and hard‑coded the URLs of each endpoint (e.g., /authorize, /token) of the Authorization Server into the client.
That approach required client‑side changes whenever the provider altered URLs or rotated public keys—clearly not scalable.
OIDC Discovery 1.0 is a standardized mechanism for a client to dynamically discover and retrieve the configuration information (metadata) of an OpenID Provider (OP).
In this article, based on the specification, we will dive deep into the two phases of OIDC Discovery:
| Phase | Goal |
|---|---|
| Issuer Discovery (Phase 1) | Discover who the OpenID Provider (Issuer) is that should authenticate the user, based on an identifier supplied by the user (e.g., an email address). This step is optional and is skipped when the Issuer is already known (e.g., clicking “Login with Google”). |
| Provider Configuration (Phase 2) | Query the identified Issuer for its configuration metadata – “Where is your Authorization Endpoint?” “Where are your public keys (JWKS)?” |
When is Issuer Discovery needed?
If your app is dedicated to Google Login, the Issuer is obviously https://accounts.google.com.
But consider an enterprise SaaS that must dynamically switch the destination IdP based on the user’s email domain (@company.com).
That’s where RFC 7033 WebFinger comes into play.
Normalizing the User‑Supplied Identifier
The value entered by the user can be:
- an email address like
alice@example.com - a URL like
https://example.com/alice
OIDC Discovery defines strict Normalization Steps to uniquely determine:
- Host – the server to contact
- Resource – the identifier to query via WebFinger
Normalization Rules
| Condition | Normalized Form |
|---|---|
No scheme and contains @ (e.g., joe@example.com) | Interpreted as the acct: scheme → acct:joe@example.com |
No scheme and does not contain @ (e.g., example.com or example.com:8080) | Interpreted as https:// → https://example.com |
Explicit scheme (https://, acct:, …) | Used as‑is, no further changes |
Fragment present (#…) | Remove the fragment entirely |
Phase 1 – Issuer Discovery via WebFinger
Assume the user entered joe@example.com, which normalizes to acct:joe@example.com.
- Identify the Host – Extract the authority part (
example.com). - Specify the Resource – Use the full normalized URI (
acct:joe@example.com) as theresourcequery parameter. - Call the WebFinger endpoint – Perform an HTTP GET against
/.well-known/webfingeron the extracted host.
GET /.well-known/webfinger?resource=acct%3Ajoe%40example.com&rel=http%3A%2F%2Fopenid.net%2Fspecs%2Fconnect%2F1.0%2Fissuer HTTP/1.1
Host: example.com
resource– URL‑encoded identifier to be queried.rel– Fixed valuehttp://openid.net/specs/connect/1.0/issuer, meaning “I am asking for OIDC Issuer information”.
Example Response (JRD – JSON Resource Descriptor)
HTTP/1.1 200 OK
Content-Type: application/jrd+json
{
"subject": "acct:joe@example.com",
"links": [
{
"rel": "http://openid.net/specs/connect/1.0/issuer",
"href": "https://server.example.com"
}
]
}
The href value (https://server.example.com) is the Issuer URL that the client will use for the next phase.
Phase 2 – Provider Configuration
With the Issuer known, the client retrieves the OP’s metadata.
OIDC Discovery mandates that the OP exposes JSON metadata at a path formed by appending /.well-known/openid-configuration to the Issuer URL.
GET /.well-known/openid-configuration HTTP/1.1
Host: server.example.com
⚠️ Common Pitfall – Issuer Contains a Path
If the Issuer includes a path (e.g., https://example.com/tenant-1):
- Remove any trailing slash.
- Append
/.well-known/openid-configurationdirectly after the path.
Resulting URL:
https://example.com/tenant-1/.well-known/openid-configuration
Do not mistakenly place the configuration file at the domain root (https://example.com/.well-known/openid-configuration), as many implementations do.
Example OP Metadata Response
HTTP/1.1 200 OK
Content-Type: application/json
{
"issuer": "https://server.example.com",
"authorization_endpoint": "https://server.example.com/oauth2/v1/authorize",
"token_endpoint": "https://server.example.com/oauth2/v1/token",
"userinfo_endpoint": "https://server.example.com/oauth2/v1/userinfo",
"jwks_uri": "https://server.example.com/oauth2/v1/keys",
"registration_endpoint": "https://server.example.com/oauth2/v1/register",
"scopes_supported": ["openid", "profile", "email"],
"response_types_supported": ["code", "id_token", "token id_token"],
"grant_types_supported": ["authorization_code", "refresh_token", "client_credentials"],
"subject_types_supported": ["public", "pairwise"],
"id_token_signing_alg_values_supported": ["RS256"],
"claims_supported": ["sub", "iss", "aud", "exp", "iat", "email", "email_verified", "name"]
}
The JSON object (OP Metadata) lists:
- Endpoints (
authorization_endpoint,token_endpoint,userinfo_endpoint,jwks_uri, …) - Supported features (
scopes_supported,grant_types_supported,claims_supported, etc.)
With this metadata, client libraries can automatically:
- Direct users to the correct Authorization Endpoint
- Exchange codes at the proper Token Endpoint
- Retrieve public keys from the JWKS URI (handling rotation transparently)
Recap
- Issuer Discovery (optional) – Use WebFinger to turn a user‑supplied identifier (email, URL, etc.) into an Issuer URL.
- Provider Configuration – Fetch the Issuer’s
/.well-known/openid-configurationdocument to obtain all required endpoints and capabilities.
This two‑phase discovery process eliminates hard‑coding, supports multi‑tenant scenarios, and ensures seamless key rotation—all without any downtime.
OpenID Connect Provider Configuration (Discovery)
{
"authorization_endpoint": "https://server.example.com/connect/authorize",
"token_endpoint": "https://server.example.com/connect/token",
"userinfo_endpoint": "https://server.example.com/connect/userinfo",
"jwks_uri": "https://server.example.com/jwks.json",
"response_types_supported": [
"code",
"id_token",
"id_token token"
],
"subject_types_supported": [
"public",
"pairwise"
],
"id_token_signing_alg_values_supported": [
"RS256",
"ES256"
],
"token_endpoint_auth_methods_supported": [
"client_secret_basic",
"private_key_jwt"
],
"scopes_supported": [
"openid",
"profile",
"email"
],
"claims_supported": [
"sub",
"iss",
"name",
"email"
],
"registration_endpoint": "https://server.example.com/connect/register"
}
Parameter Overview
| Parameter Name | Required / Optional | Description |
|---|---|---|
| issuer | REQUIRED | The OP’s Issuer Identifier. Used for TLS checks and validation of the iss claim in the ID Token. |
| authorization_endpoint | REQUIRED | The endpoint to which the user is redirected for authentication. |
| token_endpoint | REQUIRED* | The endpoint used to exchange an authorization code for tokens. Except for OPs that only support the Implicit Flow. |
| jwks_uri | REQUIRED | URL where the public keys (JWK Set) for verifying the ID Token’s signature are located. |
| response_types_supported | REQUIRED | The OIDC authentication flows supported by the OP (e.g., code, id_token, id_token token). |
| subject_types_supported | REQUIRED | Types of sub identifiers supported – public (same across all RPs) or pairwise (unique per RP). |
| id_token_signing_alg_values_supported | REQUIRED | Signature algorithms the OP can use for ID Tokens. RS256 MUST be included. |
| token_endpoint_auth_methods_supported | OPTIONAL | Client authentication methods accepted at the token endpoint (e.g., client_secret_basic, private_key_jwt). |
| scopes_supported | RECOMMENDED | List of scopes the OP supports. The openid scope SHOULD be present. |
| claims_supported | RECOMMENDED | List of claims the OP can return (e.g., name, email, sub, iss). |
| registration_endpoint | RECOMMENDED | Endpoint for Dynamic Client Registration. |
Deep‑Dive: jwks_uri
- Why it matters – The
jwks_uriis the single source of truth for the OP’s public keys. By fetching this URL, a Relying Party (RP) can verify the signature of every ID Token it receives. - Key rotation – An RP should inspect the
kid(Key ID) header of an incoming ID Token. If the corresponding key is missing from the local cache, the RP must re‑fetch the JWKS fromjwks_uri. This enables seamless key rotation without downtime. - Security considerations –
-
Impersonation attacks: If an attacker can make the RP download a malicious JWKS, forged ID Tokens become possible.
-
The Discovery spec enforces strict checks:
“The
issuervalue returned MUST be identical to the Issuer URL that was used as the prefix to/.well-known/openid-configuration.” (OIDC Discovery §4.3)“Implementations MUST support TLS.” (OIDC Discovery §7.1)
-
All communication (WebFinger and Provider Configuration) must occur over HTTPS, and the RP must verify the server’s TLS certificate (RFC 6125). Plain‑text transport would allow a MITM to rewrite
jwks_urito an attacker‑controlled server.
-
Key take‑aways from OIDC Discovery 1.0
- No hard‑coded URLs – Using
/.well-known/openid-configurationlets RPs discover the OP’s endpoints and capabilities at runtime. - WebFinger integration – An identifier such as a user’s email can be resolved to the correct Issuer (Discovery Phase 1).
- Automated key rotation – Dynamic retrieval of the JWKS enables robust, seamless security operations.
When you next use an OIDC library, picture the hidden request to
/.well-known/openid-configurationthat powers all of the above.
References
- OpenID Connect Discovery 1.0 – Core specification for provider metadata.
- RFC 7033 – WebFinger – Mechanism for discovering information about a resource (e.g., an email address).
- RFC 5785 – Well‑Known URIs – Defines the
/.well-known/namespace used for discovery endpoints.