Files
support_backend/specs/010-identity-auth/quickstart.md
T
saqib mirandClaude Sonnet 5 3b4c220a45 plan: design for identity and authentication feature (010)
Phase 0 research resolves the library choices (jsonwebtoken + bcryptjs,
chosen partly to avoid native-build friction on Windows dev
environments), the Redis-backed revocation-denylist shape (reusing
002's own jti-replay-protection pattern exactly), a 4-hour token
lifetime, and why fastify.authenticate populating the already-shared
reqContext.actorId/actorType retroactively makes every audit trail
since 007 accurate for real agent/admin actions instead of always
'unknown'.

Also surfaces and scopes a real gap found along the way: User (login
identity) and Agent (routing/skills profile) have never been linked.
Adds Agent.userId as a nullable FK now (cheap, additive) without
building the actual linking workflow, which belongs in 006's own
identity/agents admin screens as a later, separate piece of work.

Phase 1 adds data-model.md, the login/self-identity/account-creation/
logout contract, and five quickstart scenarios including a specific
requirement to re-verify at least one already-shipped admin route per
module (002-009), not just this feature's own new endpoints.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-07 11:14:42 +05:30

2.7 KiB

Quickstart: Validating Identity and Authentication

Prerequisites: migrations applied; npm run prisma:seed run so the two demo accounts exist with their new real passwords (documented in the seed script itself, local/dev use only).

Scenario 1 — login (User Story 1)

  1. POST /auth/login with the seeded admin's correct email/password. Expected: 200, a token, and {id, email, name, role: 'ADMIN'}.
  2. Repeat with the correct email but a wrong password. Expected: 401.
  3. Repeat with an email that doesn't exist at all. Expected: the exact same 401 body/ status as step 2 — diff the two responses to confirm they're indistinguishable.

Scenario 2 — route gating and role enforcement (User Story 2)

  1. Call an existing admin route (e.g. POST /admin/teams) with no Authorization header. Expected: 401.
  2. Repeat with a malformed token (Bearer not-a-real-token). Expected: 401.
  3. Log in as the seeded agent (role AGENT); call an admin-only route gated by requireRole('ADMIN'). Expected: 403.
  4. Log in as the seeded admin; repeat step 3's call. Expected: 200/201 (whatever that route normally returns on success).

Scenario 3 — self-identity (User Story 3)

  1. Log in; call GET /auth/me with the resulting token. Expected: 200, identity matches the login response exactly.
  2. Directly deactivate that account (active: false) via a direct DB update (simulating an admin action no UI exists for yet); repeat the same GET /auth/me call with the same, still-unexpired token. Expected: 401 — re-validated against current account state, not the token's own claims.

Scenario 4 — admin creates an account (User Story 4)

  1. Log in as admin; POST /admin/users with a new email/name/role AGENT/password. Expected: 201, response never includes the password or its hash.
  2. Log in as a non-admin (the seeded agent); repeat step 1. Expected: 403.
  3. Immediately log in as the newly-created account with the password from step 1. Expected: 200 — no manual step needed in between.
  4. Repeat step 1 with an email already in use. Expected: 409.

Scenario 5 — logout (User Story 5)

  1. Log in; call POST /auth/logout with the resulting token. Expected: 200.
  2. Immediately reuse that same token on any gated route. Expected: 401 — rejected even though it hasn't naturally expired.

What "done" looks like

All five scenarios pass, and Scenario 2 is additionally verified against at least one already-shipped admin route from each of 002-009 (not just a route this feature itself adds), proving the real gate actually protects what the no-op stub never did.