@mytechtoday/augment-extensions 0.1.0 → 0.1.2
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 +83 -3
- package/README.md +6 -5
- package/augment-extensions/coding-standards/python/README.md +44 -0
- package/augment-extensions/coding-standards/python/module.json +26 -0
- package/augment-extensions/coding-standards/python/rules/best-practices.md +232 -0
- package/augment-extensions/coding-standards/python/rules/code-organization.md +220 -0
- package/augment-extensions/coding-standards/python/rules/error-handling.md +221 -0
- package/augment-extensions/coding-standards/python/rules/naming-conventions.md +172 -0
- package/augment-extensions/coding-standards/python/rules/type-hints.md +188 -0
- package/augment-extensions/coding-standards/react/README.md +45 -0
- package/augment-extensions/coding-standards/react/module.json +27 -0
- package/augment-extensions/coding-standards/react/rules/component-patterns.md +214 -0
- package/augment-extensions/coding-standards/react/rules/hooks-best-practices.md +235 -0
- package/augment-extensions/coding-standards/react/rules/performance.md +300 -0
- package/augment-extensions/coding-standards/react/rules/state-management.md +265 -0
- package/augment-extensions/coding-standards/react/rules/typescript-react.md +271 -0
- package/augment-extensions/domain-rules/api-design/README.md +41 -0
- package/augment-extensions/domain-rules/api-design/module.json +27 -0
- package/augment-extensions/domain-rules/api-design/rules/authentication.md +263 -0
- package/augment-extensions/domain-rules/api-design/rules/documentation.md +395 -0
- package/augment-extensions/domain-rules/api-design/rules/error-handling.md +290 -0
- package/augment-extensions/domain-rules/api-design/rules/graphql-api.md +313 -0
- package/augment-extensions/domain-rules/api-design/rules/rest-api.md +214 -0
- package/augment-extensions/domain-rules/api-design/rules/versioning.md +268 -0
- package/augment-extensions/domain-rules/security/README.md +41 -0
- package/augment-extensions/domain-rules/security/module.json +28 -0
- package/augment-extensions/domain-rules/security/rules/authentication-security.md +361 -0
- package/augment-extensions/domain-rules/security/rules/encryption.md +208 -0
- package/augment-extensions/domain-rules/security/rules/input-validation.md +294 -0
- package/augment-extensions/domain-rules/security/rules/owasp-top-10.md +339 -0
- package/augment-extensions/domain-rules/security/rules/secure-coding.md +293 -0
- package/augment-extensions/domain-rules/security/rules/web-security.md +268 -0
- package/augment-extensions/examples/design-patterns/README.md +37 -0
- package/augment-extensions/examples/design-patterns/examples/behavioral-patterns.md +370 -0
- package/augment-extensions/examples/design-patterns/examples/creational-patterns.md +250 -0
- package/augment-extensions/examples/design-patterns/examples/structural-patterns.md +264 -0
- package/augment-extensions/examples/design-patterns/module.json +27 -0
- package/{modules → augment-extensions}/workflows/beads/examples/complete-workflow-example.md +5 -5
- package/{modules → augment-extensions}/workflows/beads/rules/file-format.md +45 -1
- package/{modules → augment-extensions}/workflows/beads/rules/workflow.md +41 -0
- package/{modules → augment-extensions}/workflows/openspec/examples/complete-change-example.md +14 -0
- package/{modules → augment-extensions}/workflows/openspec/rules/spec-format.md +44 -1
- package/{modules → augment-extensions}/workflows/openspec/rules/workflow.md +25 -0
- package/cli/dist/cli.js +69 -1
- package/cli/dist/cli.js.map +1 -1
- package/cli/dist/commands/coord.d.ts +30 -0
- package/cli/dist/commands/coord.d.ts.map +1 -0
- package/cli/dist/commands/coord.js +150 -0
- package/cli/dist/commands/coord.js.map +1 -0
- package/cli/dist/commands/link.js +1 -1
- package/cli/dist/commands/link.js.map +1 -1
- package/cli/dist/commands/list.js +1 -1
- package/cli/dist/commands/list.js.map +1 -1
- package/cli/dist/commands/search.d.ts.map +1 -1
- package/cli/dist/commands/search.js +107 -5
- package/cli/dist/commands/search.js.map +1 -1
- package/cli/dist/commands/show.js +1 -1
- package/cli/dist/commands/show.js.map +1 -1
- package/cli/dist/commands/sync.d.ts +26 -0
- package/cli/dist/commands/sync.d.ts.map +1 -0
- package/cli/dist/commands/sync.js +106 -0
- package/cli/dist/commands/sync.js.map +1 -0
- package/cli/dist/commands/update.d.ts.map +1 -1
- package/cli/dist/commands/update.js +132 -7
- package/cli/dist/commands/update.js.map +1 -1
- package/cli/dist/utils/auto-sync.d.ts +34 -0
- package/cli/dist/utils/auto-sync.d.ts.map +1 -0
- package/cli/dist/utils/auto-sync.js +172 -0
- package/cli/dist/utils/auto-sync.js.map +1 -0
- package/cli/dist/utils/beads-sync.d.ts +51 -0
- package/cli/dist/utils/beads-sync.d.ts.map +1 -0
- package/cli/dist/utils/beads-sync.js +171 -0
- package/cli/dist/utils/beads-sync.js.map +1 -0
- package/cli/dist/utils/coordination-queries.d.ts +79 -0
- package/cli/dist/utils/coordination-queries.d.ts.map +1 -0
- package/cli/dist/utils/coordination-queries.js +155 -0
- package/cli/dist/utils/coordination-queries.js.map +1 -0
- package/cli/dist/utils/file-tracking.d.ts +42 -0
- package/cli/dist/utils/file-tracking.d.ts.map +1 -0
- package/cli/dist/utils/file-tracking.js +155 -0
- package/cli/dist/utils/file-tracking.js.map +1 -0
- package/cli/dist/utils/migrate.d.ts +25 -0
- package/cli/dist/utils/migrate.d.ts.map +1 -0
- package/cli/dist/utils/migrate.js +204 -0
- package/cli/dist/utils/migrate.js.map +1 -0
- package/cli/dist/utils/openspec-sync.d.ts +48 -0
- package/cli/dist/utils/openspec-sync.d.ts.map +1 -0
- package/cli/dist/utils/openspec-sync.js +167 -0
- package/cli/dist/utils/openspec-sync.js.map +1 -0
- package/{MODULES.md → modules.md} +1 -1
- package/package.json +9 -7
- /package/{modules → augment-extensions}/coding-standards/typescript/README.md +0 -0
- /package/{modules → augment-extensions}/coding-standards/typescript/module.json +0 -0
- /package/{modules → augment-extensions}/coding-standards/typescript/rules/naming-conventions.md +0 -0
- /package/{modules → augment-extensions}/workflows/beads/README.md +0 -0
- /package/{modules → augment-extensions}/workflows/beads/module.json +0 -0
- /package/{modules → augment-extensions}/workflows/beads/rules/best-practices.md +0 -0
- /package/{modules → augment-extensions}/workflows/beads/rules/manual-setup.md +0 -0
- /package/{modules → augment-extensions}/workflows/openspec/README.md +0 -0
- /package/{modules → augment-extensions}/workflows/openspec/module.json +0 -0
- /package/{modules → augment-extensions}/workflows/openspec/rules/best-practices.md +0 -0
- /package/{modules → augment-extensions}/workflows/openspec/rules/manual-setup.md +0 -0
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
# API Versioning
|
|
2
|
+
|
|
3
|
+
Strategies for versioning APIs to maintain backward compatibility.
|
|
4
|
+
|
|
5
|
+
## Versioning Strategies
|
|
6
|
+
|
|
7
|
+
### URL Versioning (Recommended for REST)
|
|
8
|
+
|
|
9
|
+
Include version in the URL path.
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
# Good - Version in URL
|
|
13
|
+
GET /api/v1/users
|
|
14
|
+
GET /api/v2/users
|
|
15
|
+
|
|
16
|
+
# Advantages:
|
|
17
|
+
- Clear and explicit
|
|
18
|
+
- Easy to route
|
|
19
|
+
- Easy to cache
|
|
20
|
+
- Easy to test different versions
|
|
21
|
+
|
|
22
|
+
# Disadvantages:
|
|
23
|
+
- URL changes between versions
|
|
24
|
+
- Can lead to code duplication
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Header Versioning
|
|
28
|
+
|
|
29
|
+
Use custom header for version.
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
# Request
|
|
33
|
+
GET /api/users
|
|
34
|
+
Accept-Version: v1
|
|
35
|
+
|
|
36
|
+
# Or using Accept header
|
|
37
|
+
GET /api/users
|
|
38
|
+
Accept: application/vnd.myapi.v1+json
|
|
39
|
+
|
|
40
|
+
# Advantages:
|
|
41
|
+
- Clean URLs
|
|
42
|
+
- Follows REST principles
|
|
43
|
+
|
|
44
|
+
# Disadvantages:
|
|
45
|
+
- Less visible
|
|
46
|
+
- Harder to test in browser
|
|
47
|
+
- Caching complexity
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Query Parameter Versioning
|
|
51
|
+
|
|
52
|
+
Include version as query parameter.
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
# Request
|
|
56
|
+
GET /api/users?version=1
|
|
57
|
+
|
|
58
|
+
# Advantages:
|
|
59
|
+
- Simple to implement
|
|
60
|
+
- Easy to test
|
|
61
|
+
|
|
62
|
+
# Disadvantages:
|
|
63
|
+
- Pollutes query parameters
|
|
64
|
+
- Less clean than URL versioning
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Semantic Versioning
|
|
68
|
+
|
|
69
|
+
Use semantic versioning for API versions.
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
# Format: MAJOR.MINOR.PATCH
|
|
73
|
+
|
|
74
|
+
v1.0.0 - Initial release
|
|
75
|
+
v1.1.0 - Added new endpoints (backward compatible)
|
|
76
|
+
v1.1.1 - Bug fixes (backward compatible)
|
|
77
|
+
v2.0.0 - Breaking changes (not backward compatible)
|
|
78
|
+
|
|
79
|
+
# In URLs, typically use only MAJOR version
|
|
80
|
+
/api/v1/users (represents v1.x.x)
|
|
81
|
+
/api/v2/users (represents v2.x.x)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Breaking vs Non-Breaking Changes
|
|
85
|
+
|
|
86
|
+
### Non-Breaking Changes (Safe)
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
✅ Adding new endpoints
|
|
90
|
+
✅ Adding new optional fields to requests
|
|
91
|
+
✅ Adding new fields to responses
|
|
92
|
+
✅ Adding new query parameters (optional)
|
|
93
|
+
✅ Making required fields optional
|
|
94
|
+
✅ Relaxing validation rules
|
|
95
|
+
✅ Adding new enum values (if clients handle unknown values)
|
|
96
|
+
|
|
97
|
+
# Example: Adding optional field
|
|
98
|
+
# v1.0.0
|
|
99
|
+
POST /api/v1/users
|
|
100
|
+
{
|
|
101
|
+
"name": "John",
|
|
102
|
+
"email": "john@example.com"
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
# v1.1.0 (backward compatible)
|
|
106
|
+
POST /api/v1/users
|
|
107
|
+
{
|
|
108
|
+
"name": "John",
|
|
109
|
+
"email": "john@example.com",
|
|
110
|
+
"phone": "555-1234" # New optional field
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Breaking Changes (Require New Version)
|
|
115
|
+
|
|
116
|
+
```
|
|
117
|
+
❌ Removing endpoints
|
|
118
|
+
❌ Removing fields from responses
|
|
119
|
+
❌ Removing query parameters
|
|
120
|
+
❌ Renaming fields
|
|
121
|
+
❌ Changing field types
|
|
122
|
+
❌ Making optional fields required
|
|
123
|
+
❌ Changing URL structure
|
|
124
|
+
❌ Changing authentication method
|
|
125
|
+
❌ Stricter validation rules
|
|
126
|
+
|
|
127
|
+
# Example: Breaking change requires v2
|
|
128
|
+
# v1
|
|
129
|
+
GET /api/v1/users/123
|
|
130
|
+
{
|
|
131
|
+
"id": "123",
|
|
132
|
+
"name": "John Doe"
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
# v2 (breaking: renamed field)
|
|
136
|
+
GET /api/v2/users/123
|
|
137
|
+
{
|
|
138
|
+
"id": "123",
|
|
139
|
+
"fullName": "John Doe" # Renamed from 'name'
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Deprecation Strategy
|
|
144
|
+
|
|
145
|
+
Gracefully deprecate old versions.
|
|
146
|
+
|
|
147
|
+
```
|
|
148
|
+
# 1. Announce deprecation
|
|
149
|
+
GET /api/v1/users
|
|
150
|
+
Response Headers:
|
|
151
|
+
Deprecation: true
|
|
152
|
+
Sunset: Sat, 31 Dec 2024 23:59:59 GMT
|
|
153
|
+
Link: </api/v2/users>; rel="successor-version"
|
|
154
|
+
|
|
155
|
+
# 2. Response body warning
|
|
156
|
+
{
|
|
157
|
+
"data": [...],
|
|
158
|
+
"warnings": [
|
|
159
|
+
{
|
|
160
|
+
"code": "DEPRECATED_VERSION",
|
|
161
|
+
"message": "API v1 is deprecated. Please migrate to v2 by Dec 31, 2024.",
|
|
162
|
+
"link": "https://docs.example.com/migration-guide"
|
|
163
|
+
}
|
|
164
|
+
]
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
# 3. Deprecation timeline
|
|
168
|
+
Month 0: Announce v2, v1 still supported
|
|
169
|
+
Month 3: Add deprecation warnings to v1
|
|
170
|
+
Month 6: v1 in maintenance mode (bug fixes only)
|
|
171
|
+
Month 12: v1 sunset (no longer available)
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Version Migration
|
|
175
|
+
|
|
176
|
+
Provide migration paths.
|
|
177
|
+
|
|
178
|
+
```
|
|
179
|
+
# Good - Support both versions during transition
|
|
180
|
+
# v1 endpoint (deprecated)
|
|
181
|
+
GET /api/v1/users/123
|
|
182
|
+
{
|
|
183
|
+
"id": "123",
|
|
184
|
+
"name": "John Doe",
|
|
185
|
+
"email": "john@example.com"
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
# v2 endpoint (current)
|
|
189
|
+
GET /api/v2/users/123
|
|
190
|
+
{
|
|
191
|
+
"id": "123",
|
|
192
|
+
"firstName": "John",
|
|
193
|
+
"lastName": "Doe",
|
|
194
|
+
"email": "john@example.com",
|
|
195
|
+
"profile": {
|
|
196
|
+
"avatar": "https://..."
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
# Provide migration guide
|
|
201
|
+
# docs/migration/v1-to-v2.md
|
|
202
|
+
- 'name' field split into 'firstName' and 'lastName'
|
|
203
|
+
- Added 'profile' object with nested fields
|
|
204
|
+
- All dates now in ISO 8601 format
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## GraphQL Versioning
|
|
208
|
+
|
|
209
|
+
GraphQL uses schema evolution instead of versioning.
|
|
210
|
+
|
|
211
|
+
```graphql
|
|
212
|
+
# Good - Deprecate fields, don't remove
|
|
213
|
+
type User {
|
|
214
|
+
id: ID!
|
|
215
|
+
name: String! @deprecated(reason: "Use firstName and lastName instead")
|
|
216
|
+
firstName: String!
|
|
217
|
+
lastName: String!
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
# Good - Add new fields
|
|
221
|
+
type User {
|
|
222
|
+
id: ID!
|
|
223
|
+
email: String!
|
|
224
|
+
phone: String # New optional field
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
# Bad - Breaking change (don't do this)
|
|
228
|
+
type User {
|
|
229
|
+
id: ID!
|
|
230
|
+
# Removed 'name' field without deprecation period
|
|
231
|
+
firstName: String!
|
|
232
|
+
lastName: String!
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## Best Practices
|
|
237
|
+
|
|
238
|
+
1. **Use URL versioning** - For REST APIs (most explicit)
|
|
239
|
+
2. **Version only on breaking changes** - Use semantic versioning
|
|
240
|
+
3. **Support multiple versions** - During transition period
|
|
241
|
+
4. **Deprecate gracefully** - Give clients time to migrate
|
|
242
|
+
5. **Document changes** - Maintain changelog
|
|
243
|
+
6. **Provide migration guides** - Help clients upgrade
|
|
244
|
+
7. **Use sunset headers** - Communicate deprecation timeline
|
|
245
|
+
8. **Test all versions** - Ensure backward compatibility
|
|
246
|
+
9. **Monitor usage** - Track version adoption
|
|
247
|
+
10. **Plan deprecation** - Have clear timeline
|
|
248
|
+
11. **Avoid micro-versions** - Don't version every change
|
|
249
|
+
12. **Use feature flags** - For gradual rollouts
|
|
250
|
+
13. **Keep v1 simple** - Don't over-engineer first version
|
|
251
|
+
14. **Version documentation** - Docs for each version
|
|
252
|
+
15. **Automate testing** - Test all supported versions
|
|
253
|
+
|
|
254
|
+
## Version Support Policy
|
|
255
|
+
|
|
256
|
+
```
|
|
257
|
+
# Example policy
|
|
258
|
+
- Current version (v3): Full support
|
|
259
|
+
- Previous version (v2): Maintenance mode (12 months)
|
|
260
|
+
- Older versions (v1): Deprecated (6 months notice before sunset)
|
|
261
|
+
|
|
262
|
+
# Timeline example
|
|
263
|
+
2024-01-01: v3 released, v2 enters maintenance, v1 deprecated
|
|
264
|
+
2024-07-01: v1 sunset (removed)
|
|
265
|
+
2025-01-01: v4 released, v3 enters maintenance, v2 deprecated
|
|
266
|
+
2025-07-01: v2 sunset (removed)
|
|
267
|
+
```
|
|
268
|
+
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Security Guidelines
|
|
2
|
+
|
|
3
|
+
Comprehensive security guidelines for building secure applications.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This module provides detailed security guidelines based on OWASP Top 10 and industry best practices, covering authentication, encryption, input validation, and common vulnerabilities.
|
|
8
|
+
|
|
9
|
+
## Key Benefits
|
|
10
|
+
|
|
11
|
+
- **OWASP Coverage**: Address OWASP Top 10 vulnerabilities
|
|
12
|
+
- **Secure Authentication**: Proper auth implementation
|
|
13
|
+
- **Data Protection**: Encryption and data security
|
|
14
|
+
- **Input Validation**: Prevent injection attacks
|
|
15
|
+
- **Best Practices**: Industry-standard security patterns
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
augx link domain-rules/security
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Contents
|
|
24
|
+
|
|
25
|
+
### Rules
|
|
26
|
+
|
|
27
|
+
- **owasp-top-10.md** - OWASP Top 10 vulnerabilities and mitigations
|
|
28
|
+
- **authentication-security.md** - Secure authentication patterns
|
|
29
|
+
- **encryption.md** - Encryption and data protection
|
|
30
|
+
- **input-validation.md** - Input validation and sanitization
|
|
31
|
+
- **secure-coding.md** - Secure coding practices
|
|
32
|
+
- **web-security.md** - Web-specific security (XSS, CSRF, etc.)
|
|
33
|
+
|
|
34
|
+
## Character Count
|
|
35
|
+
|
|
36
|
+
~38,000 characters
|
|
37
|
+
|
|
38
|
+
## Version
|
|
39
|
+
|
|
40
|
+
1.0.0
|
|
41
|
+
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "security",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"displayName": "Security Guidelines",
|
|
5
|
+
"description": "Comprehensive security guidelines including OWASP best practices, authentication, encryption, and secure coding",
|
|
6
|
+
"type": "domain-rules",
|
|
7
|
+
"author": "Augment Extensions",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"augment": {
|
|
10
|
+
"characterCount": 38000,
|
|
11
|
+
"priority": "high",
|
|
12
|
+
"category": "domain-rules"
|
|
13
|
+
},
|
|
14
|
+
"installation": {
|
|
15
|
+
"required": false,
|
|
16
|
+
"dependencies": []
|
|
17
|
+
},
|
|
18
|
+
"tags": [
|
|
19
|
+
"security",
|
|
20
|
+
"owasp",
|
|
21
|
+
"authentication",
|
|
22
|
+
"encryption",
|
|
23
|
+
"xss",
|
|
24
|
+
"csrf",
|
|
25
|
+
"sql-injection"
|
|
26
|
+
]
|
|
27
|
+
}
|
|
28
|
+
|
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
# Authentication Security
|
|
2
|
+
|
|
3
|
+
Best practices for secure authentication implementation.
|
|
4
|
+
|
|
5
|
+
## Password Security
|
|
6
|
+
|
|
7
|
+
### Password Hashing
|
|
8
|
+
|
|
9
|
+
Always hash passwords with strong algorithms.
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
import bcrypt from 'bcrypt';
|
|
13
|
+
import argon2 from 'argon2';
|
|
14
|
+
|
|
15
|
+
// Good - bcrypt (recommended)
|
|
16
|
+
const saltRounds = 10;
|
|
17
|
+
const hashedPassword = await bcrypt.hash(password, saltRounds);
|
|
18
|
+
|
|
19
|
+
// Good - Argon2 (more secure, slower)
|
|
20
|
+
const hashedPassword = await argon2.hash(password);
|
|
21
|
+
|
|
22
|
+
// Verify password
|
|
23
|
+
const isValid = await bcrypt.compare(inputPassword, hashedPassword);
|
|
24
|
+
const isValid = await argon2.verify(hashedPassword, inputPassword);
|
|
25
|
+
|
|
26
|
+
// Bad - Weak hashing
|
|
27
|
+
const hashedPassword = crypto.createHash('md5').update(password).digest('hex'); // ❌
|
|
28
|
+
const hashedPassword = crypto.createHash('sha1').update(password).digest('hex'); // ❌
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Password Requirements
|
|
32
|
+
|
|
33
|
+
Enforce strong password policies.
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
// Good - Strong password validation
|
|
37
|
+
const validatePassword = (password: string): { valid: boolean; errors: string[] } => {
|
|
38
|
+
const errors: string[] = [];
|
|
39
|
+
|
|
40
|
+
if (password.length < 12) {
|
|
41
|
+
errors.push('Password must be at least 12 characters');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (!/[a-z]/.test(password)) {
|
|
45
|
+
errors.push('Password must contain lowercase letter');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (!/[A-Z]/.test(password)) {
|
|
49
|
+
errors.push('Password must contain uppercase letter');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (!/\d/.test(password)) {
|
|
53
|
+
errors.push('Password must contain number');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (!/[@$!%*?&]/.test(password)) {
|
|
57
|
+
errors.push('Password must contain special character');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Check against common passwords
|
|
61
|
+
if (commonPasswords.includes(password.toLowerCase())) {
|
|
62
|
+
errors.push('Password is too common');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return { valid: errors.length === 0, errors };
|
|
66
|
+
};
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Password Reset
|
|
70
|
+
|
|
71
|
+
Implement secure password reset flow.
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
// Good - Secure password reset
|
|
75
|
+
import crypto from 'crypto';
|
|
76
|
+
|
|
77
|
+
// Generate reset token
|
|
78
|
+
const resetToken = crypto.randomBytes(32).toString('hex');
|
|
79
|
+
const resetTokenHash = crypto.createHash('sha256').update(resetToken).digest('hex');
|
|
80
|
+
|
|
81
|
+
await db.users.update(userId, {
|
|
82
|
+
resetToken: resetTokenHash,
|
|
83
|
+
resetTokenExpiry: Date.now() + 3600000 // 1 hour
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// Send email with reset link
|
|
87
|
+
await sendEmail({
|
|
88
|
+
to: user.email,
|
|
89
|
+
subject: 'Password Reset',
|
|
90
|
+
body: `Reset your password: https://example.com/reset?token=${resetToken}`
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// Verify reset token
|
|
94
|
+
const tokenHash = crypto.createHash('sha256').update(req.query.token).digest('hex');
|
|
95
|
+
const user = await db.users.findOne({
|
|
96
|
+
resetToken: tokenHash,
|
|
97
|
+
resetTokenExpiry: { $gt: Date.now() }
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
if (!user) {
|
|
101
|
+
return res.status(400).json({ error: 'Invalid or expired token' });
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Multi-Factor Authentication (MFA)
|
|
106
|
+
|
|
107
|
+
### TOTP (Time-based One-Time Password)
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
import speakeasy from 'speakeasy';
|
|
111
|
+
import qrcode from 'qrcode';
|
|
112
|
+
|
|
113
|
+
// Generate MFA secret
|
|
114
|
+
const secret = speakeasy.generateSecret({
|
|
115
|
+
name: `MyApp (${user.email})`
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
await db.users.update(userId, {
|
|
119
|
+
mfaSecret: secret.base32,
|
|
120
|
+
mfaEnabled: false // User must verify first
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// Generate QR code for user to scan
|
|
124
|
+
const qrCodeUrl = await qrcode.toDataURL(secret.otpauth_url);
|
|
125
|
+
|
|
126
|
+
// Verify MFA code
|
|
127
|
+
const verified = speakeasy.totp.verify({
|
|
128
|
+
secret: user.mfaSecret,
|
|
129
|
+
encoding: 'base32',
|
|
130
|
+
token: req.body.mfaCode,
|
|
131
|
+
window: 2 // Allow 2 time steps before/after
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
if (verified) {
|
|
135
|
+
await db.users.update(userId, { mfaEnabled: true });
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Backup Codes
|
|
140
|
+
|
|
141
|
+
Provide backup codes for MFA recovery.
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
// Generate backup codes
|
|
145
|
+
const generateBackupCodes = (): string[] => {
|
|
146
|
+
const codes: string[] = [];
|
|
147
|
+
for (let i = 0; i < 10; i++) {
|
|
148
|
+
codes.push(crypto.randomBytes(4).toString('hex'));
|
|
149
|
+
}
|
|
150
|
+
return codes;
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
const backupCodes = generateBackupCodes();
|
|
154
|
+
const hashedCodes = await Promise.all(
|
|
155
|
+
backupCodes.map(code => bcrypt.hash(code, 10))
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
await db.users.update(userId, {
|
|
159
|
+
backupCodes: hashedCodes
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// Verify backup code
|
|
163
|
+
const isValidBackupCode = async (inputCode: string, user: User): Promise<boolean> => {
|
|
164
|
+
for (let i = 0; i < user.backupCodes.length; i++) {
|
|
165
|
+
const isValid = await bcrypt.compare(inputCode, user.backupCodes[i]);
|
|
166
|
+
if (isValid) {
|
|
167
|
+
// Remove used backup code
|
|
168
|
+
user.backupCodes.splice(i, 1);
|
|
169
|
+
await db.users.update(user.id, { backupCodes: user.backupCodes });
|
|
170
|
+
return true;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return false;
|
|
174
|
+
};
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Session Management
|
|
178
|
+
|
|
179
|
+
### Secure Session Configuration
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
import session from 'express-session';
|
|
183
|
+
import RedisStore from 'connect-redis';
|
|
184
|
+
|
|
185
|
+
// Good - Secure session configuration
|
|
186
|
+
app.use(session({
|
|
187
|
+
store: new RedisStore({ client: redisClient }),
|
|
188
|
+
secret: process.env.SESSION_SECRET, // Strong random secret
|
|
189
|
+
resave: false,
|
|
190
|
+
saveUninitialized: false,
|
|
191
|
+
name: 'sessionId', // Don't use default name
|
|
192
|
+
cookie: {
|
|
193
|
+
secure: true, // HTTPS only
|
|
194
|
+
httpOnly: true, // Prevent XSS
|
|
195
|
+
sameSite: 'strict', // Prevent CSRF
|
|
196
|
+
maxAge: 3600000, // 1 hour
|
|
197
|
+
domain: '.example.com'
|
|
198
|
+
}
|
|
199
|
+
}));
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Session Invalidation
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
// Logout - destroy session
|
|
206
|
+
app.post('/logout', (req, res) => {
|
|
207
|
+
req.session.destroy((err) => {
|
|
208
|
+
if (err) {
|
|
209
|
+
return res.status(500).json({ error: 'Logout failed' });
|
|
210
|
+
}
|
|
211
|
+
res.clearCookie('sessionId');
|
|
212
|
+
res.json({ message: 'Logged out successfully' });
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
// Invalidate all sessions on password change
|
|
217
|
+
await db.sessions.deleteMany({ userId: user.id });
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## JWT Security
|
|
221
|
+
|
|
222
|
+
### Secure JWT Implementation
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
import jwt from 'jsonwebtoken';
|
|
226
|
+
|
|
227
|
+
// Generate JWT
|
|
228
|
+
const accessToken = jwt.sign(
|
|
229
|
+
{
|
|
230
|
+
sub: user.id,
|
|
231
|
+
email: user.email,
|
|
232
|
+
role: user.role
|
|
233
|
+
},
|
|
234
|
+
process.env.JWT_SECRET,
|
|
235
|
+
{
|
|
236
|
+
expiresIn: '15m',
|
|
237
|
+
issuer: 'myapp.com',
|
|
238
|
+
audience: 'myapp.com'
|
|
239
|
+
}
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
// Verify JWT
|
|
243
|
+
try {
|
|
244
|
+
const decoded = jwt.verify(token, process.env.JWT_SECRET, {
|
|
245
|
+
issuer: 'myapp.com',
|
|
246
|
+
audience: 'myapp.com'
|
|
247
|
+
});
|
|
248
|
+
} catch (error) {
|
|
249
|
+
if (error.name === 'TokenExpiredError') {
|
|
250
|
+
return res.status(401).json({ error: 'Token expired' });
|
|
251
|
+
}
|
|
252
|
+
return res.status(401).json({ error: 'Invalid token' });
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### Refresh Token Pattern
|
|
257
|
+
|
|
258
|
+
```typescript
|
|
259
|
+
// Generate tokens
|
|
260
|
+
const accessToken = generateAccessToken(user); // 15 minutes
|
|
261
|
+
const refreshToken = generateRefreshToken(user); // 7 days
|
|
262
|
+
|
|
263
|
+
// Store refresh token (hashed)
|
|
264
|
+
const refreshTokenHash = crypto.createHash('sha256').update(refreshToken).digest('hex');
|
|
265
|
+
await db.refreshTokens.create({
|
|
266
|
+
userId: user.id,
|
|
267
|
+
token: refreshTokenHash,
|
|
268
|
+
expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
// Refresh access token
|
|
272
|
+
app.post('/auth/refresh', async (req, res) => {
|
|
273
|
+
const { refreshToken } = req.body;
|
|
274
|
+
|
|
275
|
+
const tokenHash = crypto.createHash('sha256').update(refreshToken).digest('hex');
|
|
276
|
+
const storedToken = await db.refreshTokens.findOne({
|
|
277
|
+
token: tokenHash,
|
|
278
|
+
expiresAt: { $gt: new Date() }
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
if (!storedToken) {
|
|
282
|
+
return res.status(401).json({ error: 'Invalid refresh token' });
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const user = await db.users.findOne(storedToken.userId);
|
|
286
|
+
const newAccessToken = generateAccessToken(user);
|
|
287
|
+
|
|
288
|
+
res.json({ accessToken: newAccessToken });
|
|
289
|
+
});
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
## Brute Force Protection
|
|
293
|
+
|
|
294
|
+
### Rate Limiting
|
|
295
|
+
|
|
296
|
+
```typescript
|
|
297
|
+
import rateLimit from 'express-rate-limit';
|
|
298
|
+
|
|
299
|
+
// Login rate limiting
|
|
300
|
+
const loginLimiter = rateLimit({
|
|
301
|
+
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
302
|
+
max: 5,
|
|
303
|
+
skipSuccessfulRequests: true,
|
|
304
|
+
message: 'Too many login attempts'
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
app.post('/login', loginLimiter, async (req, res) => {
|
|
308
|
+
// Login logic
|
|
309
|
+
});
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### Account Lockout
|
|
313
|
+
|
|
314
|
+
```typescript
|
|
315
|
+
const MAX_ATTEMPTS = 5;
|
|
316
|
+
const LOCKOUT_DURATION = 15 * 60 * 1000; // 15 minutes
|
|
317
|
+
|
|
318
|
+
// Check if account is locked
|
|
319
|
+
if (user.lockoutUntil && user.lockoutUntil > Date.now()) {
|
|
320
|
+
return res.status(429).json({
|
|
321
|
+
error: 'Account locked',
|
|
322
|
+
retryAfter: Math.ceil((user.lockoutUntil - Date.now()) / 1000)
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Failed login
|
|
327
|
+
if (!isValidPassword) {
|
|
328
|
+
const attempts = user.loginAttempts + 1;
|
|
329
|
+
|
|
330
|
+
if (attempts >= MAX_ATTEMPTS) {
|
|
331
|
+
await db.users.update(user.id, {
|
|
332
|
+
loginAttempts: attempts,
|
|
333
|
+
lockoutUntil: Date.now() + LOCKOUT_DURATION
|
|
334
|
+
});
|
|
335
|
+
return res.status(429).json({ error: 'Account locked' });
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
await db.users.update(user.id, { loginAttempts: attempts });
|
|
339
|
+
return res.status(401).json({ error: 'Invalid credentials' });
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Successful login - reset attempts
|
|
343
|
+
await db.users.update(user.id, {
|
|
344
|
+
loginAttempts: 0,
|
|
345
|
+
lockoutUntil: null
|
|
346
|
+
});
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
## Best Practices
|
|
350
|
+
|
|
351
|
+
1. **Hash passwords** - Use bcrypt or Argon2
|
|
352
|
+
2. **Strong passwords** - Enforce complexity requirements
|
|
353
|
+
3. **Secure reset flow** - Time-limited tokens
|
|
354
|
+
4. **Implement MFA** - TOTP or SMS
|
|
355
|
+
5. **Secure sessions** - httpOnly, secure, sameSite cookies
|
|
356
|
+
6. **Short-lived tokens** - 15-minute access tokens
|
|
357
|
+
7. **Refresh tokens** - For obtaining new access tokens
|
|
358
|
+
8. **Rate limiting** - Prevent brute force
|
|
359
|
+
9. **Account lockout** - After failed attempts
|
|
360
|
+
10. **Log auth events** - Monitor suspicious activity
|
|
361
|
+
|