sec(api): renderAuthError Cache-Control: no-store + lang=en (BUG-API-257/404)#196
Merged
Merged
Conversation
df0da68 to
cfddf22
Compare
…I-257/404)
The OAuth / magic-link callback HTML is per-request session-bound state
(the underlying magic-link / OAuth code has been consumed or expired by
the time the error page renders). Two compounding gaps shipped:
1. BUG-API-404 — no Cache-Control. A back-button, service-worker
re-fetch, or intermediate proxy could replay the body, leaking the
"you tried this link" UX state across sessions. Stamp
Cache-Control: no-store (RFC 9111 §5.2.2.5) which is the strongest
stop-cache directive and matches the contract every other
auth-result surface in the api already follows.
2. BUG-API-257 — <html> shipped with no `lang` attribute. WCAG 3.1.1
("Language of Page") requires a programmatically determinable
primary language; assistive tech (VoiceOver, NVDA) falls back to
the OS locale otherwise, mispronouncing English copy in non-English
locales. Pin lang="en" to match the static English-only copy.
Both fix at the single renderAuthError sink — fanning out across every
OAuth / magic-link callback error path (~20 call sites in auth.go +
magic_link.go) without scattering c.Set / template edits.
Coverage block:
Symptom: OAuth/magic-link error HTML missing Cache-Control: no-store
(BUG-API-404) and lang attribute (BUG-API-257)
Enumeration: rg -F 'renderAuthError' internal/handlers/ (~25 sites)
All flow through a single emit point in auth.go:1113
Sites found: 1 emit function (~25 callers)
Sites touched: 1 (single sink fix)
Coverage test: TestAuth_RenderAuthError_StatusAndContentType now pins
- Cache-Control: no-store
- body contains `<html lang="en">`
so a future revert of either fails before merge.
Live verified: pending auto-deploy + curl -sI
'https://api.instanode.dev/auth/email/callback?token=invalid'
| grep -i cache-control
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
cfddf22 to
602e311
Compare
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
The OAuth / magic-link callback HTML is per-request session-bound state (the underlying token has been consumed or expired). Two compounding gaps:
Both fix at the single `renderAuthError` sink, fanning out across ~25 OAuth / magic-link callback paths without scattering edits.
Coverage block
```
Symptom: OAuth/magic-link error HTML missing Cache-Control: no-store
(BUG-API-404) and lang attribute (BUG-API-257)
Enumeration: rg -F 'renderAuthError' internal/handlers/ (~25 sites)
All flow through a single emit point at auth.go:1113
Sites found: 1 emit function (~25 callers)
Sites touched: 1 (single sink fix — rule 18 single-source guarantee)
Coverage test: TestAuth_RenderAuthError_StatusAndContentType pins
- resp.Header.Get("Cache-Control") == "no-store"
- body contains '<html lang="en">'
so a future revert of either fails before merge.
Live verified: pending auto-deploy + curl -sI
'https://api.instanode.dev/auth/email/callback?token=invalid'
| grep -i cache-control
```
Inbox reference
Test plan
🤖 Generated with Claude Code