feat(auth): add os_session cookie + dual-auth on /api/calls/:id/audio#25
Merged
feat(auth): add os_session cookie + dual-auth on /api/calls/:id/audio#25
Conversation
- New auth.SetSessionCookie / ClearSessionCookie helpers (HttpOnly, Secure when HTTPS, SameSite=Strict, Path=/api) - POST /api/auth/login and /api/auth/refresh issue os_session alongside the existing access JWT response; POST /api/auth/logout clears it - New middleware.OptionalJWTOrSessionAuth resolves identity from, in priority order: bearer header, os_session cookie (guarded by Sec-Fetch-Site), anonymous - GET /api/calls/:id/audio swapped to the new middleware; every other route is unchanged. Bearer flow continues to work everywhere. - Routes.RegisterRoutes now promotes deps.Hub into the WSDisconnecter interface only when the concrete pointer is non-nil, fixing a pre-existing typed-nil interface footgun on the logout path. Tests: - backend/internal/auth/cookie_test.go: SetSessionCookie / ClearSessionCookie flag matrix - backend/internal/handler/routes/auth_test.go: login/refresh/logout cookie issuance and rotation - backend/internal/handler/routes/audio_test.go (new): full dual-auth matrix on the audio route — bearer, cookie+same-origin, cookie+missing Sec-Fetch-Site, cookie+cross-site (publicAccess on/off), stale cookie fallthrough (publicAccess on/off), anonymous (publicAccess on/off) go vet, go build, go test ./... all clean. Backwards compatible — no frontend changes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds an httpOnly Secure SameSite=Strict session cookie (
os_session) so that<audio src=…>requests can authenticate without bearer-header injection, while keeping today's bearer flow working unchanged on every route. Backwards compatible — no client changes in this PR.Why
Browsers cannot attach
Authorization: Bearerto<audio src=…>without going throughfetch()+Blob+URL.createObjectURL— exactly the path that fails on Mobile Edge for AAC (Web Audio'sdecodeAudioDataships codec gaps in Edge's Chromium fork). Switching the audio path to a real URL is the architecturally correct fix; this PR adds the auth piece so a follow-up frontend PR can simply set<audio src="/api/calls/:id/audio">and have it work.Auth flow on
/api/calls/:id/audiosame-origin/same-site/none/ absentcross-sitecross-siteStale credentials never 401 directly — they fall through to anonymous so
publicAccess=truesemantics keep working. CSRF is mitigated by theSameSite=Strictcookie attribute plus a server-sideSec-Fetch-Sitecheck.Files
backend/internal/auth/cookie.goSetSessionCookie,ClearSessionCookie,SessionCookieName,SessionCookiePathbackend/internal/auth/cookie_test.gobackend/internal/handler/auth/auth.gobackend/internal/middleware/auth.goOptionalJWTOrSessionAuthbackend/internal/handler/routes/routes.go/calls/:id/audio; fix typed-nil interface footgun on hubbackend/internal/handler/routes/auth_test.gobackend/internal/handler/routes/audio_test.go(new)CHANGELOG.mdVerification
go vet ./...— cleango build ./...— cleango test ./...— all pass (including the new dual-auth matrix)git diff --stat dev..HEAD backend/only; no frontend touchedSelf-review against OWASP
auth.ParseTokenandauth.Tokens.IsRevokedapply. Per-call grants are still enforced insideGetCallAudio. No privilege escalation surface added.OptionalJWTAuthbehaviour) — this preservespublicAccess=truesemantics and never leaks a 401 that would mask a still-valid session. Cookie rotates on every refresh; cleared on logout.SameSite=Strict+ server-sideSec-Fetch-Sitecheck. The route is read-only (GET); the worst case from a successful CSRF on this route is 'attacker causes the victim's browser to GET audio the victim was already authorised for' — no state change, no exfiltration channel beyond what the victim already has.What's next
Phase 2 (backend: drop audio bytes from the WS
CALpayload) and Phase 3 (frontend: switch playback to<audio src=…>) ship as a paired follow-up, merged sequentially. This PR is independently mergeable today.