# Prevent OAuth Mix-Up in Multi-Provider Login

> Bind each OAuth authorization response to the issuer chosen at login start, and require `iss` when supported to stop shared-callback provider confusion.

Canonical URL: https://www.devobs.io/articles/oauth-mix-up-issuer-validation/
By: Jonah Reed
Published: 2025-11-09T01:42:29.199Z
Updated: 2026-09-06T10:18:15.722Z
Section: Identity

Reviewed: 2026-09-06.

## Bind every callback to its initiating issuer

A shared callback must know which authorization server issued the response before sending the code to any token endpoint. Bind state to the chosen issuer and validate the returned issuer exactly when issuer identification is available; otherwise an attacker can confuse the client across providers. [RFC 9700: OAuth 2.0 Security Best Current Practice](https://www.rfc-editor.org/rfc/rfc9700) describes mix-up attacks in multi-provider OAuth deployments, and [RFC 9207: OAuth 2.0 Authorization Server Issuer Identification](https://www.rfc-editor.org/rfc/rfc9207) defines the `iss` response parameter as a countermeasure.

## Test a shared callback with two issuers

Store issuer, client ID, redirect URI, verifier, and nonce with state. On callback, validate state and, when the authorization server supports issuer identification, validate `iss` against the issuer already bound to that transaction. Then use metadata previously bound to that issuer. Distinct [redirect URIs](https://www.devobs.io/articles/oauth-redirect-uri-validation/) can provide another unambiguous routing boundary when issuer identification is unavailable, but they do not replace state or PKCE.

## Bind the transaction before redirecting

When login starts, store a random state value with the chosen issuer, client ID, exact redirect URI, [PKCE](https://www.devobs.io/articles/spa-pkce-callback-hardening/) verifier, nonce where applicable, and expiry. On callback, validate the state and ensure the returned `iss` matches the issuer bound to that transaction before using any authorization-server metadata or sending the code to a token endpoint. If you make state single-use, consume it atomically as part of that validation path.

Never derive the token endpoint from an untrusted callback parameter. Discovery metadata must match its claimed issuer exactly and should be cached under that identity, not under a display name such as “work login.”

If the authorization server does not support issuer identification, use another unambiguous binding such as distinct redirect URIs plus transaction state, and still keep the code exchange pinned to the issuer selected at initiation.

## Attack the shared callback

Configure two test issuers and start a transaction with issuer A. Send a callback carrying issuer B, replay a consumed state, omit `iss` where the deployment requires it, and substitute metadata. Every case should fail before a code reaches any token endpoint. Also test concurrent callbacks in two tabs so one transaction cannot consume another.

Distinct redirect URIs can create a useful routing boundary when issuer response identification is unavailable, but they still need state and PKCE. Audit one production callback by tracing the stored issuer from initiation through code exchange; any point that reselects the provider from response data is the repair target.

## Source references

- <https://www.rfc-editor.org/rfc/rfc9700>
- <https://www.rfc-editor.org/rfc/rfc9207>
