Prompt and context
A SaaS client integrates with an enterprise IdP, a public IdP, and a partner authorization server. An attacker tries to make the client treat a request started at server A as a response from server B, potentially sending a code to the wrong token endpoint or applying the wrong client configuration. Design Mix-Up defenses covering issuer binding, authorization responses, token exchange, metadata discovery, errors, and migration.
RFC 9207 defines the iss response parameter so an authorization server identifies itself in an OAuth authorization response. RFC 9700 lists issuer identification as a Mix-Up defense. The goal is to bind “the user returned” to “which authorization server completed this flow,” rather than relying on state alone.
What the interviewer is testing
- Recognition of code, token-endpoint, and client-configuration confusion in multi-issuer flows.
- Binding issuer to state, redirect URI, PKCE, and the authorization request record.
- Consistency checks across discovery metadata, issuer URLs, TLS, JWKS, and token endpoints.
- Safe handling of missing, unknown, conflicting, or forged
issvalues. - A migration plan that supports older IdPs without keeping an unsafe fallback forever.
Clarifying questions
- Is the issuer list static, dynamically registered, or discovered per tenant?
- Does the client use one shared redirect URI or a separate callback for each issuer?
- Is OIDC supported, requiring ID Token
iss,aud, and nonce validation? - Do legacy authorization servers return
iss, and can each use an isolated client configuration? - Is PKCE mandatory, and must the token endpoint use the original issuer configuration?
30-second answer
Store an immutable issuer, discovery document, authorization endpoint, token endpoint, JWKS, client ID, and redirect-URI policy for every authorization server. At authorization start, generate state, nonce, and PKCE and persist the expected issuer in a short-lived server-side record. The callback must require an allowed iss equal to that expected value, then redeem the code using only the recorded issuer configuration. Missing, unknown, or conflicting values stop the flow; the client never guesses or silently switches.
Deep-dive answer
1. Establish a trust configuration per issuer
Use a canonical issuer URL as the configuration key, including scheme, host, port, and path, and compare it according to the issuer's exact rules. Store authorization and token endpoints, JWKS, client credentials, allowed scopes, and redirect URIs.
When discovery is allowed, require the document's issuer to exactly match the configured value and fetch it over HTTPS. A user-supplied issuer must not cause arbitrary metadata or JWKS retrieval.
2. Bind the issuer when starting authorization
Generate an unpredictable state and persist state, expected issuer, client-configuration version, redirect URI, PKCE challenge, and creation time in a server-side session or short-lived store. The browser carries only a reference, not mutable tenant configuration.
The authorization URL must use the exact redirect URI registered for that issuer and client. If a tenant or IdP is selected dynamically, select and record it on the server; the callback must not retroactively choose the initial configuration.
3. Validate the authorization response iss
The RFC 9207 iss response parameter must be in the approved issuer set and equal the expected issuer in the state record. A server without iss may use a compatibility path only with an isolated callback, client, or trusted boundary; a shared callback must not guess across every issuer.
Validate state, issuer, error responses, and redirect context before processing the code. Reject unknown, duplicate, or encoding and case variants. Error pages show a generic failure and never reflect an unverified URL.
4. Keep token exchange on the original issuer
Read the token endpoint, client authentication, and PKCE verifier from the state record, never by concatenating a URL from callback input. If the token response contains an issuer, ID Token, or identity claims, compare them with the original issuer and client ID.
PKCE binds the code redeemer, state binds the browser session, and issuer binds the authorization server. OIDC also requires ID Token issuer, audience, signature, time, and nonce validation; callback iss alone is insufficient.
5. Secure discovery, JWKS, and rotation
Discovery, token endpoints, and JWKS must remain inside an approved issuer trust boundary. Use controlled JWKS caching and key versions; a missing kid may trigger one bounded refresh, never arbitrary URL access. Version issuer configuration so in-flight state continues using the version recorded at creation.
Discovery failure, issuer mismatch, TLS failure, or unverifiable signatures fail closed for high-risk flows. Do not switch token endpoints to another issuer for availability.
6. Prevent state abuse and resource exhaustion
Give state records a short TTL, one-time consumption, and concurrency limits. Duplicate callbacks, unknown or expired state, and wrong issuer invalidate the flow. Cap callback parameters and avoid logging abnormal JWTs, URLs, or long error descriptions.
Track missing and conflicting issuers, unknown issuers, state replay, discovery failures, JWKS refreshes, PKCE failures, and completion by issuer. Group alerts by tenant and issuer to separate configuration mistakes from attacks.
7. Migrate legacy authorization servers
Inventory issuers and callback capabilities, then enforce RFC 9207 for compatible servers. Legacy servers can use isolated redirect URIs, isolated clients, or a server-side proxy with explicit binding, an end date, and audit. Do not keep a shared callback that guesses the issuer from a code indefinitely.
During rollout compare completion, errors, callback latency, and issuer conflicts. Pause new-tenant rollout when signals regress. Keep state records and a rollback switch, but never disable PKCE or reopen an unbound token endpoint.
Model answer
For every authorization server I would maintain a fixed issuer, discovery document, authorization endpoint, token endpoint, JWKS, client, and redirect-URI policy. Authorization start creates state, nonce, and PKCE and stores the expected issuer and configuration version server-side. The callback validates state and the RFC 9207 iss, requiring an approved value equal to the expected issuer; token exchange then uses only the recorded endpoint and client credentials.
Discovery issuer, OIDC ID Token issuer, audience, signature, and nonce are checked again. Missing or conflicting issuer, expired or repeated state, and JWKS failures stop the flow rather than silently switching. Legacy IdPs use isolated callbacks or a proxy with a deadline. Monitor issuer conflicts, state replay, PKCE failures, and completion by issuer.
Common mistakes
- Checking state alone and assuming it identifies the authorization server.
- Building token or JWKS URLs directly from callback issuer input.
- Letting missing or unknown
issvalues continue by trying every IdP. - Skipping cross-checks among discovery issuer, ID Token issuer, and token endpoint.
- Keeping a shared callback and code-based issuer guessing indefinitely during migration.
- Describing PKCE, state, nonce, and issuer binding as one control.
- Writing unvalidated issuers, JWTs, or error URLs into logs or pages.
Follow-up questions and answers
Why cannot state alone prevent Mix-Up?
State binds a browser session and callback, but the client can still send a valid-state code to the wrong token endpoint if it does not know the response issuer. Issuer binding supplies that server identity.
Can the flow continue when a callback has no iss?
Only under an explicitly isolated compatibility policy, such as a separate callback or proxy per legacy server. A shared callback must not iterate over issuers and guess.
May a user enter an issuer URL?
The selection may map only to an approved issuer. The server must not fetch discovery or JWKS for arbitrary input, which would create SSRF, phishing, and trust-root risks.
How do you prevent tenant configuration mix-ups?
The state record contains tenant, issuer, configuration version, and redirect URI. Callback and token exchange read that record only. Configuration updates do not overwrite in-flight state during its TTL.
What else is required for OIDC?
Validate the ID Token signature, issuer, audience, expiry, issued-at time, nonce, and required authentication context in addition to callback iss. Callback parameters cannot replace ID Token validation.
How do you prove migration did not weaken security?
Record enforced policy, activation time, and callback type per issuer. Test missing and forged issuers, cross-tenant state, endpoint replacement, and repeated callbacks, and monitor fallback hits so they remain zero or within an approved exception.