--- # pages-zd46 title: GitHub OAuth, sessions, auth middleware status: completed type: feature priority: normal created_at: 2026-07-13T02:09:08Z updated_at: 2026-07-13T16:29:44Z parent: pages-77lk blocked_by: - pages-jgc4 --- Implement design doc §2.4 + §4.1 auth rows. src/auth.rs: pure helpers compiled on both targets — random 32-byte hex token gen (getrandom), sha256 hex (add sha2 crate, works on wasm), cookie header building/parsing (__Host-session HttpOnly Secure SameSite=Lax Path=/ Max-Age 30d; state cookie 10min), GitHub authorize URL builder — all native-tested. src/routes/auth.rs (wasm-only): GET /auth/login (state cookie + redirect), GET /auth/callback (verify state, exchange code via worker Fetch to github.com with Accept: application/json + User-Agent header, fetch /user, upsert user via db.rs, create session, redirect /), POST /auth/logout (delete session, clear cookie). Request extension/helper current_user(env, req) for other routes. Wire routes into lib.rs router. - [x] auth.rs pure helpers + native tests (cookie parse/build, authorize URL, token/hash) - [x] routes/auth.rs OAuth flow - [x] current_user session resolution helper - [x] full validation suite passes ## Summary of Changes GitHub OAuth + sessions by a Sonnet agent (resumed once after a session-limit interruption; #[worker::send] fixed the axum Send bound). Opus security review: APPROVE. - src/auth.rs: token gen (getrandom), sha256_hex, __Host- cookie build/parse (zero-alloc), authorize URL with hand-rolled percent-encoding, hand-implemented civil_from_days expiry formatting — 38 new unit tests + 9 doctests. - src/routes/{mod,auth}.rs: login/callback/logout, state-cookie CSRF binding, hashed session storage, generic 502s (no secret leakage), current_user helper. AppState (Db + Config) via with_state. - Review fix applied: admin login comparison now eq_ignore_ascii_case (GitHub logins are case-insensitive). 110 unit tests + 14 doctests green; both targets clippy-clean.