@tekyzinc/gsd-t 2.50.11 → 2.50.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,16 +2,24 @@
2
2
 
3
3
  All notable changes to GSD-T are documented here. Updated with each release.
4
4
 
5
- ## [2.50.10] - 2026-03-25
5
+ ## [2.50.12] - 2026-03-25
6
6
 
7
7
  ### Added
8
- - **18 new stack rule files** — python, flutter, tailwind, react-native, vite, nextjs, vue, docker, postgresql (with graph-in-SQL section), github-actions, rest-api, supabase, firebase, graphql, zustand, redux, neo4j, playwright. Total: 22 stack rules (was 4).
8
+ - **23 new stack rule files** — python, flutter, tailwind, react-native, vite, nextjs, vue, docker, postgresql (with graph-in-SQL section), github-actions, rest-api, supabase, firebase, graphql, zustand, redux, neo4j, playwright, fastapi, llm (with RAG patterns section), prisma, queues, _auth (universal). Total: 27 stack rules (was 4).
9
+ - **`_auth.md`** (universal) — email-first registration, auth provider abstraction (Cognito/Firebase/Google), token management, password policy, session management, social auth/OAuth, email verification, MFA, authorization/RBAC, auth security, auth UI patterns.
10
+ - **`fastapi.md`** — dependency injection, Pydantic request/response models, lifespan events, BackgroundTasks, async patterns, auto-generated OpenAPI docs.
11
+ - **`llm.md`** — provider-agnostic LLM patterns: structured outputs, streaming, error/retry, token management, conversation state, tool/function calling, RAG patterns (chunking, embeddings, retrieval), prompt management, testing, cost/observability.
12
+ - **`prisma.md`** — schema modeling, migrations, typed client usage, relation queries, transactions, seeding, N+1 prevention.
13
+ - **`queues.md`** — BullMQ/Bull, SQS, RabbitMQ, Celery patterns: idempotent handlers, dead letter queues, retry/backoff, job deduplication, graceful shutdown.
9
14
  - **Playwright best practices** — coverage matrix per feature, pairwise combinatorial testing, state transition testing, multi-step workflow testing, Page Object Model, API mocking patterns. Enforces rigorous test depth across permutations.
10
15
  - **react.md expanded** — added state management decision table, form management (react-hook-form + zod), React naming conventions (3 new sections from external best practices review).
16
+ - **Project-level stack overrides** — `.gsd-t/stacks/` directory for per-project customization of global stack rules. Local files replace global files of the same name.
11
17
 
12
18
  ### Changed
13
- - Stack detection in execute, quick, and debug commands updated to cover all 22 stack files with conditional detection per project dependencies.
19
+ - Stack detection in execute, quick, and debug commands updated to cover all 27 stack files with conditional detection per project dependencies.
20
+ - Detection refactored from one-liner to structured bash with `_sf()` (local override resolver) and `_add()` helper functions.
14
21
  - PostgreSQL graph-in-SQL patterns (adjacency lists, junction tables, recursive CTEs) added to postgresql.md based on real project analysis.
22
+ - GSD-T-README.md stack detection table expanded to list all 27 files with their detection triggers.
15
23
 
16
24
  ## [2.46.11] - 2026-03-24
17
25
 
@@ -37,10 +37,16 @@ if [ -d "$STACKS_DIR" ]; then
37
37
  grep -q '"@reduxjs/toolkit"' package.json 2>/dev/null && _add redux.md
38
38
  grep -q '"neo4j-driver"' package.json 2>/dev/null && _add neo4j.md
39
39
  grep -qE '"(pg|prisma|drizzle-orm|knex)"' package.json 2>/dev/null && _add postgresql.md
40
+ grep -qE '"(prisma|@prisma/client)"' package.json 2>/dev/null && _add prisma.md
41
+ grep -qE '"(bullmq|bull|amqplib|@aws-sdk/client-sqs|bee-queue|agenda)"' package.json 2>/dev/null && _add queues.md
42
+ grep -qE '"(openai|anthropic|@anthropic-ai/sdk|langchain|llama-index|@google/generative-ai)"' package.json 2>/dev/null && _add llm.md
40
43
  fi
41
44
  ([ -f "requirements.txt" ] || [ -f "pyproject.toml" ] || [ -f "Pipfile" ]) && _add python.md
42
45
  ([ -f "requirements.txt" ] && grep -q "psycopg" requirements.txt 2>/dev/null || [ -f "pyproject.toml" ] && grep -q "psycopg" pyproject.toml 2>/dev/null) && _add postgresql.md
43
46
  ([ -f "requirements.txt" ] && grep -q "neo4j" requirements.txt 2>/dev/null) && _add neo4j.md
47
+ ([ -f "requirements.txt" ] && grep -q "fastapi" requirements.txt 2>/dev/null || [ -f "pyproject.toml" ] && grep -q "fastapi" pyproject.toml 2>/dev/null) && _add fastapi.md
48
+ ([ -f "requirements.txt" ] && grep -qE "(celery|dramatiq|rq|arq)" requirements.txt 2>/dev/null || [ -f "pyproject.toml" ] && grep -qE "(celery|dramatiq|rq|arq)" pyproject.toml 2>/dev/null) && _add queues.md
49
+ ([ -f "requirements.txt" ] && grep -qE "(openai|anthropic|langchain|llama.index)" requirements.txt 2>/dev/null || [ -f "pyproject.toml" ] && grep -qE "(openai|anthropic|langchain|llama.index)" pyproject.toml 2>/dev/null) && _add llm.md
44
50
  [ -f "pubspec.yaml" ] && _add flutter.md
45
51
  [ -f "Dockerfile" ] && _add docker.md
46
52
  [ -d ".github/workflows" ] && _add github-actions.md
@@ -156,12 +156,18 @@ if [ -d "$STACKS_DIR" ]; then
156
156
  grep -q '"@reduxjs/toolkit"' package.json 2>/dev/null && _add redux.md
157
157
  grep -q '"neo4j-driver"' package.json 2>/dev/null && _add neo4j.md
158
158
  grep -qE '"(pg|prisma|drizzle-orm|knex)"' package.json 2>/dev/null && _add postgresql.md
159
+ grep -qE '"(prisma|@prisma/client)"' package.json 2>/dev/null && _add prisma.md
160
+ grep -qE '"(bullmq|bull|amqplib|@aws-sdk/client-sqs|bee-queue|agenda)"' package.json 2>/dev/null && _add queues.md
161
+ grep -qE '"(openai|anthropic|@anthropic-ai/sdk|langchain|llama-index|@google/generative-ai)"' package.json 2>/dev/null && _add llm.md
159
162
  fi
160
163
 
161
164
  # File-based detection (no package.json needed)
162
165
  ([ -f "requirements.txt" ] || [ -f "pyproject.toml" ] || [ -f "Pipfile" ]) && _add python.md
163
166
  ([ -f "requirements.txt" ] && grep -q "psycopg" requirements.txt 2>/dev/null || [ -f "pyproject.toml" ] && grep -q "psycopg" pyproject.toml 2>/dev/null) && _add postgresql.md
164
167
  ([ -f "requirements.txt" ] && grep -q "neo4j" requirements.txt 2>/dev/null) && _add neo4j.md
168
+ ([ -f "requirements.txt" ] && grep -q "fastapi" requirements.txt 2>/dev/null || [ -f "pyproject.toml" ] && grep -q "fastapi" pyproject.toml 2>/dev/null) && _add fastapi.md
169
+ ([ -f "requirements.txt" ] && grep -qE "(celery|dramatiq|rq|arq)" requirements.txt 2>/dev/null || [ -f "pyproject.toml" ] && grep -qE "(celery|dramatiq|rq|arq)" pyproject.toml 2>/dev/null) && _add queues.md
170
+ ([ -f "requirements.txt" ] && grep -qE "(openai|anthropic|langchain|llama.index)" requirements.txt 2>/dev/null || [ -f "pyproject.toml" ] && grep -qE "(openai|anthropic|langchain|llama.index)" pyproject.toml 2>/dev/null) && _add llm.md
165
171
  [ -f "pubspec.yaml" ] && _add flutter.md
166
172
  [ -f "Dockerfile" ] && _add docker.md
167
173
  [ -d ".github/workflows" ] && _add github-actions.md
@@ -258,7 +258,7 @@ Use these when user asks for help on a specific command:
258
258
  - **Use when**: Ready to implement
259
259
  - **Note (M22)**: Task-level fresh dispatch (one subagent per task, ~10-20% context each). Team mode uses worktree isolation (`isolation: "worktree"`) — zero file conflicts. Adaptive replanning between domain completions.
260
260
  - **Note (M26)**: Active rule injection — evaluates declarative rules from rules.jsonl before dispatching each domain's tasks. Fires matching rules as warnings in subagent prompts.
261
- - **Note (M29)**: Stack Rules Engine — auto-detects project tech stack from manifest files and injects mandatory best-practice rules into each task subagent prompt. Universal rules (`_security.md`) always apply; stack-specific rules layer on top. Violations are task failures (same weight as contract violations).
261
+ - **Note (M29)**: Stack Rules Engine — auto-detects project tech stack from manifest files and injects mandatory best-practice rules into each task subagent prompt. Universal rules (`_security.md`, `_auth.md`) always apply; stack-specific rules layer on top. Violations are task failures (same weight as contract violations).
262
262
 
263
263
  ### test-sync
264
264
  - **Summary**: Keep tests aligned with code changes
@@ -41,10 +41,16 @@ if [ -d "$STACKS_DIR" ]; then
41
41
  grep -q '"@reduxjs/toolkit"' package.json 2>/dev/null && _add redux.md
42
42
  grep -q '"neo4j-driver"' package.json 2>/dev/null && _add neo4j.md
43
43
  grep -qE '"(pg|prisma|drizzle-orm|knex)"' package.json 2>/dev/null && _add postgresql.md
44
+ grep -qE '"(prisma|@prisma/client)"' package.json 2>/dev/null && _add prisma.md
45
+ grep -qE '"(bullmq|bull|amqplib|@aws-sdk/client-sqs|bee-queue|agenda)"' package.json 2>/dev/null && _add queues.md
46
+ grep -qE '"(openai|anthropic|@anthropic-ai/sdk|langchain|llama-index|@google/generative-ai)"' package.json 2>/dev/null && _add llm.md
44
47
  fi
45
48
  ([ -f "requirements.txt" ] || [ -f "pyproject.toml" ] || [ -f "Pipfile" ]) && _add python.md
46
49
  ([ -f "requirements.txt" ] && grep -q "psycopg" requirements.txt 2>/dev/null || [ -f "pyproject.toml" ] && grep -q "psycopg" pyproject.toml 2>/dev/null) && _add postgresql.md
47
50
  ([ -f "requirements.txt" ] && grep -q "neo4j" requirements.txt 2>/dev/null) && _add neo4j.md
51
+ ([ -f "requirements.txt" ] && grep -q "fastapi" requirements.txt 2>/dev/null || [ -f "pyproject.toml" ] && grep -q "fastapi" pyproject.toml 2>/dev/null) && _add fastapi.md
52
+ ([ -f "requirements.txt" ] && grep -qE "(celery|dramatiq|rq|arq)" requirements.txt 2>/dev/null || [ -f "pyproject.toml" ] && grep -qE "(celery|dramatiq|rq|arq)" pyproject.toml 2>/dev/null) && _add queues.md
53
+ ([ -f "requirements.txt" ] && grep -qE "(openai|anthropic|langchain|llama.index)" requirements.txt 2>/dev/null || [ -f "pyproject.toml" ] && grep -qE "(openai|anthropic|langchain|llama.index)" pyproject.toml 2>/dev/null) && _add llm.md
48
54
  [ -f "pubspec.yaml" ] && _add flutter.md
49
55
  [ -f "Dockerfile" ] && _add docker.md
50
56
  [ -d ".github/workflows" ] && _add github-actions.md
@@ -231,20 +231,42 @@ GSD-T auto-detects your project's tech stack and injects mandatory best-practice
231
231
  ### How It Works
232
232
 
233
233
  1. At subagent spawn time, GSD-T reads project manifest files to detect the active stack(s).
234
- 2. Universal rules (`templates/stacks/_security.md`) are **always** injected.
234
+ 2. Universal rules (`templates/stacks/_security.md`, `_auth.md`) are **always** injected.
235
235
  3. Stack-specific rules are injected when the corresponding stack is detected.
236
- 4. Rules are appended to the subagent prompt as a `## Stack Rules (MANDATORY)` section.
236
+ 4. Project-level overrides in `.gsd-t/stacks/` replace global files of the same name.
237
+ 5. Rules are appended to the subagent prompt as a `## Stack Rules (MANDATORY)` section.
237
238
 
238
- ### Stack Detection
239
+ ### Stack Detection (27 files)
239
240
 
240
- | Project File | Detected Stack |
241
+ | Project File | Detected Stack(s) |
241
242
  |---|---|
242
- | `package.json` with `"react"` | React |
243
- | `package.json` with `"typescript"` or `tsconfig.json` | TypeScript |
244
- | `package.json` with `"express"`, `"fastify"`, `"hono"`, or `"koa"` | Node API |
245
- | `requirements.txt` or `pyproject.toml` | Python |
246
- | `go.mod` | Go |
247
- | `Cargo.toml` | Rust |
243
+ | *(always)* | `_security.md`, `_auth.md` |
244
+ | `package.json` with `"react"` | `react.md` |
245
+ | `package.json` with `"react-native"` | `react-native.md` |
246
+ | `package.json` with `"next"` | `nextjs.md` |
247
+ | `package.json` with `"vue"` | `vue.md` |
248
+ | `package.json` with `"typescript"` or `tsconfig.json` | `typescript.md` |
249
+ | `package.json` with `"tailwindcss"` | `tailwind.md` |
250
+ | `package.json` with `"express"`, `"fastify"`, `"hono"`, or `"koa"` | `node-api.md`, `rest-api.md` |
251
+ | `package.json` with `"vite"` | `vite.md` |
252
+ | `package.json` with `"@supabase/supabase-js"` | `supabase.md` |
253
+ | `package.json` with `"firebase"` | `firebase.md` |
254
+ | `package.json` with `"graphql"` or `"@apollo/server"` | `graphql.md` |
255
+ | `package.json` with `"zustand"` | `zustand.md` |
256
+ | `package.json` with `"@reduxjs/toolkit"` | `redux.md` |
257
+ | `package.json` with `"prisma"` or `"@prisma/client"` | `prisma.md` |
258
+ | `package.json` with `"pg"`, `"knex"`, or `"drizzle-orm"` | `postgresql.md` |
259
+ | `package.json` with `"neo4j-driver"` | `neo4j.md` |
260
+ | `package.json` with `"bullmq"`, `"bull"`, `"amqplib"`, or `"@aws-sdk/client-sqs"` | `queues.md` |
261
+ | `package.json` with `"openai"`, `"anthropic"`, `"langchain"` | `llm.md` |
262
+ | `Dockerfile` or `compose.yaml` | `docker.md` |
263
+ | `.github/workflows/*.yml` | `github-actions.md` |
264
+ | `playwright.config.*` | `playwright.md` |
265
+ | `requirements.txt` or `pyproject.toml` | `python.md` |
266
+ | `requirements.txt` with `fastapi` | `fastapi.md` |
267
+ | `requirements.txt` with `celery`, `dramatiq`, `rq`, or `arq` | `queues.md` |
268
+ | `requirements.txt` with `openai`, `anthropic`, `langchain` | `llm.md` |
269
+ | `pubspec.yaml` | `flutter.md` |
248
270
 
249
271
  ### Commands That Inject Stack Rules
250
272
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tekyzinc/gsd-t",
3
- "version": "2.50.11",
3
+ "version": "2.50.12",
4
4
  "description": "GSD-T: Contract-Driven Development for Claude Code — 51 slash commands with headless CI/CD mode, graph-powered code analysis, real-time agent dashboard, execution intelligence, task telemetry, doc-ripple enforcement, backlog management, impact analysis, test sync, milestone archival, and PRD generation",
5
5
  "author": "Tekyz, Inc.",
6
6
  "license": "MIT",
@@ -0,0 +1,324 @@
1
+ # Authentication Standards (Universal — All Projects)
2
+
3
+ These rules are MANDATORY. Violations fail the task. No exceptions.
4
+
5
+ ---
6
+
7
+ ## 1. Registration — Email-First, Password-Later
8
+
9
+ ```
10
+ MANDATORY:
11
+ ├── Signup collects email + app-specific fields only — NO password at registration
12
+ ├── After signup, send a "set your password" email (same flow as forgot password)
13
+ ├── The set-password link is a one-time, time-limited token (15-60 minutes)
14
+ ├── User clicks link → lands on set-password page → sets their password → logged in
15
+ ├── This eliminates: temporary passwords, forced password changes, password-in-transit risks
16
+ └── If the link expires, user can request a new one (same as forgot password)
17
+ ```
18
+
19
+ **Why one flow**: "Set password" and "Forgot password" are the same operation — generate a token, email a link, user sets a password. Building them as one flow halves the auth code and guarantees consistent behavior.
20
+
21
+ **GOOD**
22
+ ```
23
+ 1. User submits: { email, name, [app-specific fields] }
24
+ 2. Server creates account (no password set, status: pending_verification)
25
+ 3. Server sends email: "Welcome! Set your password: [link with token]"
26
+ 4. User clicks link → set-password page
27
+ 5. User sets password → account activated → logged in
28
+ 6. If link expires → user clicks "Resend" → same flow
29
+ ```
30
+
31
+ ---
32
+
33
+ ## 2. Provider Abstraction
34
+
35
+ ```
36
+ MANDATORY:
37
+ ├── Wrap the auth provider behind an AuthService interface
38
+ ├── Application code NEVER calls Cognito/Firebase/Google/Supabase Auth directly
39
+ ├── AuthService exposes: signup, login, logout, resetPassword, refreshToken, getCurrentUser
40
+ ├── Switching providers = rewrite AuthService implementation, not the entire app
41
+ └── Auth provider config (pool IDs, client IDs, URLs) lives in env vars — not in code
42
+ ```
43
+
44
+ **GOOD**
45
+ ```typescript
46
+ // auth/AuthService.ts — interface
47
+ interface AuthService {
48
+ signup(email: string, metadata?: Record<string, string>): Promise<{ userId: string }>;
49
+ login(email: string, password: string): Promise<AuthTokens>;
50
+ logout(): Promise<void>;
51
+ sendPasswordResetEmail(email: string): Promise<void>;
52
+ setPassword(token: string, newPassword: string): Promise<void>;
53
+ refreshToken(refreshToken: string): Promise<AuthTokens>;
54
+ getCurrentUser(): Promise<User | null>;
55
+ }
56
+
57
+ // auth/providers/CognitoAuthService.ts — implementation
58
+ // auth/providers/FirebaseAuthService.ts — implementation
59
+ // auth/providers/SupabaseAuthService.ts — implementation
60
+ ```
61
+
62
+ **BAD** — calling provider directly in components:
63
+ ```typescript
64
+ import { signInWithEmailAndPassword } from 'firebase/auth';
65
+ // Scattered across 15 components, impossible to switch providers
66
+ ```
67
+
68
+ ---
69
+
70
+ ## 3. Token Management
71
+
72
+ ```
73
+ MANDATORY:
74
+ ├── Access tokens: short-lived (15-60 minutes)
75
+ ├── Refresh tokens: longer-lived (7-30 days), used to get new access tokens
76
+ ├── Web: store tokens in httpOnly, secure, sameSite cookies — NEVER localStorage
77
+ ├── Mobile (React Native/Flutter): use platform secure storage (Keychain/Keystore)
78
+ ├── Auto-refresh: intercept 401 responses, refresh token, retry the request
79
+ ├── NEVER store tokens in JavaScript-accessible storage (localStorage, sessionStorage)
80
+ └── NEVER send tokens in URL query parameters
81
+ ```
82
+
83
+ **GOOD** — auto-refresh interceptor:
84
+ ```typescript
85
+ api.interceptors.response.use(
86
+ (response) => response,
87
+ async (error) => {
88
+ if (error.response?.status === 401 && !error.config._retry) {
89
+ error.config._retry = true;
90
+ const newTokens = await authService.refreshToken(getRefreshToken());
91
+ setTokens(newTokens);
92
+ error.config.headers.Authorization = `Bearer ${newTokens.accessToken}`;
93
+ return api(error.config);
94
+ }
95
+ return Promise.reject(error);
96
+ }
97
+ );
98
+ ```
99
+
100
+ ---
101
+
102
+ ## 4. Password Policy
103
+
104
+ ```
105
+ MANDATORY:
106
+ ├── Minimum 8 characters — no maximum (allow up to 128)
107
+ ├── Require mix of: uppercase, lowercase, number, special character
108
+ ├── Check against common password list (top 10,000) — reject "Password123!"
109
+ ├── Show password strength indicator in real-time as user types
110
+ ├── Allow paste into password fields — NEVER disable paste
111
+ ├── NEVER store plaintext passwords — hashing is server-side only (bcrypt/argon2)
112
+ ├── NEVER transmit password requirements to the client beyond the UI hints
113
+ └── Rate-limit login attempts: lock after 5 failed attempts for 15 minutes
114
+ ```
115
+
116
+ ---
117
+
118
+ ## 5. Session Management
119
+
120
+ ```
121
+ MANDATORY:
122
+ ├── Logout clears ALL state: tokens, cached user data, in-memory state
123
+ ├── Logout invalidates the refresh token server-side (not just client delete)
124
+ ├── Session timeout: auto-logout after inactivity period (configurable per app)
125
+ ├── Multi-tab sync (web): logout in one tab logs out all tabs
126
+ ├── Token expiry: show "session expired" message, redirect to login
127
+ ├── NEVER keep stale auth state — if token refresh fails, force logout
128
+ └── On app startup: validate the stored token before showing authenticated UI
129
+ ```
130
+
131
+ **GOOD** — multi-tab logout sync:
132
+ ```typescript
133
+ // Listen for storage events (fires when another tab changes storage)
134
+ window.addEventListener('storage', (event) => {
135
+ if (event.key === 'logout-event') {
136
+ authService.clearLocalState();
137
+ window.location.href = '/login';
138
+ }
139
+ });
140
+
141
+ // On logout, broadcast to other tabs
142
+ function logout() {
143
+ localStorage.setItem('logout-event', Date.now().toString());
144
+ localStorage.removeItem('logout-event');
145
+ authService.logout();
146
+ }
147
+ ```
148
+
149
+ ---
150
+
151
+ ## 6. Social Auth / OAuth
152
+
153
+ ```
154
+ WHEN SUPPORTING SOCIAL LOGIN:
155
+ ├── Use OAuth 2.0 / OpenID Connect — NEVER custom social auth flows
156
+ ├── Handle "email already exists" — offer to link accounts, don't create duplicates
157
+ ├── Store the provider + provider user ID alongside the local account
158
+ ├── Social login creates a local account on first use (same as email signup, but pre-verified)
159
+ ├── Allow users to set a password later (to enable email+password login alongside social)
160
+ ├── NEVER trust the email from the OAuth provider without verifying it's the same user
161
+ └── Implement PKCE for public clients (SPAs, mobile apps)
162
+ ```
163
+
164
+ **Account linking flow:**
165
+ ```
166
+ 1. User clicks "Sign in with Google" → OAuth flow → returns email: jane@example.com
167
+ 2. Server checks: does jane@example.com already have an account?
168
+ YES → Link Google provider to existing account → login
169
+ NO → Create new account with Google as primary provider → login
170
+ 3. User can later set a password to also use email+password login
171
+ ```
172
+
173
+ ---
174
+
175
+ ## 7. Email Verification
176
+
177
+ ```
178
+ MANDATORY:
179
+ ├── Email must be verified before account is fully activated
180
+ ├── Verification link = one-time token, expires in 24 hours
181
+ ├── Unverified accounts: allow login but restrict access (show "verify your email" banner)
182
+ ├── Resend verification: rate-limit to 3 per hour
183
+ ├── After email change: re-verify the new email before switching
184
+ └── NEVER expose whether an email is registered (prevents enumeration)
185
+ ```
186
+
187
+ **Anti-enumeration pattern:**
188
+ ```
189
+ // BAD — reveals whether email exists
190
+ "No account found for jane@example.com"
191
+
192
+ // GOOD — same message regardless
193
+ "If an account exists for this email, you'll receive a password reset link"
194
+ ```
195
+
196
+ ---
197
+
198
+ ## 8. Multi-Factor Authentication (MFA)
199
+
200
+ ```
201
+ WHEN MFA IS REQUIRED:
202
+ ├── Support TOTP (authenticator app) as primary — SMS as fallback only
203
+ ├── Provide recovery codes (8-10 one-time codes) during MFA setup
204
+ ├── Store recovery codes hashed — display only once during setup
205
+ ├── MFA enrollment: optional by default, mandatory for admin/elevated roles
206
+ ├── Remember device: offer "trust this device for 30 days" option
207
+ └── NEVER use email as a second factor — it's the same channel as password reset
208
+ ```
209
+
210
+ ---
211
+
212
+ ## 9. Authorization Patterns
213
+
214
+ ```
215
+ MANDATORY:
216
+ ├── Role-based access control (RBAC): define roles with explicit permissions
217
+ ├── Check permissions server-side on EVERY request — client checks are UI hints only
218
+ ├── Define roles as enums: ADMIN, MEMBER, VIEWER — not strings
219
+ ├── Permission check: can(user, action, resource) — not role string comparison
220
+ ├── UI: hide or disable actions the user can't perform — don't show then reject
221
+ ├── API: return 403 Forbidden for unauthorized actions — not 404
222
+ └── NEVER derive permissions from user properties — use explicit role → permission mapping
223
+ ```
224
+
225
+ **GOOD**
226
+ ```typescript
227
+ // Define permissions explicitly
228
+ const ROLE_PERMISSIONS: Record<UserRole, Permission[]> = {
229
+ [UserRole.ADMIN]: ['users:read', 'users:write', 'users:delete', 'settings:write'],
230
+ [UserRole.MEMBER]: ['users:read', 'users:write'],
231
+ [UserRole.VIEWER]: ['users:read'],
232
+ };
233
+
234
+ function can(user: User, permission: Permission): boolean {
235
+ return ROLE_PERMISSIONS[user.role]?.includes(permission) ?? false;
236
+ }
237
+
238
+ // In API handler
239
+ if (!can(currentUser, 'users:delete')) {
240
+ throw new ForbiddenError('Insufficient permissions');
241
+ }
242
+ ```
243
+
244
+ ---
245
+
246
+ ## 10. Auth-Related Security
247
+
248
+ ```
249
+ MANDATORY:
250
+ ├── All auth endpoints over HTTPS — no exceptions
251
+ ├── CSRF protection on auth forms (use framework's built-in CSRF tokens)
252
+ ├── Brute force protection: rate-limit login, lock after N failures
253
+ ├── Password reset tokens: single-use, time-limited, cryptographically random
254
+ ├── Log all auth events: login, logout, failed attempts, password changes, MFA events
255
+ ├── NEVER log passwords, tokens, or session IDs — even in error logs
256
+ ├── NEVER include auth tokens in URLs — use headers or cookies
257
+ └── Rotate signing keys periodically (JWT secret, cookie signing key)
258
+ ```
259
+
260
+ ---
261
+
262
+ ## 11. Auth UI Patterns
263
+
264
+ ```
265
+ MANDATORY:
266
+ ├── Login form: email + password + "Forgot password?" link + social login buttons
267
+ ├── Signup form: email + app-specific fields + "Already have an account?" link
268
+ ├── Forgot password: email input → "Check your email" (same message regardless of email existence)
269
+ ├── Set password: new password + confirm password + strength indicator
270
+ ├── Loading state on all auth buttons — disable during submission
271
+ ├── Show/hide password toggle on password fields
272
+ ├── Preserve form data on validation errors — don't clear the form
273
+ └── Redirect to intended destination after login (not always to homepage)
274
+ ```
275
+
276
+ **Redirect after login:**
277
+ ```typescript
278
+ // Before redirecting to login, store the intended URL
279
+ const returnUrl = window.location.pathname;
280
+ router.push(`/login?returnUrl=${encodeURIComponent(returnUrl)}`);
281
+
282
+ // After successful login
283
+ const returnUrl = searchParams.get('returnUrl') || '/dashboard';
284
+ router.push(returnUrl);
285
+ ```
286
+
287
+ ---
288
+
289
+ ## 12. Anti-Patterns
290
+
291
+ ```
292
+ NEVER:
293
+ ├── Password at registration — use email-first, set-password-later flow
294
+ ├── Tokens in localStorage — use httpOnly cookies (web) or secure storage (mobile)
295
+ ├── Tokens in URL query parameters
296
+ ├── Calling auth provider directly from components — use AuthService abstraction
297
+ ├── Revealing whether an email is registered ("no account found for...")
298
+ ├── Client-side-only permission checks — always enforce server-side
299
+ ├── Same message for login failure types ("wrong password" vs "account locked")
300
+ ├── Disabling paste on password fields
301
+ ├── Email as a second factor for MFA
302
+ ├── Storing plaintext passwords or unhashed recovery codes
303
+ └── Silent token expiry — always inform the user and redirect to login
304
+ ```
305
+
306
+ ---
307
+
308
+ ## Auth Verification Checklist
309
+
310
+ - [ ] Signup is email-first — no password at registration
311
+ - [ ] Set-password and forgot-password share the same token-based flow
312
+ - [ ] Auth provider wrapped in AuthService interface — no direct provider calls
313
+ - [ ] Tokens stored in httpOnly cookies (web) or secure storage (mobile)
314
+ - [ ] Access tokens short-lived with auto-refresh on 401
315
+ - [ ] Logout clears all state and invalidates refresh token server-side
316
+ - [ ] Multi-tab logout sync (web)
317
+ - [ ] Password policy enforced with strength indicator
318
+ - [ ] Login rate-limited — lock after 5 failed attempts
319
+ - [ ] Email enumeration prevented (same response for existing/non-existing emails)
320
+ - [ ] Social auth handles account linking for existing emails
321
+ - [ ] Permissions checked server-side on every request
322
+ - [ ] Roles defined as enums with explicit permission mapping
323
+ - [ ] All auth events logged (no tokens/passwords in logs)
324
+ - [ ] Redirect to intended destination after login