Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

01 — Product Overview (Auth Module)

StudyLyon — multi-tenant ERP / School Management API. This package designs the Auth module client (Flutter, forward-looking spec) against the implemented NestJS backend. All endpoints, DTO fields, schemas, events, rate limits, and wire contracts are derived directly from src/modules/auth/**, src/common/**, src/modules/rbac/**, and src/infrastructure/**. No feature is invented; gaps are flagged in the Assumptions & Open Questions section.


1. Purpose

Auth is the entry gate to every StudyLyon surface. It establishes who you are (identity), which tenant you act in (multi-tenancy), which roles/permissions you carry (RBAC), and manages the security artifacts that keep every subsequent request safe: JWT access/refresh token pairs, session registry, password credentials (Argon2id), email verification, TOTP 2FA, and scoped API keys for machine access.

ResponsibilitySource
Email + password login → JWT access/refresh pairauth.service.ts:123 login()
Refresh rotation with replay protectionauth.service.ts:168 refresh()
Logout / logout-all / session revocation (device mgmt)auth.service.ts:199-206, 387-394
Registration (first org admin), org seeding, welcome emailauth.service.ts:54 register()
Email verification + resendauth.service.ts:208 verifyEmail()
Forgot / reset password (1 h token; revokes all sessions)auth.service.ts:256, 285
TOTP 2FA enable / verify / disableauth.service.ts:320-385
API keys issue / list / revoke (key shown once)auth.service.ts:396-441
Brute-force counters (failed attempts, lock field)auth.service.ts:133-143, auth-account.schema.ts:30-34
Domain events → BullMQ (email, audit)auth-events.ts, event-queue-map.ts:7-13, email.worker.ts

2. Business goals

GoalMeasure
First log-in in secondsLogin round-trip < 1.5 s p95 (auth-account + RBAC role lookup + token sign)
Zero account-existence leaksidentical 401 message for unknown email vs wrong password (auth.service.ts:125,131,141)
Safe session lifecyclerefresh rotation: old session deleted before new pair issued (auth.service.ts:190-196)
No usable credential leaked at restArgon2id password hashes (auth.service.ts:71,298); SHA-256 refresh hashes (auth.service.ts:494); API key SHA-256 hash (auth.service.ts:403)
Operational resilienceauth endpoints rate-limited (per-tier), token TTLs 15m/7d (env.ts:29,34)
Enterprise security posturemandatory email verification + 2FA per tenant (target policy, see OQ-1)

3. User goals

  • Anonymous visitor: register a new institution + admin account in one step, land inside with working tokens (register returns tokens at institution).
  • School admin: daily painless login, device overview + per-device logout, 2FA on, keys to integrations.
  • Teacher / Staff / Parent / Student: quiet, reliable login; password reset that survive lost passwords; never re-enter credentials more than once a session.
  • Platform admin: cross-tenant tool access via platform_admin role; impersonation is (planned).

4. Stakeholders

Platform operator, institution admins, teachers/staff, parents, students, M2M integrators (API keys), audit/security board, support staff (reset complaints), QA + design + engineering.

5. Why this exists

StudyLyon is multi-tenant: one codebase, thousands of schools. A flawed auth gate is the single highest-risk component (credential leaks, cross-tenant access, account takeover). This module is the flagship because every other module trusts its tokens.

6. Dependencies

DependencyRoleSource
Users moduleidentity profile (first/last/displayName, email)users/schemas/user.schema.ts
RBAC moduleseeded roles + member role resolution into JWT claimsrbac.service.ts, role.schema.ts:8
Config (env.ts)JWT secrets/ttl, TOTP_ISSUER (StudyLyon), SMTP, rate-limitconfig/env.ts:28-53,76-80
Redisrate-limit sliding window (rl:{tier}:{ip})rate-limit.guard.ts:40; rate-limit.constants.ts
BullMQ emailswelcome + password-reset email deliveryevent-queue-map.ts:7,9, email.worker.ts
BullMQ audit-writeUserLoggedIn → audit log jobevent-queue-map.ts:8
IdempotencyServicejob dedupidempotency.service.ts
Mongo collectionsauth_accounts, user_sessions, api_keysblueprint 04-Modules/Auth.md

7. Success metrics

  • Login success rate ≥ 99.5% of attempts (excl. intentional 401).
  • Session refresh success ≥ 99% (token rotation works with no user-visible 4xx on interstitial).
  • Password reset → logged in within 5 min for ≥ 99% of resets (email delivery + flow).
  • 2FA enablement completion rate ≥ 80% of admins within 30 days of onboarding.
  • Zero auth-related security incidents (account enumeration, cross-tenant, token reuse).
  • Rate-limit 429 correctly shown (never silent retry loops) on auth endpoints.
  • API-Key creation → shown once (raw key never recoverable from any list).

8. Edge cases

  • Unknown email vs wrong password → identical 401 (auth.service.ts:125,131).
  • Locked account → 401 "Account locked. Try again later." (auth.service.ts:133-135) — lock expiry honored but no producer sets lockedUntil (OQ-2).
  • Expired refresh used → session deleted → 401 "Session expired." (auth.service.ts:185-190).
  • Reused/rotated refresh token → hash not in store → 401 "Session not found." (replay-safe by rotation; family revocation not implemented — OQ-3).
  • Reset password → all existing sessions deleted → user must log in on every device (auth.service.ts:306).
  • Verify+resend: verify already verified → 200 "Email already verified." (auth.service.ts:215).
  • TOTP enable twice → 400; verify w/o enable → general → 400 "Call enable first."; wrong code → 401 (auth.service.ts:326,350-356).
  • Register duplicate email → 409 (auth.service.ts:57).
  • Logout with missing refresh token → controller throws generic Error → 500 (OQ-4).
  • Rate-limit 429 → client countdown; global guard active only in production (rate-limit.guard.ts:30).
  • List API keys: only non-revoked returned (api-key.repository.ts:22).

9. Assumptions (module)

  • Mobile client is forward-looking: backend is complete; this package is the UI-side spec.
  • "Email verification mandatory before first login" is the target FIXED WIRE contract (docs/blueprint security policy) but is not enforced by auth.service.ts today — register() mints tokens immediately and login() never checks emailVerified (auth.service.ts:54,123). UI must anticipate a "verify your email" interstitial and hard log-in gating as soon as the server enforces it (OQ-1).
  • 2FA challenge during login (a "2FA step" after password) is not implemented in the backend; login returns tokens directly. Current code only supports 2FA as an account setting (enable/verify/disable). The 2FA-challenge screen is speced as (planned) / (forward-looking) and should only be reached when the server returns a challenge signal.
  • Registration carries tenantId in the body (register.dto.ts:26-29) — a deliberate exception to "tenantId from token" for the tenant-bootstrap flow.
  • No QR-code backend endpoint exists — the 2FA QR image is generated on-device from the returned secret/qrCodeUri (totp qrCodeUri is an otpauth:// URI, auth.service.ts:330).

10. Open questions (module-level; global ledger in 00-shared/12)

#ItemImpact
OQ-1Server does not gate first login on verified email, and does not emit a 2FA challenge at login. When is the server-side enforcement + challenge added?Login screen gating, 2FA-challenge route timing
OQ-2lockedUntil/failedLoginAttempts are stored+incremented but no threshold ever sets lockedUntil — permanent state? Add lockout-on-N?Lock-screen copy, retry UX
OQ-3Refresh reuse: rotation detected (old token fails re-use) but "revoke entire family" is not implemented — add?Security messaging on devices
OQ-4logout with missing body token throws generic Error (→500) instead of 400Client must never send empty token; error mapping
OQ-5No role/permission metadata on auth endpoints (any authenticated user can revoke any session by id) — ownership guard (planned)Sessions screen permission UX

11. Glossary (this module)

TermMeaning
AccessToken15 m JWT, {sub, tenantId, roles, type:'access', jti}
RefreshToken7 d JWT, {sub, tenantId, roles, type:'refresh', jti}; stored hashed
Sessionuser_sessions doc — one per token pair; device/browser/os/ip/location metadata
TOTPTime-based one-time password (default: SHA1, 6 digits, 30 s, issuer StudyLyon)
API keysk-style 64-hex raw value shown once; prefix (first 8 chars) + keyHash stored
Rate tier / slashRedis sliding-window per-IP limit on auth/api/public/admin
Envelope{success,message,data,meta?,timestamp,requestId}
jtiper-token-pair UUID tying access+refresh (auth.service.ts:458,460)