feat: ACCESS_CODE site-level authentication (#407)#411
Merged
Conversation
Returns whether ACCESS_CODE is enabled and whether the current user is authenticated via cookie, to support the frontend access gate. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Creates POST /api/access-code/verify that validates ACCESS_CODE env var and sets an HttpOnly 7-day cookie on success. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Intercepts all requests when ACCESS_CODE env var is set. API routes return 401 without the openmaic_access cookie; page requests pass through so the frontend can render the access-code modal. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add accessCode top-level key to en-US, zh-CN, ja-JP, and ru-RU locale files with title, placeholder, submit, and error strings. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Wraps page children with an access-code gate that checks /api/access-code/status on mount and renders AccessCodeModal when authentication is required. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds the modal dialog component that prompts users for an access code, using existing Dialog/Button/Input UI components and i18n translation keys. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix useEffect setState pattern in access-code-guard.tsx - Format middleware.ts and layout.tsx with Prettier Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace generic Radix Dialog with a custom full-screen gate: - Mesh gradient background with noise texture - Frosted glass card with backdrop-blur - Staggered motion animations on mount - Inline submit button inside input field - Success state animation before dismissal - Proper dark mode support Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Critical: - Middleware now validates HMAC-signed cookie tokens, not just existence - Verify endpoint uses timingSafeEqual for constant-time comparison - Cookie value is now `timestamp.HMAC(ACCESS_CODE, timestamp)` instead of UUID Important: - Middleware 401 response includes `errorCode` matching apiError shape - Status endpoint uses apiSuccess wrapper for consistency - Remove unused `accessCode.submit` i18n key from all 4 locales Suggestions: - Guard renders children during loading (overlay on top, not blocking) - Network error defaults to enabled=true (safer than silently disabling) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Node.js crypto module is not available in Next.js Edge Runtime. Replace createHmac/timingSafeEqual with crypto.subtle HMAC and manual constant-length XOR comparison. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
cosarah
approved these changes
Apr 12, 2026
Collaborator
cosarah
left a comment
There was a problem hiding this comment.
LGTM! Clean architecture and solid security fundamentals (HMAC-signed tokens, timingSafeEqual, httpOnly/secure cookies, fail-closed error handling).
Two minor notes for awareness (non-blocking):
-
Token has no server-side expiry check — the HMAC verification only validates the signature, not the timestamp age. The cookie
maxAgehandles this in practice, but an extracted token would remain valid indefinitely as long asACCESS_CODEstays the same. -
Duplicate HMAC verification logic —
verify/route.ts(Nodecrypto) andmiddleware.ts(Web Crypto API) implement the same token verification independently. Necessary for Edge Runtime compatibility, but worth keeping in mind if the token format ever 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
ACCESS_CODEenv var that gates access to the entire appHow it works
openmaic_accesscookie/api/access-code/verify, which sets an HMAC-signed HttpOnly cookie (7-day TTL)ACCESS_CODEis not set, middleware exits immediately — zero overheadNew files (10)
middleware.tsapp/api/access-code/verify/route.tsapp/api/access-code/status/route.tscomponents/access-code-guard.tsxcomponents/access-code-modal.tsxModified files
app/layout.tsx— wrap children with<AccessCodeGuard>.env.example— addACCESS_CODElib/i18n/locales/*.json(4 files) — addaccessCode.*translation keys (en-US, zh-CN, ja-JP, ru-RU)Security
timestamp.HMAC-SHA256(ACCESS_CODE, timestamp)— never the plaintext codetimingSafeEqual)Code Review Summary
Reviewed by automated code reviewer. All Critical and Important findings addressed:
timingSafeEqualin verify endpointerrorCodeapiErrorshapeapiSuccessaccessCode.submiti18n keyenabled: trueon errorcryptonot available in Edge RuntimeTest plan
ACCESS_CODE=test123in.env.local, start dev servercurl /api/verify-modelwithout cookie → 401ACCESS_CODEfrom env, restart → no modal, all APIs workCloses #407
Related: #287, Discussion #293, Roadmap #406
🤖 Generated with Claude Code