#32135·vault

Safari sends duplicate requests to CLI OIDC callback, causing expired OAuth state

Author: JaminenBCreated Sep 30, 2026Updated Sep 30, 2026

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

  1. On macOS, set Safari as the default browser.

  2. Run:

    bash
    vault login \
      -address=https://vault.example.com \
      -method=oidc
  3. Complete authentication in Safari.

  4. 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 storage
  5. Capture 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=2fa3a121e56b
  6. Repeat 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=f30c09b20e51

    Chrome 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):

hcl
# Full server configuration is not available.
# The standard JWT/OIDC auth method is configured with an Okta provider

Additional context

  • Vault 2.0.4 and 2.1.0 continue to use vault-plugin-auth-jwt 0.26.3.
  • Vault 2.1.1 updates vault-plugin-auth-jwt to 0.26.4, but cli.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-jwt issue or pull request history.
  • Firefox is a reliable workaround for the affected environment.