Evalium – Users, Authentication & Authorization Roadmap
This document describes the full roadmap for implementing Users, Authentication (AuthN), Authorization (AuthZ), RBAC, and security controls in Evalium. It supersedes all prior informal notes and brings everything into a single, coherent plan.
It is aligned with:
docs/architecturedocs/FOUNDATION.mddocs/security/roles-and-access-control.md- Evalium’s backend-first development strategy
0. Current State (as of last review)
Schema
userstable contains legacyrole text(Admin|Author|Viewer) — to be removed- RLS covers tenants only; org-unit isolation exists but is not applied to users
- No RBAC tables (roles, capabilities, mappings)
clerk_user_idremains unused
SQL Layer
- CRUD queries assume a single role column
- No capability or permission filtering
- No org-aware listing
- No linkage to roles/capabilities
HTTP Layer
- Users API has no authentication and no authorization checks
- Scoping relies on dev headers (
X-Tenant-Id,X-Org-Id) instead of real Auth - DTOs reflect legacy structure
Identity/Auth
- Magic link authentication not yet implemented
- JWT parsing & capability resolution not implemented
Tests
- Only a simple users CRUD test
- No authZ, RLS, RBAC, or integration tests for identity flows
1. Roadmap Overview
Evalium will implement users, authentication, and authorization in six milestones.
Each milestone produces:
✔ SQL migrations ✔ sqlc regen ✔ shell regression script ✔ Go integration test ✔ API documentation update ✔ CHANGELOG entry
Milestone 1 — RBAC Schema & Org Isolation Foundation
1.1 Add Required Tables
Create the RBAC schema:
rolescapabilitiesrole_capabilitiesuser_rolesuser_org_units(optional per-org role scoping)
Seed default roles & capabilities from:
docs/security/roles-and-access-control.md
1.2 Remove legacy users.role
Replace with a compatibility view temporarily:
users_effective_role AS …
(View optional for legacy test compatibility; will be removed later.)
1.3 RLS for Users
Important correction: Users must be isolated by tenant, NOT by org-unit.
Users may belong to multiple org units. Org scoping applies to resource access, not to the users table.
RLS for users should be:
tenant_id = current_setting('app.tenant_id')::uuid
1.4 Integration Tests
backend/tests/user_rbac_schema.shinternal/db/schema/user_rbac_test.go
Verifies:
- default roles/capabilities exist
- org RLS does not incorrectly filter users
- role-capability mapping works
- TxManager GUCs behave correctly
Milestone 2 — Authorization Service & Middleware
2.1 JWT Middleware (Decode Only)
Middleware should:
-
Verify token signature
-
Extract:
- userId
- tenantId
- orgUnitId
- roleIds
-
Attach these to context
-
DO NOT enforce capability checks here
2.2 Capability Resolution Service
A service that:
- maps
roleIds → capabilities - caches the result for fast lookup
- invalidates cache on role/capability updates
2.3 Require() Helper
All handlers must use:
auth.Require(ctx, capabilities.Users.Manage)
This avoids putting authorization logic in middleware.
2.4 Route Protection
UsersHandler must require:
users.readusers.manage
2.5 Tests
backend/tests/authz_middleware.shmiddleware/authz_test.go
Covers:
- missing/invalid JWT → 401
- insufficient capability → 403
- valid JWT with right capabilities → 200
Milestone 3 — Magic-Link Authentication (MVP Identity)
3.1 Magic Link Table
magic_links table must include:
- hashed token
- userId or email
- tenantId
- orgUnitId
- JTI
- expiry
- single-use flag
3.2 Issue & Redeem Endpoints
POST /auth/login
GET /auth/verify
3.3 JWT Issuance
JWT contains:
- userId
- tenantId
- orgUnitId
- roleIds
NOT included:
- capabilities[]
- tags
- evaluation data
- anything large or mutable
3.4 Org & Tenant Integrity
Magic link must be validated:
- against the tenant
- against org membership
- scope_all must NEVER be set by magic links
3.5 Tests
backend/tests/magic_links.shauth_magic_link_test.go
Tests:
- valid/invalid token
- single-use enforcement
- token expiry
- authorized request after redeem
Milestone 4 — Roles & Capabilities Management APIs
API Surface
POST /roles
GET /roles
PATCH /roles/\{id\}
DELETE /roles/\{id\}
POST /users/\{id\}/roles
DELETE /users/\{id\}/roles/{roleId}
GET /capabilities
Must enforce:
- default system roles cannot be deleted
- Billing Admin has restricted capabilities
- Org-bound role assignment (future-ready)
Tests
backend/tests/roles_crud.shroles_integration_test.go
Milestone 5 — Platform-Wide Permission Enforcement
Add capability checks to:
- questions
- question_versions
- evaluations
- evaluation_versions
- assignments
- sessions
- submissions
- reporting endpoints
- RLS-backed resources
Error Model
- 401 → missing/invalid token
- 403 → insufficient capabilities
Tests
permissions_regression.shquestions_authz_test.goevaluations_authz_test.goassignments_authz_test.go- etc.
Milestone 6 — Custom Roles, Caching & Security Hardening
6.1 Custom Roles
Tenants can:
- clone default roles
- edit capabilities
- assign to users
6.2 Caching Layer
Cache:
- capabilities for roleIds
- role lists
- magic link validations
Invalidate on:
- role update
- capability update
- manual admin flush
6.3 Audit Logs
Emit audit events for:
- login
- role assignment
- capability changes
- permission-denied events
6.4 Optional features
- per-org role binding
- role expiration (guest auditor)
- scoped magic-link reporting roles
Where this roadmap fits in Evalium’s overall architecture
This roadmap is aligned with:
• RLS-first isolation • TxManager-only DB access • Versioned immutable authoring • Backend-first development • Capability-based authorization • Passwordless authentication • Hard org-unit silos
These constraints are described in:
docs/architecturedocs/FOUNDATION.mddocs/security/roles-and-access-control.md
This document adds:
- a realistic implementation sequence
- the correct boundaries between authN/authZ
- safe JWT strategy
- developer-safe patterns
Final Notes
This roadmap is:
✔ production-grade ✔ test-driven ✔ enterprise-safe ✔ aligned to Evalium’s backend-first philosophy ✔ future-proof (custom roles, reviewer flows, auditors, SSO later)