Provider discovery
All domains, identifiers, and keys in these examples are fictional.
Every check in Validation and trust compared the ID token with something the printer already held. The issuer had to be exactly https://auth.photos.example. The signing key had to come from the key set at https://auth.photos.example/jwks. The audience had to include photo-printer, and the algorithm had to be one the printer accepts. In When validation fails, a mismatch between those expected values and the provider's real ones was one of the commonest causes.
So where did the expected values come from? The client ID came from the printer's registration, which Registering a relying party returns to. The rest came from the photo service itself, which publishes them in a document the printer can find from a single value.
Finding the provider's configuration
An OpenID Provider that supports discovery publishes a JSON document describing itself. Its entries are the provider's metadata, and OpenID Connect Discovery says where the document lives: at the issuer identifier with /.well-known/openid-configuration added to the end. Paths under /.well-known/ are reserved for documents that software needs to find without being told where to look.
The printer's developer started from one value, https://auth.photos.example. From it, the printer builds the request:
GET /.well-known/openid-configuration HTTP/1.1
Host: auth.photos.example
The photo service answers with JSON. This copy is shortened to the entries this lesson needs:
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: public, max-age=86400
{
"issuer": "https://auth.photos.example",
"authorization_endpoint": "https://auth.photos.example/authorize",
"token_endpoint": "https://auth.photos.example/token",
"userinfo_endpoint": "https://auth.photos.example/userinfo",
"jwks_uri": "https://auth.photos.example/jwks",
"id_token_signing_alg_values_supported": ["RS256", "ES256"],
...
}
Every address the printer has used since its first sign-in is here: where to send your browser, where to exchange the code, where to ask for your profile, and where to find the keys that sign ID tokens. Without discovery, a developer copies each value from the provider's documentation into the printer's settings and hopes to notice when one changes. With it, the printer's own settings for the photo service shrink to the issuer and what its registration gave it, such as its client ID and credential. Reading provider metadata goes through the rest of the document.
An issuer can include a path. A provider that runs a separate issuer for each organization it serves might name one https://login.example/org-7. Its document then lives after the path, at https://login.example/org-7/.well-known/openid-configuration, and if the issuer ends with a slash, that slash is removed first so the address does not contain two. The OAuth version of this document, which Authorization server metadata covers in Advanced OAuth, was generalized from this one but puts its well-known name before the path instead, so the two addresses differ whenever an issuer has a path.
Checking the document
For a relying party, this document decides whose signatures count. Suppose the printer accepted a copy that listed an attacker's key set under the photo service's name. The attacker could sign an ID token saying "sub": "user-2048", and every check in Validation and trust would pass: the right issuer, the right audience, and a valid signature from a key in the configured set. The attacker would be signed in to the printer as you, or as any other customer of the photo service.
Two checks keep the printer from accepting such a copy. The first is HTTPS with ordinary certificate validation for auth.photos.example, which establishes that the answer came from the host the printer named. A relying party that turns certificate checks off, even "just for testing", hands that decision to whoever controls the network between it and the provider.
The second is the issuer value inside the document. It must be identical to the issuer the printer used to build the address, character for character, so https://auth.photos.example/ with a trailing slash fails. The same string must also appear in every ID token from this provider, which makes one value the link between three places:
| Where the issuer appears | Value |
|---|---|
| The printer's configuration | https://auth.photos.example |
issuer in the document fetched from https://auth.photos.example/.well-known/openid-configuration | https://auth.photos.example |
iss in every ID token the printer accepts from this provider | https://auth.photos.example |
The check matters most where the expected issuer and the document's address can drift apart. Many libraries let a developer configure the address of the configuration document directly. If that address is wrong, through a typing mistake or a convincing fake setup guide, the document it leads to can claim to be https://auth.photos.example and list someone else's keys. A relying party that took its expected issuer from that document would then compare every ID token with values an attacker chose. When a library asks for the document's address, the relying party also configures the issuer it expects and requires the document's issuer to match it. A document that fails is discarded whole, never used in part.
The answer itself must be a 200 response containing a JSON object. An error page, or a document missing required entries such as jwks_uri, is a failure to report, not something to work around.
The document changes rarely, so the printer does not fetch it for every sign-in. It loads it when it first needs it, keeps it, and fetches it again on a schedule, here guided by the photo service's Cache-Control header, which allows a day. Every refreshed copy goes through the same checks as the first. If a refresh fails, or produces a document that does not pass, the printer keeps its last good copy for a while and alerts its operators. If it has never obtained a good copy, sign-in with the photo service is unavailable until it does, because the only alternative would be guessing at endpoints and keys. The key set at jwks_uri follows its own rhythm, fetched again with a limit when an unknown kid appears, as Signing keys and rotation described.
Where trust starts
Discovery fills in the details of a decision someone has already made. The printing company decided to accept sign-ins from the photo service, and a developer wrote https://auth.photos.example into the printer's configuration. Everything in this lesson begins from that value. Discovery tells the printer how to work with a provider. It never tells the printer which providers to work with.
That rules out taking an issuer from anything that arrives during a sign-in. The iss claim in an ID token, the iss parameter on the callback, and a provider named in a link are all values someone else can write. Suppose the printer fetched configuration for whatever issuer an ID token named. An attacker would put their own issuer in a token, publish a matching document and key set, and pass every check, because each check would compare the attacker's values with the attacker's values. Incoming values are compared with the issuer the printer chose for the attempt. They never choose it.
The sign-in page follows the same rule. When you select Continue with your photo account, the button sends the printer a short name such as photos, and the printer looks that name up in its own list of configured providers. It never takes an issuer address from the request.
Some relying parties let people bring an account from a provider nobody configured in advance. OpenID Connect Discovery describes an optional first step for them, issuer discovery: you type an identifier such as an email address, and the relying party asks that address's domain which issuer serves it, using a protocol called WebFinger. It is rare in practice. A relying party that uses it is accepting providers it did not choose, so it has to decide what a sign-in from an unfamiliar provider is worth, and the issuer it finds still goes through every check in this lesson. Most relying parties, the printer included, offer a short list of providers chosen in advance.