Commit Graph
1 Commits
Author SHA1 Message Date
c071e02d83 feat: ACCESS_CODE site-level authentication (#407) (#411)
* feat(access-code): add status API endpoint

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>

* feat(access-code): add verify API endpoint

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>

* feat(access-code): add Next.js middleware to gate API routes

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>

* feat(i18n): add accessCode translations for all 4 locales

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>

* docs: add ACCESS_CODE documentation to .env.example

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: add AccessCodeGuard component and integrate into root layout

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>

* feat(access-code): add AccessCodeModal component

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(access-code): fix lint error and format files

- 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>

* style(access-code): redesign modal as full-screen gate

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>

* fix(access-code): address code review findings

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>

* fix(access-code): use Web Crypto API in middleware for Edge Runtime

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>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: 杨慎 <117187635+cosarah@users.noreply.github.com>
2026-04-12 22:14:14 +08:00