hashicorp/nomad · error
nonce reuse detected
Error message
nonce reuse detected
What it means
ErrNonceReuse is returned when the OIDC request cache already holds an entry for the same client nonce. Nonces must be unique per auth-url request; a collision indicates a bug or forged/duplicated callback traffic, so the store refuses the request.
Source
Thrown at lib/auth/oidc/request.go:17
// Copyright IBM Corp. 2015, 2026
// SPDX-License-Identifier: BUSL-1.1
package oidc
import (
"errors"
"fmt"
"sync"
"time"
"github.com/hashicorp/cap/oidc"
"github.com/hashicorp/golang-lru/v2/expirable"
)
var (
ErrNonceReuse = errors.New("nonce reuse detected")
// ErrTooManyRequests is returned if the request cache is full.
// Realistically, we expect this only to happen if the auth-url
// API endpoint is being DOS'd.
ErrTooManyRequests = errors.New("too many auth requests")
)
// MaxRequests is how many requests are allowed to be stored at a time.
// It needs to be large enough for legitimate user traffic, but small enough
// to prevent a DOS from eating up server memory.
const MaxRequests = 1000
// NewRequestCache creates a cache for OIDC requests.
// The JWT expiration time in the cap library is 5 minutes,
// so timeout should be around that long.
func NewRequestCache(timeout time.Duration) *RequestCache {
return &RequestCache{
c: expirable.NewLRU[string, *oidc.Req](MaxRequests, nil, timeout),
}View on GitHub (pinned to 482b49bf1a)
Solutions
- Generate a fresh unique nonce for every auth-url request on the client side and retry.
- Investigate the client generating the duplicate nonce — nonce generation should use a CSPRNG per request.
- If triggered by a replayed callback, treat it as suspicious traffic and drop it rather than retrying.
Example fix
// before req, _ := oidc.NewReq(provider, clientID, clientSecret, oidc.WithNonce(fixedNonce)) rc.store(req) // second store of same nonce fails // after nonce, _ := uuid.GenerateUUID() req, _ := oidc.NewReq(provider, clientID, clientSecret, oidc.WithNonce(nonce)) rc.store(req)
Defensive patterns
Strategy: try-catch
Validate before calling
if rc.Has(req.Nonce()) {
req = regenerateWithNewNonce(req)
} Try / catch
err := rc.store(req)
if errors.Is(err, oidc.ErrNonceReuse) {
// regenerate nonce and retry once, or reject the callback as replay
return fmt.Errorf("rejecting duplicate OIDC nonce: %w", err)
} Prevention
- Generate nonce with a CSPRNG (crypto/rand) per request
- Never reuse or cache nonces client-side across logins
- Treat nonce reuse callbacks as potential replay attacks
When it happens
Trigger: RequestCache.store (storeLocked) is called twice with an oidc.Req whose Nonce() is already present in the LRU cache — checked at request.go:58 via rc.c.Get(req.Nonce()).
Common situations: A client replaying the same auth-url request; two browser tabs racing with an identical nonce; a broken client that regenerates the same nonce; tests deliberately re-storing the same req (request_test.go:26).
Related errors
- no OIDC request found for client nonce
- missing client nonce
- error storing OIDC request: %w
- no auth method config or client assertion
- certificate has expired or is not yet valid
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/bd807c32ebe5f204.
Report an issue: GitHub.