Skip to content

feat(auth): user management and access control#133

Merged
danielnaab merged 7 commits intomainfrom
story-118/user-management
May 7, 2026
Merged

feat(auth): user management and access control#133
danielnaab merged 7 commits intomainfrom
story-118/user-management

Conversation

@danielnaab
Copy link
Copy Markdown
Member

Summary

  • Add hybrid access control: flexion.us email auto-approves, external users go through request/approval workflow
  • Admin page at /admin/users for managing access (approve/deny/revoke/add users)
  • Slack notifications on access requests, mid-session revocation enforcement

Story

Closes #118

Acceptance Criteria

  • A decision is documented on the access control model (hybrid: domain auto-approve + request/approval for external users) — see notes/story-118-user-management/design.md
  • The chosen model is implemented and tested — three-layer authorization (env var → domain → database), 27 new tests
  • Admins can see who has accessed the platform — admin page at /admin/users shows all users by status
  • The approach is simple enough that it doesn't create friction for legitimate users — flexion.us users auto-approve silently on sign-in

Test Plan

  • bun run check passes (1381 tests, lint, typecheck)
  • Sign in with flexion.us email → auto-approved, dashboard shown
  • Visit /admin/users as admin → see user list with approve/deny/revoke/add
  • Add user manually → appears in approved list
  • Revoke user → moves to revoked section
  • Deployed and verified at branch URL

Review Notes

Authorization model: Three layers checked in order during OAuth callback:

  1. ALLOWED_USERS env var (bootstrap/emergency access)
  2. flexion.us domain bypass (hardcoded, not configurable)
  3. user_access SQLite table (request/approval workflow)

New env var: ADMIN_USERS (comma-separated GitHub logins, defaults to danielnaab)

Files: 13 files changed, ~1,300 lines added. Core changes in src/services/auth/access-store.ts (new), src/entrypoints/app/middleware/auth.ts (modified), src/entrypoints/app/routes/auth/index.tsx (rewritten callback), src/entrypoints/app/routes/admin/ (new).

@danielnaab danielnaab temporarily deployed to story-118-user-management May 6, 2026 12:36 Inactive
danielnaab added 7 commits May 6, 2026 19:46
SQLite-backed store managing user_access table with status
(approved/pending/revoked) and source tracking (domain/env/admin/request).
Supports request, approve, deny, revoke, and auto-approve workflows.

Part of #118
requireAdmin gates routes to ADMIN_USERS env var (defaults to danielnaab).
requireAuth now optionally accepts an AccessStore and rejects revoked users
by clearing their session cookie.

Part of #118
OAuth callback now checks: (1) ALLOWED_USERS env var, (2) flexion.us
domain bypass (hardcoded), (3) user_access database table. Unknown
external users are redirected to request access. Pending and revoked
users see appropriate status pages.

Part of #118
GET /admin/users shows pending, approved, and revoked users.
POST actions for approve, deny, revoke, and add.
Protected by requireAdmin middleware (ADMIN_USERS env var).

Part of #118
Create access store alongside user store in server.tsx. Mount admin
routes at /admin with requireAuth guard. Replace raw HTML in auth
and admin pages with design-system JSX components.

Part of #118
Thread accessStore through to requireAuth() calls in forms and
settings routes, not just /new and /admin. Previously a revoked
user with a valid session could still access form and settings
routes.

Part of #118
Show "Admin" nav item in the header when the logged-in user is in
ADMIN_USERS. Derives admin status from user.login + env var in
Layout, avoiding changes to 70+ Layout call sites. Also extracts
parseAdminUsers helper in middleware to reduce duplication.

Part of #118
@danielnaab danielnaab force-pushed the story-118/user-management branch from 21217c2 to 4dba0cb Compare May 6, 2026 19:47
@danielnaab danielnaab temporarily deployed to story-118-user-management May 6, 2026 19:47 Inactive
@danielnaab danielnaab merged commit 9e0a05d into main May 7, 2026
4 checks passed
@danielnaab danielnaab deleted the story-118/user-management branch May 7, 2026 03:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Revisit user management and access control policy

1 participant