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

12 — API Mapping (Auth Module)

Exact wire contract for every screen → endpoint. Base /api/v1; envelope per 00-shared/07. All endpoints from src/modules/auth/auth.controller.ts; business rules from auth.service.ts. Bearer JWT; global guards: RateLimitGuardJwtAuthGuardRbacGuard (app.module.ts:129-133). Public endpoints: register, login, refresh, logout, verify-email, forgot-password, reset-password (@Public()`).


0. Module-wide request envelope & client policy

AspectContract
Basehttps://api.<domain>/api/v1
HeadersAuthorization: Bearer <accessToken>; x-request-id client-generated; Content-Type: application/json
response{success,message,data,meta?,timestamp,requestId}
error{success,message,error:{code,details?},timestamp,requestId}
TenancytenantId from JWT claim; never in body (except register.tenantId bootstrap)
Cachingnone (auth state) — client caches nothing server-sided
Offlinepre-auth writes blocked; reads (sessions) cached last-good
Retrybackoff on 5xx/network; no auto-retry on 429

Screen: Login / 2FA-challenge

EndpointPOST /auth/login
Request{email, password} (login.dto.ts)
Success200 data:{accessToken,refreshToken}
Client flowvalidate → header set → persist token pair (secure storage) → navigate home
Auth@Public, rate auth 10/min (auth.controller.ts:38-44)
Errors400 validation; 401 UNAUTHENTICATED (“Invalid email or password.”); 429 RATE_LIMITED; 5xx
2FA(planned) client challenge — no server support today (OQ-1)
Offlineblocked, banner

Endpoint source: auth.controller.ts:40, auth.service.ts:123


Screen: Register

POST /auth/register
Request: {firstName,lastName,email,password(≥8),tenantId,phone?}
200 data:{accessToken,refreshToken} (tokens in register response)
@Public() rate auth 5/min (auth.controller.ts:30-36)
Flag: tenantId in body = tenant bootstrap exception (AGENTS.md:38 note)

Screen: Verify email

POST /auth/verify-email {token} → 200 {message:"Email verified successfully."} or 400/404
@Public() rate auth 10/min

Screen: Resend verification (in-verify, JWT)

POST /auth/resend-verification (JWT) → 200 {message:"Verification email resent."}; rate 5/120s.


Screen: Forgot password

POST /auth/forgot-password {email} → 200 {message:"If that email exists, a reset link has been sent."} (both branches)
rate auth 3/min

Screen: Reset password

POST /auth/reset-password {token,password(≥8)} → 200 {message:"Password reset successfully."}
400 invalid/expired token; side-effect: sessions deleted
rate auth 5/min

Screen: 2FA detail

POST /auth/2fa/enable (JWT) → 200 {secret, qrCodeUri}
POST /auth/2fa/verify {token} → 200 {message:"2FA enabled successfully."}
POST /auth/2fa/disable {token} → 200 {message:"2FA disabled successfully."}
errors: 400 not-enabled / already-enabled / no-secret; 401 invalid code (auth.service.ts:326-384)

Screen: Sessions / devices

GET /auth/sessions (JWT) → 200 data:[sessionDoc…]
DELETE /auth/sessions/:id (JWT) → 200 {message:"Session revoked."}
POST /auth/logout-all (JWT) → 200 {message:"Logged out from all devices."}
POST /auth/logout {refreshToken} (@Public) → 200 {message:"Logged out successfully."}

Screen: API keys

GET /auth/api-keys (JWT) → 200 data:[{id,name,prefix,scopes,createdAt,lastUsedAt}]
POST /auth/api-keys (JWT) {name,scopes?} → 200 data:{id,prefix,key} (key once)
DELETE /auth/api-keys/:id (JWT) → 200 {message:"API key revoked."}

Loading / streaming / realtime

ScreenLoadingStreamingRealtime
loginbutton
registerbutton
sessionsAppSkeleton rows(planned) WS session-change event
api-keysAppSkeleton
2FAspinner

Client-side error mapping table (module)

ScreencodeUI
login401「 credentials incorrect」
login429countdown
verify-email400“Verify link expired/invalid” + resend
reset400expired-token banner
sessions404 (revoke id)treat as removed
api-key create400/409inline; 401→refresh; 5xx generic+requestId
any401 (during auth action)silent refresh; fail→sessionExpired

Pagination

GET /auth/sessions & GET /auth/api-keys are non-paginated arrays (listX returns full docs, user-session.repository.ts:19, api-key.repository.ts:20); client renders all (no infinite scroll). Sort: api-keys by createdAt desc server-side; sessions insertion order — client groups current-first.

Optimistic / undo

Sessions/key listings are read+refresh (Refresh), revoke=server-confirm (no optimistic) because irreversible; disable 2FA = server-first (code needed). Exception: row “revoke” shows immediate removal only on server 200.