@orchestrator-claude/definitions 3.5.0

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.
Files changed (73) hide show
  1. package/agents/api-extractor.md +687 -0
  2. package/agents/business-rule-miner.md +754 -0
  3. package/agents/code-archaeologist.md +720 -0
  4. package/agents/docs-guardian.md +524 -0
  5. package/agents/implementer.md +512 -0
  6. package/agents/legacy-discoverer.md +583 -0
  7. package/agents/legacy-synthesizer.md +1101 -0
  8. package/agents/orchestrator.md +165 -0
  9. package/agents/planner.md +365 -0
  10. package/agents/researcher.md +447 -0
  11. package/agents/reviewer.md +514 -0
  12. package/agents/schema-extractor.md +781 -0
  13. package/agents/specifier.md +360 -0
  14. package/agents/task-generator.md +390 -0
  15. package/bin/orch-defs.js +2 -0
  16. package/dist/cli.d.ts +3 -0
  17. package/dist/cli.d.ts.map +1 -0
  18. package/dist/cli.js +172 -0
  19. package/dist/cli.js.map +1 -0
  20. package/dist/commands/DiffCommand.d.ts +13 -0
  21. package/dist/commands/DiffCommand.d.ts.map +1 -0
  22. package/dist/commands/DiffCommand.js +74 -0
  23. package/dist/commands/DiffCommand.js.map +1 -0
  24. package/dist/commands/SeedCommand.d.ts +19 -0
  25. package/dist/commands/SeedCommand.d.ts.map +1 -0
  26. package/dist/commands/SeedCommand.js +56 -0
  27. package/dist/commands/SeedCommand.js.map +1 -0
  28. package/dist/http/ApiClient.d.ts +50 -0
  29. package/dist/http/ApiClient.d.ts.map +1 -0
  30. package/dist/http/ApiClient.js +58 -0
  31. package/dist/http/ApiClient.js.map +1 -0
  32. package/dist/index.d.ts +12 -0
  33. package/dist/index.d.ts.map +1 -0
  34. package/dist/index.js +11 -0
  35. package/dist/index.js.map +1 -0
  36. package/dist/manifest/ManifestLoader.d.ts +34 -0
  37. package/dist/manifest/ManifestLoader.d.ts.map +1 -0
  38. package/dist/manifest/ManifestLoader.js +110 -0
  39. package/dist/manifest/ManifestLoader.js.map +1 -0
  40. package/dist/manifest/types.d.ts +59 -0
  41. package/dist/manifest/types.d.ts.map +1 -0
  42. package/dist/manifest/types.js +5 -0
  43. package/dist/manifest/types.js.map +1 -0
  44. package/dist/scripts/generate-manifest.d.ts +10 -0
  45. package/dist/scripts/generate-manifest.d.ts.map +1 -0
  46. package/dist/scripts/generate-manifest.js +114 -0
  47. package/dist/scripts/generate-manifest.js.map +1 -0
  48. package/hooks/post-agent-artifact-relay.sh +157 -0
  49. package/hooks/post-artifact-generate.sh +39 -0
  50. package/hooks/post-implement-validate.sh +139 -0
  51. package/hooks/post-phase-checkpoint.sh +322 -0
  52. package/hooks/pre-agent-invoke.sh +34 -0
  53. package/hooks/pre-phase-advance.sh +40 -0
  54. package/hooks/track-agent-invocation.sh +241 -0
  55. package/kb/auth-strategies.md +742 -0
  56. package/kb/docs-constitution.md +310 -0
  57. package/kb/error-handling.md +555 -0
  58. package/kb/rest-conventions.md +458 -0
  59. package/kb/validation-patterns.md +589 -0
  60. package/manifest.json +314 -0
  61. package/package.json +65 -0
  62. package/skills/artifact-validator/SKILL.md +226 -0
  63. package/skills/docs-guardian/SKILL.md +230 -0
  64. package/skills/kb-lookup/SKILL.md +257 -0
  65. package/skills/phase-gate-evaluator/SKILL.md +274 -0
  66. package/skills/release/SKILL.md +239 -0
  67. package/skills/release/release.sh +491 -0
  68. package/skills/smoke-test/SKILL.md +195 -0
  69. package/skills/workflow-status/SKILL.md +322 -0
  70. package/workflows/bug-fix.json +74 -0
  71. package/workflows/feature-development.json +88 -0
  72. package/workflows/legacy-analysis.json +304 -0
  73. package/workflows/refactoring.json +74 -0
@@ -0,0 +1,742 @@
1
+ ---
2
+ title: "Authentication Strategies"
3
+ category: "security"
4
+ tier: "foundational"
5
+ tags: ["api", "auth", "jwt", "oauth", "security"]
6
+ template: "api"
7
+ ---
8
+
9
+ # Authentication Strategies
10
+
11
+ ## Overview
12
+
13
+ This document defines authentication strategies and best practices for securing REST APIs, covering JWT, OAuth 2.0, API keys, and session-based authentication.
14
+
15
+ **Project**: my-project
16
+ **Template**: api
17
+
18
+ ---
19
+
20
+ ## 1. Authentication vs Authorization
21
+
22
+ ### Authentication
23
+
24
+ **Who are you?** - Verifying user identity
25
+
26
+ - Login with credentials
27
+ - Token validation
28
+ - Session management
29
+
30
+ ### Authorization
31
+
32
+ **What can you do?** - Verifying permissions
33
+
34
+ - Role-based access control (RBAC)
35
+ - Permission checks
36
+ - Resource ownership validation
37
+
38
+ ---
39
+
40
+ ## 2. JWT (JSON Web Token) Authentication
41
+
42
+ ### Overview
43
+
44
+ JWT is a stateless authentication mechanism where tokens contain user identity and claims.
45
+
46
+ **Pros:**
47
+ - Stateless (no server-side session storage)
48
+ - Scalable across multiple servers
49
+ - Can include custom claims
50
+
51
+ **Cons:**
52
+ - Cannot revoke tokens before expiration
53
+ - Larger payload than session IDs
54
+ - Vulnerable if secret key compromised
55
+
56
+ ### JWT Structure
57
+
58
+ ```
59
+ header.payload.signature
60
+
61
+ eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
62
+ eyJ1c2VySWQiOiIxMjMiLCJyb2xlIjoiYWRtaW4iLCJpYXQiOjE2MTYyMzkwMjJ9.
63
+ SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
64
+ ```
65
+
66
+ ### Implementation
67
+
68
+ #### Install Dependencies
69
+
70
+ ```bash
71
+ npm install jsonwebtoken bcrypt
72
+ npm install --save-dev @types/jsonwebtoken @types/bcrypt
73
+ ```
74
+
75
+ #### Generate Token
76
+
77
+ ```typescript
78
+ // src/infrastructure/auth/JwtService.ts
79
+ import jwt from 'jsonwebtoken';
80
+
81
+ export interface TokenPayload {
82
+ userId: string;
83
+ email: string;
84
+ role: string;
85
+ }
86
+
87
+ export class JwtService {
88
+ private readonly secret: string;
89
+ private readonly expiresIn: string;
90
+
91
+ constructor() {
92
+ this.secret = process.env.JWT_SECRET!;
93
+ this.expiresIn = process.env.JWT_EXPIRES_IN || '24h';
94
+
95
+ if (!this.secret) {
96
+ throw new Error('JWT_SECRET is not defined');
97
+ }
98
+ }
99
+
100
+ generateToken(payload: TokenPayload): string {
101
+ return jwt.sign(payload, this.secret, {
102
+ expiresIn: this.expiresIn,
103
+ issuer: 'api-service',
104
+ audience: 'api-client',
105
+ });
106
+ }
107
+
108
+ verifyToken(token: string): TokenPayload {
109
+ try {
110
+ return jwt.verify(token, this.secret, {
111
+ issuer: 'api-service',
112
+ audience: 'api-client',
113
+ }) as TokenPayload;
114
+ } catch (err) {
115
+ throw new Error('Invalid or expired token');
116
+ }
117
+ }
118
+ }
119
+ ```
120
+
121
+ #### Authentication Middleware
122
+
123
+ ```typescript
124
+ // src/presentation/middleware/authenticate.ts
125
+ import type { Request, Response, NextFunction } from 'express';
126
+ import { JwtService } from '../../infrastructure/auth/JwtService';
127
+
128
+ const jwtService = new JwtService();
129
+
130
+ export function authenticate(req: Request, res: Response, next: NextFunction) {
131
+ const authHeader = req.headers.authorization;
132
+
133
+ if (!authHeader || !authHeader.startsWith('Bearer ')) {
134
+ return res.status(401).json({
135
+ error: 'Unauthorized',
136
+ message: 'Missing or invalid authorization header',
137
+ timestamp: new Date().toISOString(),
138
+ path: req.path,
139
+ });
140
+ }
141
+
142
+ const token = authHeader.substring(7); // Remove 'Bearer ' prefix
143
+
144
+ try {
145
+ const payload = jwtService.verifyToken(token);
146
+
147
+ // Attach user info to request
148
+ req.user = {
149
+ id: payload.userId,
150
+ email: payload.email,
151
+ role: payload.role,
152
+ };
153
+
154
+ next();
155
+ } catch (err) {
156
+ return res.status(401).json({
157
+ error: 'Unauthorized',
158
+ message: 'Invalid or expired token',
159
+ timestamp: new Date().toISOString(),
160
+ path: req.path,
161
+ });
162
+ }
163
+ }
164
+ ```
165
+
166
+ #### Login Endpoint
167
+
168
+ ```typescript
169
+ // src/presentation/routes/auth.ts
170
+ import bcrypt from 'bcrypt';
171
+ import { JwtService } from '../../infrastructure/auth/JwtService';
172
+
173
+ const jwtService = new JwtService();
174
+
175
+ router.post('/api/v1/auth/login', async (req, res, next) => {
176
+ const { email, password } = req.body;
177
+
178
+ // Find user
179
+ const user = await userRepository.findByEmail(email);
180
+ if (!user) {
181
+ return res.status(401).json({
182
+ error: 'Unauthorized',
183
+ message: 'Invalid credentials',
184
+ });
185
+ }
186
+
187
+ // Verify password
188
+ const isValidPassword = await bcrypt.compare(password, user.passwordHash);
189
+ if (!isValidPassword) {
190
+ return res.status(401).json({
191
+ error: 'Unauthorized',
192
+ message: 'Invalid credentials',
193
+ });
194
+ }
195
+
196
+ // Generate token
197
+ const token = jwtService.generateToken({
198
+ userId: user.id,
199
+ email: user.email,
200
+ role: user.role,
201
+ });
202
+
203
+ res.json({
204
+ token,
205
+ user: {
206
+ id: user.id,
207
+ email: user.email,
208
+ role: user.role,
209
+ },
210
+ });
211
+ });
212
+ ```
213
+
214
+ #### Protected Route
215
+
216
+ ```typescript
217
+ import { authenticate } from '../middleware/authenticate';
218
+
219
+ router.get('/api/v1/profile', authenticate, async (req, res) => {
220
+ // req.user is populated by authenticate middleware
221
+ const user = await userRepository.findById(req.user.id);
222
+ res.json(user);
223
+ });
224
+ ```
225
+
226
+ ---
227
+
228
+ ## 3. Refresh Tokens
229
+
230
+ ### Why Refresh Tokens?
231
+
232
+ - Short-lived access tokens (15-30 min)
233
+ - Long-lived refresh tokens (7-30 days)
234
+ - Refresh tokens can be revoked
235
+
236
+ ### Implementation
237
+
238
+ #### Database Schema
239
+
240
+ ```sql
241
+ CREATE TABLE refresh_tokens (
242
+ id UUID PRIMARY KEY,
243
+ user_id UUID NOT NULL,
244
+ token_hash VARCHAR(255) NOT NULL,
245
+ expires_at TIMESTAMP NOT NULL,
246
+ created_at TIMESTAMP DEFAULT NOW(),
247
+ revoked_at TIMESTAMP
248
+ );
249
+ ```
250
+
251
+ #### Generate Refresh Token
252
+
253
+ ```typescript
254
+ import crypto from 'crypto';
255
+
256
+ export class RefreshTokenService {
257
+ async generateRefreshToken(userId: string): Promise<string> {
258
+ const token = crypto.randomBytes(32).toString('hex');
259
+ const tokenHash = crypto.createHash('sha256').update(token).digest('hex');
260
+
261
+ // Store in database
262
+ await refreshTokenRepository.create({
263
+ userId,
264
+ tokenHash,
265
+ expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000), // 30 days
266
+ });
267
+
268
+ return token;
269
+ }
270
+
271
+ async verifyRefreshToken(token: string): Promise<string | null> {
272
+ const tokenHash = crypto.createHash('sha256').update(token).digest('hex');
273
+
274
+ const refreshToken = await refreshTokenRepository.findByTokenHash(tokenHash);
275
+
276
+ if (!refreshToken || refreshToken.revokedAt || refreshToken.expiresAt < new Date()) {
277
+ return null;
278
+ }
279
+
280
+ return refreshToken.userId;
281
+ }
282
+
283
+ async revokeRefreshToken(token: string): Promise<void> {
284
+ const tokenHash = crypto.createHash('sha256').update(token).digest('hex');
285
+ await refreshTokenRepository.revoke(tokenHash);
286
+ }
287
+ }
288
+ ```
289
+
290
+ #### Refresh Endpoint
291
+
292
+ ```typescript
293
+ router.post('/api/v1/auth/refresh', async (req, res) => {
294
+ const { refreshToken } = req.body;
295
+
296
+ const userId = await refreshTokenService.verifyRefreshToken(refreshToken);
297
+
298
+ if (!userId) {
299
+ return res.status(401).json({
300
+ error: 'Unauthorized',
301
+ message: 'Invalid or expired refresh token',
302
+ });
303
+ }
304
+
305
+ const user = await userRepository.findById(userId);
306
+
307
+ // Generate new access token
308
+ const accessToken = jwtService.generateToken({
309
+ userId: user.id,
310
+ email: user.email,
311
+ role: user.role,
312
+ });
313
+
314
+ res.json({ accessToken });
315
+ });
316
+ ```
317
+
318
+ ---
319
+
320
+ ## 4. Role-Based Access Control (RBAC)
321
+
322
+ ### Authorization Middleware
323
+
324
+ ```typescript
325
+ // src/presentation/middleware/authorize.ts
326
+ import type { Request, Response, NextFunction } from 'express';
327
+
328
+ export function authorize(...allowedRoles: string[]) {
329
+ return (req: Request, res: Response, next: NextFunction) => {
330
+ if (!req.user) {
331
+ return res.status(401).json({
332
+ error: 'Unauthorized',
333
+ message: 'Authentication required',
334
+ });
335
+ }
336
+
337
+ if (!allowedRoles.includes(req.user.role)) {
338
+ return res.status(403).json({
339
+ error: 'Forbidden',
340
+ message: 'Insufficient permissions',
341
+ });
342
+ }
343
+
344
+ next();
345
+ };
346
+ }
347
+ ```
348
+
349
+ ### Usage
350
+
351
+ ```typescript
352
+ import { authenticate } from '../middleware/authenticate';
353
+ import { authorize } from '../middleware/authorize';
354
+
355
+ // Only admins can delete users
356
+ router.delete(
357
+ '/api/v1/users/:id',
358
+ authenticate,
359
+ authorize('admin'),
360
+ async (req, res) => {
361
+ // Delete user logic
362
+ }
363
+ );
364
+
365
+ // Admins and moderators can update users
366
+ router.patch(
367
+ '/api/v1/users/:id',
368
+ authenticate,
369
+ authorize('admin', 'moderator'),
370
+ async (req, res) => {
371
+ // Update user logic
372
+ }
373
+ );
374
+ ```
375
+
376
+ ---
377
+
378
+ ## 5. OAuth 2.0 (Third-Party Auth)
379
+
380
+ ### Overview
381
+
382
+ OAuth 2.0 allows users to authenticate using third-party providers (Google, GitHub, etc.).
383
+
384
+ ### Install Passport.js
385
+
386
+ ```bash
387
+ npm install passport passport-google-oauth20
388
+ npm install --save-dev @types/passport @types/passport-google-oauth20
389
+ ```
390
+
391
+ ### Configure Google OAuth
392
+
393
+ ```typescript
394
+ // src/infrastructure/auth/passport.ts
395
+ import passport from 'passport';
396
+ import { Strategy as GoogleStrategy } from 'passport-google-oauth20';
397
+
398
+ passport.use(
399
+ new GoogleStrategy(
400
+ {
401
+ clientID: process.env.GOOGLE_CLIENT_ID!,
402
+ clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
403
+ callbackURL: '/api/v1/auth/google/callback',
404
+ },
405
+ async (accessToken, refreshToken, profile, done) => {
406
+ try {
407
+ // Find or create user
408
+ let user = await userRepository.findByEmail(profile.emails![0].value);
409
+
410
+ if (!user) {
411
+ user = await userRepository.create({
412
+ email: profile.emails![0].value,
413
+ name: profile.displayName,
414
+ googleId: profile.id,
415
+ });
416
+ }
417
+
418
+ done(null, user);
419
+ } catch (err) {
420
+ done(err, undefined);
421
+ }
422
+ }
423
+ )
424
+ );
425
+ ```
426
+
427
+ ### OAuth Routes
428
+
429
+ ```typescript
430
+ import passport from 'passport';
431
+
432
+ // Redirect to Google login
433
+ router.get(
434
+ '/api/v1/auth/google',
435
+ passport.authenticate('google', { scope: ['profile', 'email'], session: false })
436
+ );
437
+
438
+ // Google callback
439
+ router.get(
440
+ '/api/v1/auth/google/callback',
441
+ passport.authenticate('google', { session: false }),
442
+ (req, res) => {
443
+ const user = req.user as User;
444
+
445
+ // Generate JWT
446
+ const token = jwtService.generateToken({
447
+ userId: user.id,
448
+ email: user.email,
449
+ role: user.role,
450
+ });
451
+
452
+ // Redirect to frontend with token
453
+ res.redirect(`${process.env.FRONTEND_URL}/auth/callback?token=${token}`);
454
+ }
455
+ );
456
+ ```
457
+
458
+ ---
459
+
460
+ ## 6. API Key Authentication
461
+
462
+ ### Overview
463
+
464
+ API keys are used for server-to-server authentication or public API access.
465
+
466
+ **Use cases:**
467
+ - Third-party integrations
468
+ - Webhooks
469
+ - Public API access
470
+
471
+ ### Generate API Key
472
+
473
+ ```typescript
474
+ import crypto from 'crypto';
475
+
476
+ export class ApiKeyService {
477
+ async generateApiKey(userId: string): Promise<string> {
478
+ const apiKey = `sk_${crypto.randomBytes(32).toString('hex')}`;
479
+ const keyHash = crypto.createHash('sha256').update(apiKey).digest('hex');
480
+
481
+ // Store in database
482
+ await apiKeyRepository.create({
483
+ userId,
484
+ keyHash,
485
+ createdAt: new Date(),
486
+ });
487
+
488
+ return apiKey;
489
+ }
490
+
491
+ async verifyApiKey(apiKey: string): Promise<string | null> {
492
+ const keyHash = crypto.createHash('sha256').update(apiKey).digest('hex');
493
+ const record = await apiKeyRepository.findByKeyHash(keyHash);
494
+
495
+ if (!record || record.revokedAt) {
496
+ return null;
497
+ }
498
+
499
+ return record.userId;
500
+ }
501
+ }
502
+ ```
503
+
504
+ ### API Key Middleware
505
+
506
+ ```typescript
507
+ // src/presentation/middleware/authenticateApiKey.ts
508
+ import type { Request, Response, NextFunction } from 'express';
509
+
510
+ export async function authenticateApiKey(
511
+ req: Request,
512
+ res: Response,
513
+ next: NextFunction
514
+ ) {
515
+ const apiKey = req.headers['x-api-key'] as string;
516
+
517
+ if (!apiKey) {
518
+ return res.status(401).json({
519
+ error: 'Unauthorized',
520
+ message: 'Missing API key',
521
+ });
522
+ }
523
+
524
+ const userId = await apiKeyService.verifyApiKey(apiKey);
525
+
526
+ if (!userId) {
527
+ return res.status(401).json({
528
+ error: 'Unauthorized',
529
+ message: 'Invalid API key',
530
+ });
531
+ }
532
+
533
+ const user = await userRepository.findById(userId);
534
+ req.user = user;
535
+
536
+ next();
537
+ }
538
+ ```
539
+
540
+ ---
541
+
542
+ ## 7. Password Hashing
543
+
544
+ ### Use bcrypt
545
+
546
+ ```bash
547
+ npm install bcrypt
548
+ npm install --save-dev @types/bcrypt
549
+ ```
550
+
551
+ ### Hash Password
552
+
553
+ ```typescript
554
+ import bcrypt from 'bcrypt';
555
+
556
+ const SALT_ROUNDS = 10;
557
+
558
+ export async function hashPassword(password: string): Promise<string> {
559
+ return bcrypt.hash(password, SALT_ROUNDS);
560
+ }
561
+
562
+ export async function verifyPassword(
563
+ password: string,
564
+ hash: string
565
+ ): Promise<boolean> {
566
+ return bcrypt.compare(password, hash);
567
+ }
568
+ ```
569
+
570
+ ### Usage
571
+
572
+ ```typescript
573
+ router.post('/api/v1/auth/register', async (req, res) => {
574
+ const { email, password } = req.body;
575
+
576
+ // Hash password
577
+ const passwordHash = await hashPassword(password);
578
+
579
+ // Create user
580
+ const user = await userRepository.create({
581
+ email,
582
+ passwordHash,
583
+ });
584
+
585
+ res.status(201).json({ user });
586
+ });
587
+ ```
588
+
589
+ ---
590
+
591
+ ## 8. Security Best Practices
592
+
593
+ ### Environment Variables
594
+
595
+ ```env
596
+ # JWT
597
+ JWT_SECRET=your-secret-key-min-32-chars
598
+ JWT_EXPIRES_IN=15m
599
+
600
+ # Refresh Token
601
+ REFRESH_TOKEN_EXPIRES_IN=30d
602
+
603
+ # OAuth
604
+ GOOGLE_CLIENT_ID=your-google-client-id
605
+ GOOGLE_CLIENT_SECRET=your-google-client-secret
606
+
607
+ # API Keys
608
+ API_KEY_PREFIX=sk_
609
+ ```
610
+
611
+ ### Security Headers (Helmet)
612
+
613
+ ```bash
614
+ npm install helmet
615
+ ```
616
+
617
+ ```typescript
618
+ import helmet from 'helmet';
619
+
620
+ app.use(helmet());
621
+ ```
622
+
623
+ ### Rate Limiting
624
+
625
+ ```typescript
626
+ import rateLimit from 'express-rate-limit';
627
+
628
+ const authLimiter = rateLimit({
629
+ windowMs: 15 * 60 * 1000, // 15 minutes
630
+ max: 5, // 5 attempts
631
+ message: 'Too many login attempts, please try again later',
632
+ });
633
+
634
+ router.post('/api/v1/auth/login', authLimiter, async (req, res) => {
635
+ // Login logic
636
+ });
637
+ ```
638
+
639
+ ### HTTPS Only
640
+
641
+ ```typescript
642
+ app.use((req, res, next) => {
643
+ if (process.env.NODE_ENV === 'production' && !req.secure) {
644
+ return res.redirect(`https://${req.headers.host}${req.url}`);
645
+ }
646
+ next();
647
+ });
648
+ ```
649
+
650
+ ---
651
+
652
+ ## 9. Testing Authentication
653
+
654
+ ### Test Login
655
+
656
+ ```typescript
657
+ import request from 'supertest';
658
+ import { app } from '../src/server';
659
+
660
+ describe('POST /api/v1/auth/login', () => {
661
+ it('should return token for valid credentials', async () => {
662
+ const response = await request(app)
663
+ .post('/api/v1/auth/login')
664
+ .send({
665
+ email: 'user@example.com',
666
+ password: 'SecureP@ss123',
667
+ })
668
+ .expect(200);
669
+
670
+ expect(response.body).toHaveProperty('token');
671
+ expect(response.body.token).toMatch(/^[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+$/); // JWT format
672
+ });
673
+
674
+ it('should return 401 for invalid credentials', async () => {
675
+ await request(app)
676
+ .post('/api/v1/auth/login')
677
+ .send({
678
+ email: 'user@example.com',
679
+ password: 'wrong-password',
680
+ })
681
+ .expect(401);
682
+ });
683
+ });
684
+ ```
685
+
686
+ ### Test Protected Route
687
+
688
+ ```typescript
689
+ describe('GET /api/v1/profile', () => {
690
+ it('should return user profile with valid token', async () => {
691
+ const token = generateValidToken();
692
+
693
+ const response = await request(app)
694
+ .get('/api/v1/profile')
695
+ .set('Authorization', `Bearer ${token}`)
696
+ .expect(200);
697
+
698
+ expect(response.body).toHaveProperty('id');
699
+ });
700
+
701
+ it('should return 401 without token', async () => {
702
+ await request(app).get('/api/v1/profile').expect(401);
703
+ });
704
+
705
+ it('should return 401 with invalid token', async () => {
706
+ await request(app)
707
+ .get('/api/v1/profile')
708
+ .set('Authorization', 'Bearer invalid-token')
709
+ .expect(401);
710
+ });
711
+ });
712
+ ```
713
+
714
+ ---
715
+
716
+ ## 10. Common Pitfalls
717
+
718
+ ### DON'T
719
+
720
+ 1. **Store passwords in plain text** - Always hash with bcrypt
721
+ 2. **Use weak JWT secrets** - Min 32 characters, random
722
+ 3. **Include sensitive data in JWT** - No passwords, credit cards
723
+ 4. **Expose JWT secret in client** - Server-side only
724
+ 5. **Use long-lived access tokens** - Max 30 minutes
725
+ 6. **Forget to validate token expiration** - Always check `exp` claim
726
+ 7. **Allow password reuse** - Check against previous passwords
727
+ 8. **Skip HTTPS in production** - Always use HTTPS
728
+
729
+ ---
730
+
731
+ ## References
732
+
733
+ - [JWT.io](https://jwt.io/)
734
+ - [OAuth 2.0 RFC](https://datatracker.ietf.org/doc/html/rfc6749)
735
+ - [OWASP Authentication Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html)
736
+ - [Passport.js Documentation](http://www.passportjs.org/)
737
+
738
+ ---
739
+
740
+ **Generated for**: my-project
741
+ **Template**: api
742
+ **Constitution Preset**: orchestrator