Skip to content

refactor(auth): stateless OAuth2 state via securecookie - #38

Merged
AYDEV-FR merged 1 commit into
mainfrom
refactor/state-securecookie
Jun 5, 2026
Merged

refactor(auth): stateless OAuth2 state via securecookie#38
AYDEV-FR merged 1 commit into
mainfrom
refactor/state-securecookie

Conversation

@AYDEV-FR

@AYDEV-FR AYDEV-FR commented Jun 5, 2026

Copy link
Copy Markdown
Owner

Phase C of the auth-code rationalisation. Drops the hand-rolled state map + RWMutex + cleanup goroutine; replaces them with a signed self-contained blob carried in the OAuth2 state parameter itself.

What's gone

Removed Why
states map[string]*StateData The state IS the data now — packed into the signed string we hand to the IdP.
statesMu sync.RWMutex No shared mutable state to protect.
cleanupStates goroutine + stateCleanupEvery constant Expiry is embedded in the blob, the lib enforces it on Decode.
crypto/rand + encoding/base64 + sync imports The lib handles all of those.

What's new

github.com/gorilla/securecookie — battle-tested HMAC + AES helper. Encode("dploy-state", blob) returns the URL-safe signed string we use as state=…; Decode(…) verifies + returns the blob. About 8 lines of integration.

Trade-offs vs the in-memory map (same as today)

  • Pod restart: in-flight logins fail closed. Identical to the current behaviour (the map was lost too).
  • Replay within TTL: theoretically possible (no nonce store to detect reuse) where the old code deleted on first use. Mitigated by the IdP's own one-time-code semantics + 10-min TTL. Acceptable for the threat model — can add a nonce later if needed.

Stable keys (future)

Process-random keys mean restart loses keys. Promoting them to env vars or a k8s Secret is a one-line follow-up that would also let multiple replicas share state across restarts. Out of scope for this PR.

Stats

oidc.go 252 → 234 lines (-18)
Combined with phases A+B (PRs #36 + #37) 655 → 319 (-336, -51%)

Build green, existing internal/auth + internal/handlers tests pass.

Drops the in-memory state map, the RWMutex protecting it and the
cleanup goroutine. The OAuth2 `state` parameter is now a self-contained
signed blob — `securecookie.Encode` packs (returnURL, expiry) into the
string we hand to the IdP, `Decode` verifies the signature and pulls
them back out on callback. No server-side bookkeeping.

Removed:
- states map[string]*StateData + statesMu sync.RWMutex
- cleanupStates goroutine + stateCleanupEvery constant
- StateData struct (replaced by tiny private stateBlob)
- crypto/rand + encoding/base64 + sync imports

Added:
- github.com/gorilla/securecookie (battle-tested; the lib handles all
  the HMAC + AES details and the MaxAge bookkeeping)

Trade-offs vs the in-memory map:

- Pod restart: same behaviour. Both the in-memory map AND the
  process-random securecookie keys are lost on restart, so in-flight
  logins fail closed either way. Stable keys (env or k8s secret) is a
  one-line follow-up that would also let multiple replicas share state.

- Replay within TTL: theoretically possible with the new code (no
  nonce store to detect reuse), where the old code deleted on first
  use. Mitigated by the IdP's own one-time-code semantics and the
  10-min TTL. Acceptable for the threat model; can add a nonce later
  if needed.

Stats:
- oidc.go: 252 -> 234 lines (-18)
- Combined with phases A+B: 655 -> 319 (-336, -51% of pre-refactor
  auth code)
@AYDEV-FR
AYDEV-FR merged commit f261b0c into main Jun 5, 2026
6 of 7 checks passed
@AYDEV-FR
AYDEV-FR deleted the refactor/state-securecookie branch June 5, 2026 16:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant