Add admin magic-link login and sessions #58

Merged
erik merged 7 commits from feat/task-8af07ab8-admin-magic-link-sessions into main 2026-05-22 13:52:43 -05:00
Owner

Summary

  • Add admin_magic_links and admin_sessions SQLite migration.
  • Add ADMIN_EMAIL-based magic-link bootstrap auth with hashed single-use expiring tokens.
  • Add dev-mode magic-link delivery logging and fail-closed production delivery boundary.
  • Add server-side admin sessions, session cookie helpers, and protected admin middleware.
  • Add /login, /login/verify, /logout, and protected /admin routes.
  • Add tests for login, authorization non-disclosure, token verification/reuse/expiry failures, protected admin access, and logout.

Testing

  • make check
  • ./scripts/pre-pr.sh
  • Manual smoke: migrated temp DB, started template site, checked GET /login returns 200 and anonymous GET /admin redirects to /login.

Task: #task-8af07ab8

## Summary - Add `admin_magic_links` and `admin_sessions` SQLite migration. - Add ADMIN_EMAIL-based magic-link bootstrap auth with hashed single-use expiring tokens. - Add dev-mode magic-link delivery logging and fail-closed production delivery boundary. - Add server-side admin sessions, session cookie helpers, and protected admin middleware. - Add `/login`, `/login/verify`, `/logout`, and protected `/admin` routes. - Add tests for login, authorization non-disclosure, token verification/reuse/expiry failures, protected admin access, and logout. ## Testing - `make check` - `./scripts/pre-pr.sh` - Manual smoke: migrated temp DB, started template site, checked `GET /login` returns 200 and anonymous `GET /admin` redirects to `/login`. Task: #task-8af07ab8
feat: add admin magic-link sessions
All checks were successful
CI / build-lint-test (pull_request) Successful in 27s
0b75a6a4d0
Task: #task-8af07ab8
Author
Owner

PR Review: Approved

Summary

Reviewed PR #58 at commit 0b75a6a. The PR implements the magic-link bootstrap slice: ADMIN_EMAIL is the owner allow-list, magic-link tokens are hashed, expiring, and single-use, development delivery logs the link, production delivery fails closed when not configured, server-side sessions and cookies are added, and /login, /login/verify, /logout, and protected /admin routes are wired with tests.

Acceptance Criteria

  • GET /login renders an email login form.
  • Authorized POST /login creates a hashed, expiring, single-use magic-link record and exposes the raw link only through dev logging or the configured delivery boundary.
  • Unauthorized POST /login does not create a usable magic link and does not reveal authorization status to the requester.
  • GET /login/verify with a valid token creates a server-side session and sets a secure cookie.
  • Reusing, expiring, malformed, or unknown magic-link tokens does not create a session.
  • GET /admin redirects anonymous users to /login.
  • GET /admin succeeds for authenticated owner sessions.
  • Logout deletes the server-side session and clears the browser cookie.
  • Tests cover authorized login request, unauthorized login request, successful verification, single-use token behavior, expired token behavior, protected admin access, and logout.
  • Relevant lint/test checks pass — make check, ./scripts/pre-pr.sh, manual smoke, and Forgejo CI passed.

Blocking Issues

None.

Warnings

None.

Verdict

Approved for merge.

## PR Review: Approved ### Summary Reviewed PR #58 at commit `0b75a6a`. The PR implements the magic-link bootstrap slice: `ADMIN_EMAIL` is the owner allow-list, magic-link tokens are hashed, expiring, and single-use, development delivery logs the link, production delivery fails closed when not configured, server-side sessions and cookies are added, and `/login`, `/login/verify`, `/logout`, and protected `/admin` routes are wired with tests. ### Acceptance Criteria - [x] `GET /login` renders an email login form. - [x] Authorized `POST /login` creates a hashed, expiring, single-use magic-link record and exposes the raw link only through dev logging or the configured delivery boundary. - [x] Unauthorized `POST /login` does not create a usable magic link and does not reveal authorization status to the requester. - [x] `GET /login/verify` with a valid token creates a server-side session and sets a secure cookie. - [x] Reusing, expiring, malformed, or unknown magic-link tokens does not create a session. - [x] `GET /admin` redirects anonymous users to `/login`. - [x] `GET /admin` succeeds for authenticated owner sessions. - [x] Logout deletes the server-side session and clears the browser cookie. - [x] Tests cover authorized login request, unauthorized login request, successful verification, single-use token behavior, expired token behavior, protected admin access, and logout. - [x] Relevant lint/test checks pass — `make check`, `./scripts/pre-pr.sh`, manual smoke, and Forgejo CI passed. ### Blocking Issues None. ### Warnings None. ### Verdict Approved for merge.
feat: log admin auth setup in dev
All checks were successful
CI / build-lint-test (pull_request) Successful in 25s
3d98a1db42
Task: #task-8af07ab8
chore: set default admin email
All checks were successful
CI / build-lint-test (pull_request) Successful in 26s
ffef5f7036
Task: #task-8af07ab8
fix: default admin email when unset
All checks were successful
CI / build-lint-test (pull_request) Successful in 27s
53084846f6
Task: #task-8af07ab8
fix: apply migrations on startup
All checks were successful
CI / build-lint-test (pull_request) Successful in 28s
0df710d4a6
Task: #task-8af07ab8
feat: log admin auth lifecycle
All checks were successful
CI / build-lint-test (pull_request) Successful in 32s
48a0a10ea0
Task: #task-8af07ab8
chore: ignore runtime data
All checks were successful
CI / build-lint-test (pull_request) Successful in 29s
ec095f4416
Task: #task-8af07ab8
Author
Owner

PR Review: Approved (updated)

Summary

Reviewed updated PR #58 through commit ec095f4. The PR now includes the original magic-link/session bootstrap plus the follow-up fixes found during manual testing: startup migrations run before serving, default admin email falls back to admin@slugkit.com, dev startup logs show the effective admin email, auth lifecycle events are logged, and runtime SQLite data is ignored.

Acceptance Criteria

  • GET /login renders an email login form.
  • Authorized POST /login creates a hashed, expiring, single-use magic-link record and exposes the raw link only through dev logging or the configured delivery boundary.
  • Unauthorized POST /login does not create a usable magic link and does not reveal authorization status to the requester.
  • GET /login/verify with a valid token creates a server-side session and sets a secure cookie.
  • Reusing, expiring, malformed, or unknown magic-link tokens does not create a session.
  • GET /admin redirects anonymous users to /login.
  • GET /admin succeeds for authenticated owner sessions.
  • Logout deletes the server-side session and clears the browser cookie.
  • Tests cover authorized login request, unauthorized login request, successful verification, single-use token behavior, expired token behavior, protected admin access, and logout.
  • Relevant lint/test checks pass — make check, ./scripts/pre-pr.sh, manual login smoke, and Forgejo CI passed.

Additional Verification

  • Startup applies pending migrations before Hono serves requests.
  • Dev startup logs show ADMIN_EMAIL=admin@slugkit.com when unset.
  • Magic-link request and successful login now emit clear auth lifecycle logs without tokens or session IDs.
  • Runtime data/ paths are ignored.

Blocking Issues

None.

Warnings

None.

Verdict

Approved for merge.

## PR Review: Approved (updated) ### Summary Reviewed updated PR #58 through commit `ec095f4`. The PR now includes the original magic-link/session bootstrap plus the follow-up fixes found during manual testing: startup migrations run before serving, default admin email falls back to `admin@slugkit.com`, dev startup logs show the effective admin email, auth lifecycle events are logged, and runtime SQLite data is ignored. ### Acceptance Criteria - [x] `GET /login` renders an email login form. - [x] Authorized `POST /login` creates a hashed, expiring, single-use magic-link record and exposes the raw link only through dev logging or the configured delivery boundary. - [x] Unauthorized `POST /login` does not create a usable magic link and does not reveal authorization status to the requester. - [x] `GET /login/verify` with a valid token creates a server-side session and sets a secure cookie. - [x] Reusing, expiring, malformed, or unknown magic-link tokens does not create a session. - [x] `GET /admin` redirects anonymous users to `/login`. - [x] `GET /admin` succeeds for authenticated owner sessions. - [x] Logout deletes the server-side session and clears the browser cookie. - [x] Tests cover authorized login request, unauthorized login request, successful verification, single-use token behavior, expired token behavior, protected admin access, and logout. - [x] Relevant lint/test checks pass — `make check`, `./scripts/pre-pr.sh`, manual login smoke, and Forgejo CI passed. ### Additional Verification - Startup applies pending migrations before Hono serves requests. - Dev startup logs show `ADMIN_EMAIL=admin@slugkit.com` when unset. - Magic-link request and successful login now emit clear auth lifecycle logs without tokens or session IDs. - Runtime `data/` paths are ignored. ### Blocking Issues None. ### Warnings None. ### Verdict Approved for merge.
erik merged commit 5ade972f59 into main 2026-05-22 13:52:43 -05:00
Sign in to join this conversation.
No description provided.