Safari sends duplicate requests to CLI OIDC callback, causing expired OAuth state
Describe the bug
When vault login -method=oidc launches Safari on macOS, Safari sends the same OIDC callback twice to the CLI listener at http://localhost:8250/oidc/callback. The requests use separate TCP connections but contain the same authorization code and OAuth state.
The login fails with Vault login failed. Expired or missing OAuth state. The same login from the same computer and network succeeds when Firefox or Chrome is the default browser. Both Firefox and Chrome send the callback once.
This is similar to #26101, where an unexpected Chrome OPTIONS request reaches the one-time CLI callback listener before the valid callback. In this reproduction, Safari sends two identical GET requests instead.
To Reproduce
On macOS, set Safari as the default browser.
Run:
vault login \ -address=https://vault.example.com \ -method=oidcComplete authentication in Safari.
Observe the login failure:
Error authenticating: Error making API request. URL: GET https://vault.example.com/v1/auth/oidc/oidc/callback?client_nonce=<REDACTED>&code=<REDACTED>&id_token=&state=<REDACTED> Code: 400. Errors: * errors from both primary and secondary; primary error was Vault login failed. Expired or missing OAuth state.; secondary errors follow: cannot write to readonly storageCapture loopback callback requests while reproducing. OAuth values below were replaced with SHA-256 fingerprints of the complete request target.
Safari sends two identical callbacks over separate TCP connections:
GET /oidc/callback source=127.0.0.1.64735 seq=0:553 params=[code,state] fingerprint=2fa3a121e56b GET /oidc/callback source=127.0.0.1.64738 seq=0:593 params=[code,state] fingerprint=2fa3a121e56bRepeat with Firefox or Chrome as the default browser.
Firefox sends one callback and login succeeds:
GET /oidc/callback source=127.0.0.1.64720 seq=0:530 params=[code,state] fingerprint=f30c09b20e51Chrome sends one callback and login succeeds:
GET /oidc/callback source=127.0.0.1.65093 seq=0:764 params=[code,state] fingerprint=f527f74bc483
The different Safari source ports confirm that the two callback requests use separate TCP connections. Their identical fingerprints confirm that both requests contain the same callback target, including the same authorization code and state.
Expected behavior
The CLI OIDC callback listener should complete login using the first valid callback. A duplicate request for the same callback should not replace a successful result with an expired-state error or cause the overall login to fail.
Environment:
- Vault Server Version:
2.0.3+ent - Vault CLI Version:
Vault v2.0.3+ent - Client Operating System/Architecture: macOS 27.0 build 26A428, Apple Silicon arm64
- Safari Version: 27.0
- Firefox Version: 156.0
- Chrome Version: 154.0.8037.58
- Server Operating System/Architecture: Ubuntu22/arm64
- Vault HA: Enabled
- OIDC provider: Okta
- Callback URI:
http://localhost:8250/oidc/callback
Vault server configuration file(s):
# Full server configuration is not available.
# The standard JWT/OIDC auth method is configured with an Okta providerAdditional context
- Vault 2.0.4 and 2.1.0 continue to use
vault-plugin-auth-jwt0.26.3. - Vault 2.1.1 updates
vault-plugin-auth-jwtto 0.26.4, butcli.go, including the callback handler, is unchanged between plugin versions 0.26.3 and 0.26.4. - No Safari references were found in the
vault-plugin-auth-jwtissue or pull request history. - Firefox is a reliable workaround for the affected environment.
Source: hashicorp/vault