Skip to content

P1: identity, roles, RBAC - #1

Merged
sujitd7 merged 3 commits into
mainfrom
p1-identity-rbac
Aug 28, 2026
Merged

P1: identity, roles, RBAC#1
sujitd7 merged 3 commits into
mainfrom
p1-identity-rbac

Conversation

@sujitd7

@sujitd7 sujitd7 commented Aug 28, 2026

Copy link
Copy Markdown
Owner

P1 — Identity, roles, RBAC

The auth layer every later phase builds on. Closes the P1 checklist in docs/ROADMAP.md.

Models & migration

  • users (email, full_name, password_hash, role, team, is_active) and refresh_tokens (jti, expires_at, revoked_at).
  • Alembic migration bd693f8a6bb0 — creates/drops the role and team Postgres enums explicitly, tested downgrade(). Round-trips clean on SQLite; CI runs it on Postgres.

Security

  • app/security.py — Argon2 password hashing (with a constant dummy hash so an unknown-email login takes the same time as a real one), HS256 JWT encode/decode. Tokens carry a type claim (access / refresh) that is checked, plus required exp / sub / type.
  • app/config.py — the API refuses to boot with the placeholder JWT_SECRET (or anything under 32 chars) unless ENVIRONMENT is dev / test / local.

Auth flows

  • POST /auth/login, GET /auth/me.
  • POST /auth/refresh — rotating refresh tokens. The row is locked for the transaction; replay of an already-rotated token is detected and burns the whole token family, committed in its own unit of work so the revocation survives the 401 that follows.
  • POST /auth/logout — authenticated; only revokes a refresh token the caller owns.
  • Login and refresh failures all return one generic 401 (no account-existence leak).

RBAC

  • Role / Team enums; require_roles(...) dependency in app/deps.py (declared on the route, never in handler bodies). get_current_user guards a malformed sub and rejects inactive users.
  • app/errors.pyAppError hierarchy rendered as {"detail": ...} JSON, wired in create_app().

Tests — 35, ruff + mypy --strict + pytest green

  • tests/test_auth.py — login ok/bad/inactive, expired + wrong-type tokens, rotation, reuse-family-burn (incl. a variant using real per-request sessions to prove the burn is committed), logout ownership, and a per-role probe matrix against test-only /_probe/* routes.
  • tests/test_config.py — the strong-secret boot check.
  • conftest.pymake_user factory, per-role user + auth_* header fixtures.

Notes

  • A prior security-review pass drove the fixes above (reuse-detection commit, secret fail-fast, row locking, timing on disabled accounts, claim requirements, authenticated logout).
  • Deferred P0 infra items (branch protection, docker compose up verification — Docker is blocked on the dev machine) are marked as such in docs/ROADMAP.md; backend tests run on SQLite locally and the CI Postgres service.

🤖 Generated with Claude Code

sujitd7 and others added 3 commits August 28, 2026 21:04
Adds the auth layer the later phases build on.

Models & migration
- users (email, full_name, password_hash, role, team, is_active) and
  refresh_tokens (jti, expires_at, revoked_at); Alembic migration bd693f8a6bb0
  with a PG-appropriate enum create/drop and a tested downgrade.

Security
- app/security.py: Argon2 password hashing (+ a dummy hash so unknown-email
  login timing matches), HS256 JWT encode/decode with a checked `type` claim
  and required exp/sub/type claims.
- app/config.py: refuses to boot with the placeholder JWT secret (or one under
  32 chars) unless ENVIRONMENT is dev/test/local.

Auth flows (app/services/auth.py, app/routers/auth.py)
- POST /auth/login, GET /auth/me
- POST /auth/refresh: rotating refresh tokens with row locking; replay of a
  rotated token is detected and burns the whole family, committed in its own
  unit of work so it survives the 401.
- POST /auth/logout: authenticated; only revokes a token the caller owns.
- Credential/refresh failures all return one generic 401.

RBAC
- Role/Team enums; require_roles(...) dependency in app/deps.py (re-exports
  Role). get_current_user guards a malformed `sub` and checks is_active.
- app/errors.py: AppError hierarchy -> JSON, wired in create_app().

Tests
- tests/test_auth.py (login, /me, rotation + reuse, logout, per-role probe
  matrix via test-only /_probe/* routes), tests/test_config.py, conftest
  fixtures (make_user factory, per-role users + auth_* headers). 35 tests,
  ruff + mypy --strict + pytest green (SQLite locally / CI Postgres).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…rkflow

- README: mermaid architecture diagram, backend layering + domain-rule
  highlights, RBAC matrix, a phase-by-phase roadmap progress table, and a
  section on the guard-railed Claude Code workflow (hooks / subagents / skills /
  loop) with the real P1 security-reviewer catch as evidence.
- STATUS / ai-workflow: record the P1 security-review findings and fixes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@sujitd7
sujitd7 merged commit 777d8a3 into main Aug 28, 2026
3 checks passed
@sujitd7
sujitd7 deleted the p1-identity-rbac branch August 28, 2026 16:04
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.

1 participant