@eddacraft/anvil-adapters 0.1.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.
- package/AGENTS.md +180 -0
- package/BMAD_ADAPTER_SPEC.md +489 -0
- package/LICENSE +14 -0
- package/README.md +500 -0
- package/dist/aps-markdown/adapter.d.ts +102 -0
- package/dist/aps-markdown/adapter.d.ts.map +1 -0
- package/dist/aps-markdown/adapter.js +351 -0
- package/dist/aps-markdown/index.d.ts +8 -0
- package/dist/aps-markdown/index.d.ts.map +1 -0
- package/dist/aps-markdown/index.js +7 -0
- package/dist/base/file-discovery.d.ts +63 -0
- package/dist/base/file-discovery.d.ts.map +1 -0
- package/dist/base/file-discovery.js +246 -0
- package/dist/base/index.d.ts +10 -0
- package/dist/base/index.d.ts.map +1 -0
- package/dist/base/index.js +9 -0
- package/dist/base/registry.d.ts +155 -0
- package/dist/base/registry.d.ts.map +1 -0
- package/dist/base/registry.js +227 -0
- package/dist/base/testing.d.ts +102 -0
- package/dist/base/testing.d.ts.map +1 -0
- package/dist/base/testing.js +221 -0
- package/dist/base/types.d.ts +255 -0
- package/dist/base/types.d.ts.map +1 -0
- package/dist/base/types.js +78 -0
- package/dist/base/utils.d.ts +127 -0
- package/dist/base/utils.d.ts.map +1 -0
- package/dist/base/utils.js +254 -0
- package/dist/bmad/format-adapter.d.ts +76 -0
- package/dist/bmad/format-adapter.d.ts.map +1 -0
- package/dist/bmad/format-adapter.js +186 -0
- package/dist/bmad/index.d.ts +12 -0
- package/dist/bmad/index.d.ts.map +1 -0
- package/dist/bmad/index.js +10 -0
- package/dist/bmad/parser.d.ts +12 -0
- package/dist/bmad/parser.d.ts.map +1 -0
- package/dist/bmad/parser.js +181 -0
- package/dist/bmad/serializer.d.ts +16 -0
- package/dist/bmad/serializer.d.ts.map +1 -0
- package/dist/bmad/serializer.js +170 -0
- package/dist/bmad/types.d.ts +127 -0
- package/dist/bmad/types.d.ts.map +1 -0
- package/dist/bmad/types.js +47 -0
- package/dist/bmad/utils.d.ts +120 -0
- package/dist/bmad/utils.d.ts.map +1 -0
- package/dist/bmad/utils.js +480 -0
- package/dist/common/index.d.ts +3 -0
- package/dist/common/index.d.ts.map +1 -0
- package/dist/common/index.js +2 -0
- package/dist/common/registry.d.ts +18 -0
- package/dist/common/registry.d.ts.map +1 -0
- package/dist/common/registry.js +58 -0
- package/dist/common/types.d.ts +68 -0
- package/dist/common/types.d.ts.map +1 -0
- package/dist/common/types.js +12 -0
- package/dist/generic/format-adapter.d.ts +64 -0
- package/dist/generic/format-adapter.d.ts.map +1 -0
- package/dist/generic/format-adapter.js +159 -0
- package/dist/generic/index.d.ts +10 -0
- package/dist/generic/index.d.ts.map +1 -0
- package/dist/generic/index.js +9 -0
- package/dist/generic/parser.d.ts +11 -0
- package/dist/generic/parser.d.ts.map +1 -0
- package/dist/generic/parser.js +106 -0
- package/dist/generic/serializer.d.ts +11 -0
- package/dist/generic/serializer.d.ts.map +1 -0
- package/dist/generic/serializer.js +118 -0
- package/dist/generic/types.d.ts +52 -0
- package/dist/generic/types.d.ts.map +1 -0
- package/dist/generic/types.js +6 -0
- package/dist/generic/utils.d.ts +51 -0
- package/dist/generic/utils.d.ts.map +1 -0
- package/dist/generic/utils.js +232 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +31 -0
- package/dist/speckit/export.d.ts +22 -0
- package/dist/speckit/export.d.ts.map +1 -0
- package/dist/speckit/export.js +384 -0
- package/dist/speckit/format-adapter.d.ts +104 -0
- package/dist/speckit/format-adapter.d.ts.map +1 -0
- package/dist/speckit/format-adapter.js +488 -0
- package/dist/speckit/import-v2.d.ts +33 -0
- package/dist/speckit/import-v2.d.ts.map +1 -0
- package/dist/speckit/import-v2.js +361 -0
- package/dist/speckit/import.d.ts +16 -0
- package/dist/speckit/import.d.ts.map +1 -0
- package/dist/speckit/import.js +247 -0
- package/dist/speckit/index.d.ts +5 -0
- package/dist/speckit/index.d.ts.map +1 -0
- package/dist/speckit/index.js +4 -0
- package/dist/speckit/parser.d.ts +28 -0
- package/dist/speckit/parser.d.ts.map +1 -0
- package/dist/speckit/parser.js +283 -0
- package/dist/speckit/parsers/plan-parser.d.ts +71 -0
- package/dist/speckit/parsers/plan-parser.d.ts.map +1 -0
- package/dist/speckit/parsers/plan-parser.js +216 -0
- package/dist/speckit/parsers/spec-parser.d.ts +67 -0
- package/dist/speckit/parsers/spec-parser.d.ts.map +1 -0
- package/dist/speckit/parsers/spec-parser.js +255 -0
- package/dist/speckit/parsers/tasks-parser.d.ts +57 -0
- package/dist/speckit/parsers/tasks-parser.d.ts.map +1 -0
- package/dist/speckit/parsers/tasks-parser.js +157 -0
- package/package.json +23 -0
- package/project.json +29 -0
- package/src/__tests__/adapter-edge-cases.test.ts +937 -0
- package/src/__tests__/bmad-format-adapter.test.ts +1470 -0
- package/src/__tests__/fixtures/aps/expected-output.json +83 -0
- package/src/__tests__/fixtures/bmad/invalid-malformed-yaml.md +16 -0
- package/src/__tests__/fixtures/bmad/invalid-no-requirements.md +23 -0
- package/src/__tests__/fixtures/bmad/invalid-only-yaml.md +16 -0
- package/src/__tests__/fixtures/bmad/invalid-too-short.md +3 -0
- package/src/__tests__/fixtures/bmad/invalid-wrong-format.md +40 -0
- package/src/__tests__/fixtures/bmad/valid-agent.md +27 -0
- package/src/__tests__/fixtures/bmad/valid-architecture.md +116 -0
- package/src/__tests__/fixtures/bmad/valid-complex-prd.md +161 -0
- package/src/__tests__/fixtures/bmad/valid-epic.md +73 -0
- package/src/__tests__/fixtures/bmad/valid-minimal-prd.md +19 -0
- package/src/__tests__/fixtures/bmad/valid-prd.md +107 -0
- package/src/__tests__/fixtures/bmad/valid-story.md +107 -0
- package/src/__tests__/fixtures/bmad/valid-task.md +79 -0
- package/src/__tests__/fixtures/bmad/valid-v6-prd.md +35 -0
- package/src/__tests__/fixtures/generic/plan-detailed.md +39 -0
- package/src/__tests__/fixtures/generic/prd-simple.md +27 -0
- package/src/__tests__/fixtures/generic/rfc-example.md +26 -0
- package/src/__tests__/fixtures/generic/todo-list.md +23 -0
- package/src/__tests__/fixtures/speckit/sample-plan.md +63 -0
- package/src/__tests__/fixtures/speckit/sample-spec-namespaced.md +50 -0
- package/src/__tests__/fixtures/speckit/sample-spec.md +105 -0
- package/src/__tests__/fixtures/speckit/sample-tasks.md +87 -0
- package/src/__tests__/fixtures/speckit-official/auth-feature/plan.md +272 -0
- package/src/__tests__/fixtures/speckit-official/auth-feature/spec.md +149 -0
- package/src/__tests__/fixtures/speckit-official/auth-feature/tasks.md +169 -0
- package/src/__tests__/generic-format-adapter.test.ts +398 -0
- package/src/__tests__/speckit-export.test.ts +233 -0
- package/src/__tests__/speckit-format-adapter.test.ts +832 -0
- package/src/__tests__/speckit-import-v2.test.ts +253 -0
- package/src/__tests__/speckit-import.test.ts +209 -0
- package/src/__tests__/speckit-parser.test.ts +219 -0
- package/src/__tests__/speckit-spec-parser.test.ts +120 -0
- package/src/aps-markdown/__tests__/__fixtures__/simple-leaf.aps.md +17 -0
- package/src/aps-markdown/__tests__/adapter.test.ts +393 -0
- package/src/aps-markdown/adapter.ts +455 -0
- package/src/aps-markdown/index.ts +8 -0
- package/src/base/__tests__/registry.test.ts +515 -0
- package/src/base/file-discovery.ts +305 -0
- package/src/base/index.ts +10 -0
- package/src/base/registry.ts +263 -0
- package/src/base/testing.ts +334 -0
- package/src/base/types.ts +342 -0
- package/src/base/utils.ts +306 -0
- package/src/bmad/format-adapter.ts +227 -0
- package/src/bmad/index.ts +21 -0
- package/src/bmad/parser.ts +224 -0
- package/src/bmad/serializer.ts +206 -0
- package/src/bmad/types.ts +135 -0
- package/src/bmad/utils.ts +575 -0
- package/src/common/index.ts +2 -0
- package/src/common/registry.ts +72 -0
- package/src/common/types.ts +84 -0
- package/src/generic/__tests__/serializer.test.ts +167 -0
- package/src/generic/format-adapter.ts +200 -0
- package/src/generic/index.ts +11 -0
- package/src/generic/parser.ts +129 -0
- package/src/generic/serializer.ts +134 -0
- package/src/generic/types.ts +53 -0
- package/src/generic/utils.ts +270 -0
- package/src/index.ts +48 -0
- package/src/speckit/export.ts +489 -0
- package/src/speckit/format-adapter.ts +595 -0
- package/src/speckit/import-v2.ts +445 -0
- package/src/speckit/import.ts +305 -0
- package/src/speckit/index.ts +4 -0
- package/src/speckit/parser.ts +351 -0
- package/src/speckit/parsers/plan-parser.ts +342 -0
- package/src/speckit/parsers/spec-parser.ts +379 -0
- package/src/speckit/parsers/tasks-parser.ts +246 -0
- package/tsconfig.json +26 -0
- package/tsconfig.lib.json +21 -0
- package/tsconfig.lib.tsbuildinfo +1 -0
- package/tsconfig.spec.json +9 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/vitest.config.ts +14 -0
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
# Implementation Plan: User Authentication System
|
|
2
|
+
|
|
3
|
+
**Branch**: `feature/001-auth-system` **Date**: 2025-01-15 **Spec**:
|
|
4
|
+
[spec.md](./spec.md)
|
|
5
|
+
|
|
6
|
+
## Summary
|
|
7
|
+
|
|
8
|
+
Implement a secure JWT-based authentication system with user registration,
|
|
9
|
+
login, password reset, and token refresh capabilities. The system will use
|
|
10
|
+
Node.js/TypeScript with Express.js, PostgreSQL for data storage, and Redis for
|
|
11
|
+
session management.
|
|
12
|
+
|
|
13
|
+
## Technical Context
|
|
14
|
+
|
|
15
|
+
- **Language/Version**: TypeScript 5.3, Node.js 20 LTS
|
|
16
|
+
- **Dependencies**:
|
|
17
|
+
- express ^4.18.0
|
|
18
|
+
- jsonwebtoken ^9.0.2
|
|
19
|
+
- bcrypt ^5.1.1
|
|
20
|
+
- @types/node ^20.0.0
|
|
21
|
+
- zod ^3.22.0 (validation)
|
|
22
|
+
- pg ^8.11.0 (PostgreSQL client)
|
|
23
|
+
- redis ^4.6.0 (session management)
|
|
24
|
+
- nodemailer ^6.9.0 (email sending)
|
|
25
|
+
- **Storage**: PostgreSQL 15+ for user data, Redis 7+ for session/rate limiting
|
|
26
|
+
- **Testing**: Vitest for unit tests, Supertest for API tests
|
|
27
|
+
- **Target**: REST API server, containerized with Docker
|
|
28
|
+
- **Type**: Backend service
|
|
29
|
+
- **Performance Goals**:
|
|
30
|
+
- Authentication endpoint response time < 200ms (p95)
|
|
31
|
+
- Support 100 concurrent requests
|
|
32
|
+
- **Constraints**:
|
|
33
|
+
- Must comply with OWASP authentication guidelines
|
|
34
|
+
- Must support horizontal scaling
|
|
35
|
+
- **Scale**: Expected initial load: 1000 daily active users, 10,000 total users
|
|
36
|
+
|
|
37
|
+
## Constitution Check
|
|
38
|
+
|
|
39
|
+
✅ **Modularity**: Authentication logic separated into controller, service, and
|
|
40
|
+
repository layers ✅ **Testability**: Each component has clear interfaces for
|
|
41
|
+
mocking ✅ **Security**: Follows OWASP Top 10 guidelines, password hashing, JWT
|
|
42
|
+
validation ✅ **Performance**: Redis caching for rate limiting, indexed database
|
|
43
|
+
queries ✅ **Maintainability**: TypeScript for type safety, clear error handling
|
|
44
|
+
✅ **Documentation**: OpenAPI/Swagger docs for all endpoints
|
|
45
|
+
|
|
46
|
+
## Project Structure
|
|
47
|
+
|
|
48
|
+
### Documentation
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
specs/
|
|
52
|
+
└── 001-auth-system/
|
|
53
|
+
├── spec.md (this file's sibling)
|
|
54
|
+
├── plan.md (this file)
|
|
55
|
+
└── tasks.md (to be generated)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Source Code Structure
|
|
59
|
+
|
|
60
|
+
#### Option 1: Modular Backend Service (Selected)
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
src/
|
|
64
|
+
├── modules/
|
|
65
|
+
│ └── auth/
|
|
66
|
+
│ ├── auth.controller.ts # HTTP endpoints
|
|
67
|
+
│ ├── auth.service.ts # Business logic
|
|
68
|
+
│ ├── auth.repository.ts # Data access
|
|
69
|
+
│ ├── auth.validation.ts # Zod schemas
|
|
70
|
+
│ ├── auth.middleware.ts # JWT verification
|
|
71
|
+
│ └── __tests__/
|
|
72
|
+
│ ├── auth.controller.test.ts
|
|
73
|
+
│ ├── auth.service.test.ts
|
|
74
|
+
│ └── auth.integration.test.ts
|
|
75
|
+
├── entities/
|
|
76
|
+
│ ├── user.entity.ts
|
|
77
|
+
│ ├── auth-token.entity.ts
|
|
78
|
+
│ ├── audit-log.entity.ts
|
|
79
|
+
│ └── password-reset.entity.ts
|
|
80
|
+
├── database/
|
|
81
|
+
│ ├── migrations/
|
|
82
|
+
│ │ ├── 001_create_users_table.sql
|
|
83
|
+
│ │ ├── 002_create_auth_tokens_table.sql
|
|
84
|
+
│ │ ├── 003_create_audit_logs_table.sql
|
|
85
|
+
│ │ └── 004_create_password_resets_table.sql
|
|
86
|
+
│ └── connection.ts
|
|
87
|
+
├── utils/
|
|
88
|
+
│ ├── jwt.util.ts
|
|
89
|
+
│ ├── hash.util.ts
|
|
90
|
+
│ ├── email.util.ts
|
|
91
|
+
│ └── rate-limit.util.ts
|
|
92
|
+
├── config/
|
|
93
|
+
│ ├── env.ts
|
|
94
|
+
│ └── constants.ts
|
|
95
|
+
└── app.ts
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Implementation Details
|
|
99
|
+
|
|
100
|
+
### API Endpoints
|
|
101
|
+
|
|
102
|
+
**POST /api/auth/register**
|
|
103
|
+
|
|
104
|
+
- Request: `{ email: string, password: string }`
|
|
105
|
+
- Response: `{ user: { id, email }, token: string }`
|
|
106
|
+
- Errors: 400 (validation), 409 (email exists)
|
|
107
|
+
|
|
108
|
+
**POST /api/auth/login**
|
|
109
|
+
|
|
110
|
+
- Request: `{ email: string, password: string }`
|
|
111
|
+
- Response: `{ user: { id, email }, token: string, refreshToken: string }`
|
|
112
|
+
- Errors: 401 (invalid credentials), 423 (account locked)
|
|
113
|
+
|
|
114
|
+
**POST /api/auth/logout**
|
|
115
|
+
|
|
116
|
+
- Request: Headers with JWT
|
|
117
|
+
- Response: 204 No Content
|
|
118
|
+
- Errors: 401 (unauthorized)
|
|
119
|
+
|
|
120
|
+
**POST /api/auth/refresh**
|
|
121
|
+
|
|
122
|
+
- Request: `{ refreshToken: string }`
|
|
123
|
+
- Response: `{ token: string, refreshToken: string }`
|
|
124
|
+
- Errors: 401 (invalid/expired token)
|
|
125
|
+
|
|
126
|
+
**POST /api/auth/password-reset/request**
|
|
127
|
+
|
|
128
|
+
- Request: `{ email: string }`
|
|
129
|
+
- Response: 200 OK (always, for security)
|
|
130
|
+
- Errors: 429 (rate limit)
|
|
131
|
+
|
|
132
|
+
**POST /api/auth/password-reset/confirm**
|
|
133
|
+
|
|
134
|
+
- Request: `{ token: string, newPassword: string }`
|
|
135
|
+
- Response: 200 OK
|
|
136
|
+
- Errors: 400 (invalid token), 410 (expired token)
|
|
137
|
+
|
|
138
|
+
### Database Schema
|
|
139
|
+
|
|
140
|
+
**users table**
|
|
141
|
+
|
|
142
|
+
```sql
|
|
143
|
+
CREATE TABLE users (
|
|
144
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
145
|
+
email VARCHAR(255) UNIQUE NOT NULL,
|
|
146
|
+
hashed_password VARCHAR(255) NOT NULL,
|
|
147
|
+
created_at TIMESTAMP DEFAULT NOW(),
|
|
148
|
+
updated_at TIMESTAMP DEFAULT NOW(),
|
|
149
|
+
last_login TIMESTAMP,
|
|
150
|
+
account_locked BOOLEAN DEFAULT FALSE,
|
|
151
|
+
failed_login_count INTEGER DEFAULT 0
|
|
152
|
+
);
|
|
153
|
+
CREATE INDEX idx_users_email ON users(email);
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
**auth_tokens table**
|
|
157
|
+
|
|
158
|
+
```sql
|
|
159
|
+
CREATE TABLE auth_tokens (
|
|
160
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
161
|
+
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
|
|
162
|
+
refresh_token VARCHAR(255) UNIQUE NOT NULL,
|
|
163
|
+
expires_at TIMESTAMP NOT NULL,
|
|
164
|
+
created_at TIMESTAMP DEFAULT NOW()
|
|
165
|
+
);
|
|
166
|
+
CREATE INDEX idx_auth_tokens_user_id ON auth_tokens(user_id);
|
|
167
|
+
CREATE INDEX idx_auth_tokens_refresh_token ON auth_tokens(refresh_token);
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
**audit_logs table**
|
|
171
|
+
|
|
172
|
+
```sql
|
|
173
|
+
CREATE TABLE audit_logs (
|
|
174
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
175
|
+
user_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
|
176
|
+
event_type VARCHAR(50) NOT NULL,
|
|
177
|
+
ip_address VARCHAR(45),
|
|
178
|
+
user_agent TEXT,
|
|
179
|
+
success BOOLEAN NOT NULL,
|
|
180
|
+
error_message TEXT,
|
|
181
|
+
timestamp TIMESTAMP DEFAULT NOW()
|
|
182
|
+
);
|
|
183
|
+
CREATE INDEX idx_audit_logs_user_id ON audit_logs(user_id);
|
|
184
|
+
CREATE INDEX idx_audit_logs_timestamp ON audit_logs(timestamp);
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
**password_resets table**
|
|
188
|
+
|
|
189
|
+
```sql
|
|
190
|
+
CREATE TABLE password_resets (
|
|
191
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
192
|
+
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
|
|
193
|
+
token VARCHAR(255) UNIQUE NOT NULL,
|
|
194
|
+
expires_at TIMESTAMP NOT NULL,
|
|
195
|
+
used BOOLEAN DEFAULT FALSE,
|
|
196
|
+
created_at TIMESTAMP DEFAULT NOW()
|
|
197
|
+
);
|
|
198
|
+
CREATE INDEX idx_password_resets_token ON password_resets(token);
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Configuration
|
|
202
|
+
|
|
203
|
+
**Environment Variables**
|
|
204
|
+
|
|
205
|
+
```
|
|
206
|
+
# Database
|
|
207
|
+
DATABASE_URL=postgresql://user:pass@localhost:5432/dbname
|
|
208
|
+
REDIS_URL=redis://localhost:6379
|
|
209
|
+
|
|
210
|
+
# JWT
|
|
211
|
+
JWT_SECRET=<generate-secure-secret>
|
|
212
|
+
JWT_EXPIRES_IN=15m
|
|
213
|
+
REFRESH_TOKEN_EXPIRES_IN=7d
|
|
214
|
+
|
|
215
|
+
# Email
|
|
216
|
+
SMTP_HOST=smtp.example.com
|
|
217
|
+
SMTP_PORT=587
|
|
218
|
+
SMTP_USER=auth@example.com
|
|
219
|
+
SMTP_PASS=<smtp-password>
|
|
220
|
+
|
|
221
|
+
# Security
|
|
222
|
+
BCRYPT_ROUNDS=10
|
|
223
|
+
MAX_LOGIN_ATTEMPTS=5
|
|
224
|
+
RATE_LIMIT_WINDOW=60000
|
|
225
|
+
RATE_LIMIT_MAX_REQUESTS=10
|
|
226
|
+
PASSWORD_RESET_EXPIRY=24h
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Security Measures
|
|
230
|
+
|
|
231
|
+
1. **Password Security**
|
|
232
|
+
- Bcrypt hashing with cost factor 10
|
|
233
|
+
- Minimum 8 characters, complexity requirements enforced
|
|
234
|
+
- Old passwords not stored (for comparison)
|
|
235
|
+
|
|
236
|
+
2. **JWT Security**
|
|
237
|
+
- Signed with HS256 algorithm
|
|
238
|
+
- Short expiration (15 minutes)
|
|
239
|
+
- Refresh tokens stored in database, long-lived (7 days)
|
|
240
|
+
- Token blacklisting on logout via Redis
|
|
241
|
+
|
|
242
|
+
3. **Rate Limiting**
|
|
243
|
+
- 10 requests per minute per IP on auth endpoints
|
|
244
|
+
- Implemented with Redis sliding window
|
|
245
|
+
- Account lockout after 5 failed login attempts
|
|
246
|
+
|
|
247
|
+
4. **Audit Logging**
|
|
248
|
+
- All authentication events logged
|
|
249
|
+
- IP address and user agent captured
|
|
250
|
+
- 90-day retention policy
|
|
251
|
+
|
|
252
|
+
## Complexity Tracking
|
|
253
|
+
|
|
254
|
+
### Complex Design Decision: Token Refresh Strategy
|
|
255
|
+
|
|
256
|
+
**Problem**: Need to balance security (short-lived tokens) with UX (not forcing
|
|
257
|
+
frequent re-logins)
|
|
258
|
+
|
|
259
|
+
**Solution**: Dual-token approach
|
|
260
|
+
|
|
261
|
+
- Short-lived JWT (15 min) for API access
|
|
262
|
+
- Long-lived refresh token (7 days) stored in database
|
|
263
|
+
- Automatic token refresh before expiration
|
|
264
|
+
- Refresh token rotation on each use
|
|
265
|
+
|
|
266
|
+
**Justification**:
|
|
267
|
+
|
|
268
|
+
- JWT is stateless and fast to validate
|
|
269
|
+
- Refresh tokens are revocable (database-backed)
|
|
270
|
+
- Compromise of JWT has limited window
|
|
271
|
+
- UX stays smooth with automatic refresh
|
|
272
|
+
- Meets security best practices (OWASP)
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# Feature: User Authentication System
|
|
2
|
+
|
|
3
|
+
**Branch**: `feature/001-auth-system` **Date**: 2025-01-15 **Status**: Draft
|
|
4
|
+
|
|
5
|
+
## User Scenarios & Testing
|
|
6
|
+
|
|
7
|
+
### P1: User Registration
|
|
8
|
+
|
|
9
|
+
**As a** new user **I want to** create an account with email and password **So
|
|
10
|
+
that** I can access the application securely
|
|
11
|
+
|
|
12
|
+
**Acceptance Scenarios:**
|
|
13
|
+
|
|
14
|
+
- User submits valid email and strong password
|
|
15
|
+
- System validates email format and password strength
|
|
16
|
+
- System creates user account and sends confirmation email
|
|
17
|
+
- User receives success message
|
|
18
|
+
|
|
19
|
+
**Edge Cases:**
|
|
20
|
+
|
|
21
|
+
- Email already registered → Show error message
|
|
22
|
+
- Weak password → Show password requirements
|
|
23
|
+
- Invalid email format → Show format error
|
|
24
|
+
- Network failure during registration → Show retry option
|
|
25
|
+
|
|
26
|
+
### P1: User Login
|
|
27
|
+
|
|
28
|
+
**As a** registered user **I want to** log in with my credentials **So that** I
|
|
29
|
+
can access my personalized content
|
|
30
|
+
|
|
31
|
+
**Acceptance Scenarios:**
|
|
32
|
+
|
|
33
|
+
- User enters valid email and password
|
|
34
|
+
- System validates credentials
|
|
35
|
+
- System generates JWT token
|
|
36
|
+
- User is redirected to dashboard
|
|
37
|
+
|
|
38
|
+
**Edge Cases:**
|
|
39
|
+
|
|
40
|
+
- Invalid credentials → Show error, increment failure count
|
|
41
|
+
- Account locked (5 failed attempts) → Show account locked message
|
|
42
|
+
- Expired session → Redirect to login with message
|
|
43
|
+
- [NEEDS CLARIFICATION: Should we support "remember me" functionality?]
|
|
44
|
+
|
|
45
|
+
### P2: Password Reset
|
|
46
|
+
|
|
47
|
+
**As a** user who forgot their password **I want to** reset my password via
|
|
48
|
+
email **So that** I can regain access to my account
|
|
49
|
+
|
|
50
|
+
**Acceptance Scenarios:**
|
|
51
|
+
|
|
52
|
+
- User requests password reset
|
|
53
|
+
- System sends reset link to registered email
|
|
54
|
+
- User clicks link and sets new password
|
|
55
|
+
- User can log in with new password
|
|
56
|
+
|
|
57
|
+
**Edge Cases:**
|
|
58
|
+
|
|
59
|
+
- Email not found → Show generic message for security
|
|
60
|
+
- Reset link expired (24 hours) → Show error, offer to resend
|
|
61
|
+
- Weak new password → Show requirements
|
|
62
|
+
- [NEEDS CLARIFICATION: Should old passwords be prevented from reuse?]
|
|
63
|
+
|
|
64
|
+
### P3: Token Refresh
|
|
65
|
+
|
|
66
|
+
**As a** logged-in user **I want** my session to be extended automatically **So
|
|
67
|
+
that** I don't lose my work due to session expiration
|
|
68
|
+
|
|
69
|
+
**Acceptance Scenarios:**
|
|
70
|
+
|
|
71
|
+
- User's token expires during active session
|
|
72
|
+
- System automatically requests refresh token
|
|
73
|
+
- New JWT token is issued
|
|
74
|
+
- User continues working without interruption
|
|
75
|
+
|
|
76
|
+
**Edge Cases:**
|
|
77
|
+
|
|
78
|
+
- Refresh token expired → Force re-login
|
|
79
|
+
- Network failure during refresh → Queue and retry
|
|
80
|
+
- [NEEDS CLARIFICATION: What is the token expiration policy?]
|
|
81
|
+
|
|
82
|
+
## Requirements
|
|
83
|
+
|
|
84
|
+
### Functional Requirements
|
|
85
|
+
|
|
86
|
+
**FR-001**: System MUST validate email addresses using RFC 5322 standard
|
|
87
|
+
**FR-002**: System MUST enforce password requirements: minimum 8 characters, one
|
|
88
|
+
uppercase, one lowercase, one number, one special character **FR-003**: System
|
|
89
|
+
MUST hash passwords using bcrypt with minimum cost factor of 10 **FR-004**:
|
|
90
|
+
System MUST generate JWT tokens with configurable expiration time **FR-005**:
|
|
91
|
+
System MUST lock accounts after 5 consecutive failed login attempts **FR-006**:
|
|
92
|
+
System MUST send password reset emails with time-limited tokens (24 hours)
|
|
93
|
+
**FR-007**: System MUST retain user authentication audit logs for 90 days
|
|
94
|
+
**FR-008**: [NEEDS CLARIFICATION: Token expiration time - 15 minutes or 1 hour?]
|
|
95
|
+
**FR-009**: [NEEDS CLARIFICATION: Should we support OAuth providers (Google,
|
|
96
|
+
GitHub)?] **FR-010**: System MUST rate-limit authentication endpoints to prevent
|
|
97
|
+
brute force attacks
|
|
98
|
+
|
|
99
|
+
### Key Entities
|
|
100
|
+
|
|
101
|
+
**User**
|
|
102
|
+
|
|
103
|
+
- Represents: A person with access to the application
|
|
104
|
+
- Key Attributes: id, email, hashed_password, created_at, last_login,
|
|
105
|
+
account_locked, failed_login_count
|
|
106
|
+
- Relationships: Has many AuthTokens, has many AuditLogs
|
|
107
|
+
|
|
108
|
+
**AuthToken**
|
|
109
|
+
|
|
110
|
+
- Represents: A JWT token for authentication
|
|
111
|
+
- Key Attributes: id, user_id, token, expires_at, refresh_token, created_at
|
|
112
|
+
- Relationships: Belongs to User
|
|
113
|
+
|
|
114
|
+
**AuditLog**
|
|
115
|
+
|
|
116
|
+
- Represents: Record of authentication events
|
|
117
|
+
- Key Attributes: id, user_id, event_type, ip_address, user_agent, success,
|
|
118
|
+
timestamp
|
|
119
|
+
- Relationships: Belongs to User
|
|
120
|
+
|
|
121
|
+
**PasswordReset**
|
|
122
|
+
|
|
123
|
+
- Represents: Password reset request
|
|
124
|
+
- Key Attributes: id, user_id, token, expires_at, used, created_at
|
|
125
|
+
- Relationships: Belongs to User
|
|
126
|
+
|
|
127
|
+
## Success Criteria
|
|
128
|
+
|
|
129
|
+
### Quantitative Metrics
|
|
130
|
+
|
|
131
|
+
- Authentication endpoint response time < 200ms (p95)
|
|
132
|
+
- Successfully handle 100 concurrent login requests
|
|
133
|
+
- Zero plain-text password storage
|
|
134
|
+
- Account lockout triggers within 100ms of 5th failed attempt
|
|
135
|
+
- Password reset emails delivered within 30 seconds
|
|
136
|
+
|
|
137
|
+
### Qualitative Metrics
|
|
138
|
+
|
|
139
|
+
- Users can complete registration in under 2 minutes
|
|
140
|
+
- Login flow requires no more than 2 steps
|
|
141
|
+
- Error messages are clear and actionable
|
|
142
|
+
- Password reset process is intuitive and requires no support intervention
|
|
143
|
+
|
|
144
|
+
### Security Metrics
|
|
145
|
+
|
|
146
|
+
- All passwords stored with bcrypt cost factor >= 10
|
|
147
|
+
- JWT tokens properly signed and validated
|
|
148
|
+
- Rate limiting prevents more than 10 login attempts per minute per IP
|
|
149
|
+
- Audit logs capture all authentication events
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# Tasks: User Authentication System
|
|
2
|
+
|
|
3
|
+
**Branch**: `feature/001-auth-system` **Date**: 2025-01-15 **Spec**:
|
|
4
|
+
[spec.md](./spec.md) | **Plan**: [plan.md](./plan.md)
|
|
5
|
+
|
|
6
|
+
## Phase 1: Setup
|
|
7
|
+
|
|
8
|
+
- [001] Create project structure and configuration files
|
|
9
|
+
- [002] Set up database connection and migration system
|
|
10
|
+
- [003] Configure TypeScript, ESLint, and testing environment
|
|
11
|
+
- [004] Set up Redis connection for rate limiting and caching
|
|
12
|
+
- [005] Configure environment variables and validation
|
|
13
|
+
|
|
14
|
+
**Checkpoint**: Project builds successfully, database and Redis connections
|
|
15
|
+
established
|
|
16
|
+
|
|
17
|
+
## Phase 2: Foundational Infrastructure
|
|
18
|
+
|
|
19
|
+
- [006] Create database migrations for all tables (users, auth_tokens,
|
|
20
|
+
audit_logs, password_resets)
|
|
21
|
+
- [007] Implement User entity with TypeScript types
|
|
22
|
+
- [008] Implement AuthToken entity with TypeScript types
|
|
23
|
+
- [009] Implement AuditLog entity with TypeScript types
|
|
24
|
+
- [010] Implement PasswordReset entity with TypeScript types
|
|
25
|
+
- [011] Create utility functions for JWT generation and validation
|
|
26
|
+
- [012] Create utility functions for password hashing and verification
|
|
27
|
+
- [013] Create utility function for email sending
|
|
28
|
+
- [014] Create rate limiting middleware using Redis
|
|
29
|
+
- [015] Write tests for all utility functions
|
|
30
|
+
|
|
31
|
+
**Checkpoint**: All foundational utilities tested and working
|
|
32
|
+
|
|
33
|
+
## Phase 3: User Story - P1: User Registration
|
|
34
|
+
|
|
35
|
+
- [016] Write failing tests for registration validation (email format, password
|
|
36
|
+
strength)
|
|
37
|
+
- [017] Implement Zod schemas for registration request validation
|
|
38
|
+
- [018] Write failing tests for AuthRepository.createUser()
|
|
39
|
+
- [019] Implement AuthRepository.createUser() method
|
|
40
|
+
- [020] Write failing tests for AuthService.register()
|
|
41
|
+
- [021] Implement AuthService.register() with email uniqueness check
|
|
42
|
+
- [022] Implement password hashing in registration flow
|
|
43
|
+
- [023] Write failing tests for POST /api/auth/register endpoint
|
|
44
|
+
- [024] Implement AuthController.register() endpoint
|
|
45
|
+
- [025] Add audit logging for registration events
|
|
46
|
+
- [026] Add email confirmation sending (placeholder for now)
|
|
47
|
+
- [027] Run all registration tests and verify they pass
|
|
48
|
+
|
|
49
|
+
**Checkpoint**: Users can successfully register with valid credentials, invalid
|
|
50
|
+
attempts are rejected
|
|
51
|
+
|
|
52
|
+
## Phase 4: User Story - P1: User Login
|
|
53
|
+
|
|
54
|
+
- [028] Write failing tests for login validation
|
|
55
|
+
- [029] Implement Zod schemas for login request validation
|
|
56
|
+
- [030] Write failing tests for AuthRepository.findByEmail()
|
|
57
|
+
- [031] Implement AuthRepository.findByEmail() method
|
|
58
|
+
- [032] Write failing tests for AuthService.login() with credential validation
|
|
59
|
+
- [033] Implement AuthService.login() with bcrypt password verification
|
|
60
|
+
- [034] Implement JWT and refresh token generation on successful login
|
|
61
|
+
- [035] Write failing tests for AuthRepository.saveRefreshToken()
|
|
62
|
+
- [036] Implement AuthRepository.saveRefreshToken() method
|
|
63
|
+
- [037] Implement failed login attempt tracking
|
|
64
|
+
- [038] Implement account lockout after 5 failed attempts
|
|
65
|
+
- [039] Write failing tests for POST /api/auth/login endpoint
|
|
66
|
+
- [040] Implement AuthController.login() endpoint
|
|
67
|
+
- [041] Add audit logging for login attempts (success and failure)
|
|
68
|
+
- [042] Add rate limiting to login endpoint
|
|
69
|
+
- [043] Run all login tests and verify they pass
|
|
70
|
+
|
|
71
|
+
**Checkpoint**: Users can log in with valid credentials, receive JWT + refresh
|
|
72
|
+
tokens, accounts lock after failures
|
|
73
|
+
|
|
74
|
+
## Phase 5: User Story - P2: Password Reset
|
|
75
|
+
|
|
76
|
+
- [044] [P] Write failing tests for password reset request validation
|
|
77
|
+
- [045] [P] Implement Zod schema for password reset request
|
|
78
|
+
- [046] [P] Write failing tests for AuthRepository.createPasswordReset()
|
|
79
|
+
- [047] [P] Implement AuthRepository.createPasswordReset() method
|
|
80
|
+
- [048] [P] Write failing tests for AuthService.requestPasswordReset()
|
|
81
|
+
- [049] [P] Implement AuthService.requestPasswordReset() with token generation
|
|
82
|
+
- [050] [P] Implement email sending for password reset link
|
|
83
|
+
- [051] [P] Write failing tests for POST /api/auth/password-reset/request
|
|
84
|
+
endpoint
|
|
85
|
+
- [052] [P] Implement AuthController.requestPasswordReset() endpoint
|
|
86
|
+
- [053] [P] Add rate limiting to password reset request endpoint
|
|
87
|
+
- [054] Write failing tests for password reset confirmation validation
|
|
88
|
+
- [055] Implement Zod schema for password reset confirmation
|
|
89
|
+
- [056] Write failing tests for AuthRepository.validateResetToken()
|
|
90
|
+
- [057] Implement AuthRepository.validateResetToken() method
|
|
91
|
+
- [058] Write failing tests for AuthService.confirmPasswordReset()
|
|
92
|
+
- [059] Implement AuthService.confirmPasswordReset() with password update
|
|
93
|
+
- [060] Mark reset token as used after successful reset
|
|
94
|
+
- [061] Write failing tests for POST /api/auth/password-reset/confirm endpoint
|
|
95
|
+
- [062] Implement AuthController.confirmPasswordReset() endpoint
|
|
96
|
+
- [063] Add audit logging for password reset events
|
|
97
|
+
- [064] Run all password reset tests and verify they pass
|
|
98
|
+
|
|
99
|
+
**Checkpoint**: Users can request and complete password reset, expired/invalid
|
|
100
|
+
tokens are rejected
|
|
101
|
+
|
|
102
|
+
## Phase 6: User Story - P3: Token Refresh
|
|
103
|
+
|
|
104
|
+
- [065] [P] Write failing tests for token refresh validation
|
|
105
|
+
- [066] [P] Implement Zod schema for refresh token request
|
|
106
|
+
- [067] [P] Write failing tests for AuthRepository.validateRefreshToken()
|
|
107
|
+
- [068] [P] Implement AuthRepository.validateRefreshToken() method
|
|
108
|
+
- [069] [P] Write failing tests for AuthService.refreshToken()
|
|
109
|
+
- [070] [P] Implement AuthService.refreshToken() with token rotation
|
|
110
|
+
- [071] [P] Implement refresh token expiration check
|
|
111
|
+
- [072] [P] Write failing tests for POST /api/auth/refresh endpoint
|
|
112
|
+
- [073] [P] Implement AuthController.refresh() endpoint
|
|
113
|
+
- [074] [P] Run all token refresh tests and verify they pass
|
|
114
|
+
|
|
115
|
+
**Checkpoint**: Tokens can be refreshed successfully, expired tokens are
|
|
116
|
+
rejected
|
|
117
|
+
|
|
118
|
+
## Phase 7: Polish and Security
|
|
119
|
+
|
|
120
|
+
- [075] Implement JWT verification middleware for protected routes
|
|
121
|
+
- [076] Write tests for JWT middleware
|
|
122
|
+
- [077] Implement logout functionality (token blacklisting)
|
|
123
|
+
- [078] Write tests for logout endpoint
|
|
124
|
+
- [079] Add OpenAPI/Swagger documentation for all endpoints
|
|
125
|
+
- [080] Run security audit (password storage, JWT validation, rate limiting)
|
|
126
|
+
- [081] Run performance tests (concurrent logins, response times)
|
|
127
|
+
- [082] Add integration tests for complete user journeys
|
|
128
|
+
- [083] Review and update error messages for clarity
|
|
129
|
+
- [084] Set up audit log cleanup job (90-day retention)
|
|
130
|
+
- [085] Final code review and cleanup
|
|
131
|
+
|
|
132
|
+
**Checkpoint**: All tests passing, security measures verified, documentation
|
|
133
|
+
complete
|
|
134
|
+
|
|
135
|
+
## Dependencies & Execution Order
|
|
136
|
+
|
|
137
|
+
### Required Sequential Order
|
|
138
|
+
|
|
139
|
+
- Setup (001-005) must complete before Foundational (006-015)
|
|
140
|
+
- Foundational (006-015) must complete before any user story implementation
|
|
141
|
+
- Registration (016-027) should complete before Login (028-043)
|
|
142
|
+
- Login (028-043) should complete before Password Reset and Token Refresh
|
|
143
|
+
|
|
144
|
+
### Parallel Work Opportunities
|
|
145
|
+
|
|
146
|
+
- Password Reset (044-064) can be developed in parallel with Token Refresh
|
|
147
|
+
(065-074) after Login is complete
|
|
148
|
+
- Within each user story, test writing and implementation can be done by
|
|
149
|
+
different team members
|
|
150
|
+
- Documentation (079) can be written in parallel with final polish tasks
|
|
151
|
+
|
|
152
|
+
## Implementation Strategies
|
|
153
|
+
|
|
154
|
+
### Strategy 1: MVP (Minimum Viable Product)
|
|
155
|
+
|
|
156
|
+
Deliver P1 features first (Registration + Login), deploy to get user feedback,
|
|
157
|
+
then add P2/P3 features.
|
|
158
|
+
|
|
159
|
+
### Strategy 2: Incremental by Priority
|
|
160
|
+
|
|
161
|
+
Complete P1 stories → P2 stories → P3 stories in sequence. Deploy after each
|
|
162
|
+
priority level.
|
|
163
|
+
|
|
164
|
+
### Strategy 3: Parallel Development
|
|
165
|
+
|
|
166
|
+
Assign user stories to different developers, integrate at the end. Requires
|
|
167
|
+
strong coordination.
|
|
168
|
+
|
|
169
|
+
**Recommended**: Strategy 2 (Incremental by Priority) for this feature size.
|