1001Ferramentas
๐Ÿ›ก๏ธ Generators

OAuth State Generator

Generate secure random state tokens (128 bits) for CSRF protection in OAuth 2.0 flows. Recommended by RFC 6749.

โ€”

Signed OAuth state: encoding intent in the state parameter

There are two schools of thought for the OAuth 2.0 state parameter. The random opaque approach generates a high-entropy string, stores it server-side, and compares on callback. The signed claims approach embeds structured data โ€” CSRF nonce, return URL, user intent, A/B variant โ€” inside the state itself and proves integrity with an HMAC. Both prevent CSRF; signed state additionally lets the callback handler reconstruct context without server-side session storage, which is decisive for serverless and edge runtimes (Cloudflare Workers, Vercel Edge, AWS Lambda) where sticky sessions are expensive or impossible.

A typical signed state layout encodes a JSON payload and appends an HMAC-SHA256 tag, separated by a dot:

payload = base64url(JSON.stringify({
  csrf: random(16),       // anti-CSRF nonce
  return_url: "/dashboard",
  intent: "signup",       // signup | login | link
  variant: "B",           // A/B test arm
  iat: 1716902400,        // issued-at (unix)
  ttl: 600                // 10 minutes
}))
tag = base64url(HMAC_SHA256(STATE_SECRET, payload))
state = payload + "." + tag

Validation flow on the callback

Split on the dot, recompute the HMAC over the payload, and compare in constant time (crypto.timingSafeEqual). Only after the tag matches should you parse the JSON. Then check iat + ttl > now to enforce a 5โ€“10 minute window โ€” short enough to defeat replay, long enough for legitimate users on slow networks. Reject anything older. Finally, validate return_url against an allowlist of same-origin paths to prevent open redirects.

JWT vs custom HMAC

You can use a JWT (jsonwebtoken, jose) for the same job โ€” the exp/iat claims and HS256 signature map directly onto signed state. JWT is overkill for a value that lives 10 minutes: the header adds bytes and the algorithm-confusion footguns ("alg":"none") are real. Custom HMAC keeps the state shorter for URL budgets and avoids surprise parsers. libsodium / tweetnacl offer crypto_auth if you want HMAC without sourcing it from your standard library.

Secret hygiene and rotation

Pick a 256-bit secret (32 random bytes), store in STATE_SECRET environment variable, never check into git. Rotate by keeping a small key ring โ€” current and previous secrets โ€” verifying against any active key for the TTL window. After rotation, the old key can be retired in 10 minutes since no in-flight state outlives its TTL. OAuth 2.1 still recommends state alongside the now-mandatory PKCE; they defend different layers.

FAQ

Should I prefer signed state over a server-side store? Yes if you are stateless (serverless, edge). No if you already maintain a session store โ€” random state is simpler and the state value stays opaque.

Is HMAC-SHA256 strong enough? Yes โ€” with a 256-bit secret it is the same primitive that backs HS256 JWTs, AWS request signing and TLS PRFs. The weak link is secret rotation discipline, not the algorithm.

How long should the TTL be? 5โ€“10 minutes for interactive flows. Longer windows leak through redirects and shoulder-surfing; shorter windows fail real users on flaky connections.

Does signed state make PKCE unnecessary? No. PKCE binds the authorization code to the original client; signed state defends the callback URL against CSRF. They mitigate different attacks and OAuth 2.1 requires both for public clients.

Related Tools