@intentsolutionsio/fullstack-starter-pack 1.0.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/.claude-plugin/plugin.json +31 -0
- package/LICENSE +21 -0
- package/README.md +168 -0
- package/agents/api-builder.md +610 -0
- package/agents/backend-architect.md +574 -0
- package/agents/database-designer.md +509 -0
- package/agents/deployment-specialist.md +603 -0
- package/agents/react-specialist.md +668 -0
- package/agents/ui-ux-expert.md +652 -0
- package/commands/auth-setup.md +422 -0
- package/commands/component-generator.md +343 -0
- package/commands/css-utility-generator.md +621 -0
- package/commands/env-config-setup.md +338 -0
- package/commands/express-api-scaffold.md +659 -0
- package/commands/fastapi-scaffold.md +674 -0
- package/commands/prisma-schema-gen.md +582 -0
- package/commands/project-scaffold.md +355 -0
- package/commands/sql-query-builder.md +461 -0
- package/package.json +52 -0
- package/skills/skill-adapter/assets/README.md +8 -0
- package/skills/skill-adapter/assets/config-template.json +32 -0
- package/skills/skill-adapter/assets/example_env_config.txt +100 -0
- package/skills/skill-adapter/assets/skill-schema.json +28 -0
- package/skills/skill-adapter/assets/test-data.json +27 -0
- package/skills/skill-adapter/references/README.md +4 -0
- package/skills/skill-adapter/references/best-practices.md +69 -0
- package/skills/skill-adapter/references/examples.md +73 -0
- package/skills/skill-adapter/scripts/README.md +7 -0
- package/skills/skill-adapter/scripts/helper-template.sh +42 -0
- package/skills/skill-adapter/scripts/validation.sh +32 -0
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: auth-setup
|
|
3
|
+
description: >
|
|
4
|
+
Generate authentication boilerplate with JWT, OAuth, and session support
|
|
5
|
+
shortcut: as
|
|
6
|
+
category: backend
|
|
7
|
+
difficulty: intermediate
|
|
8
|
+
estimated_time: 5-10 minutes
|
|
9
|
+
---
|
|
10
|
+
# Auth Setup
|
|
11
|
+
|
|
12
|
+
Generates complete authentication boilerplate including JWT, OAuth (Google/GitHub), session management, and password reset flows.
|
|
13
|
+
|
|
14
|
+
## What This Command Does
|
|
15
|
+
|
|
16
|
+
**Generated Auth System:**
|
|
17
|
+
- JWT authentication with refresh tokens
|
|
18
|
+
- OAuth2 (Google, GitHub, Facebook)
|
|
19
|
+
- Password hashing (bcrypt)
|
|
20
|
+
- Email verification
|
|
21
|
+
- Password reset flow
|
|
22
|
+
- Session management
|
|
23
|
+
- Rate limiting on auth endpoints
|
|
24
|
+
- Authentication middleware
|
|
25
|
+
|
|
26
|
+
**Output:** Complete authentication system ready for production
|
|
27
|
+
|
|
28
|
+
**Time:** 5-10 minutes
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Usage
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
# Generate full auth system
|
|
36
|
+
/auth-setup jwt
|
|
37
|
+
|
|
38
|
+
# Shortcut
|
|
39
|
+
/as oauth --providers google,github
|
|
40
|
+
|
|
41
|
+
# With specific features
|
|
42
|
+
/as jwt --features email-verification,password-reset,2fa
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Example Output
|
|
48
|
+
|
|
49
|
+
### **JWT Authentication**
|
|
50
|
+
|
|
51
|
+
**auth.service.ts:**
|
|
52
|
+
```typescript
|
|
53
|
+
import bcrypt from 'bcrypt'
|
|
54
|
+
import jwt from 'jsonwebtoken'
|
|
55
|
+
import { User } from './models/User'
|
|
56
|
+
|
|
57
|
+
export class AuthService {
|
|
58
|
+
async register(email: string, password: string, name: string) {
|
|
59
|
+
// Check if user exists
|
|
60
|
+
const existing = await User.findOne({ email })
|
|
61
|
+
if (existing) {
|
|
62
|
+
throw new Error('Email already registered')
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Hash password
|
|
66
|
+
const hashedPassword = await bcrypt.hash(password, 12)
|
|
67
|
+
|
|
68
|
+
// Create user
|
|
69
|
+
const user = await User.create({
|
|
70
|
+
email,
|
|
71
|
+
password: hashedPassword,
|
|
72
|
+
name,
|
|
73
|
+
emailVerified: false
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
// Generate verification token
|
|
77
|
+
const verificationToken = this.generateToken({ userId: user.id, type: 'verify' }, '24h')
|
|
78
|
+
|
|
79
|
+
// Send verification email (implement sendEmail)
|
|
80
|
+
await this.sendVerificationEmail(email, verificationToken)
|
|
81
|
+
|
|
82
|
+
// Generate auth tokens
|
|
83
|
+
const accessToken = this.generateAccessToken(user)
|
|
84
|
+
const refreshToken = this.generateRefreshToken(user)
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
user: { id: user.id, email: user.email, name: user.name },
|
|
88
|
+
accessToken,
|
|
89
|
+
refreshToken
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async login(email: string, password: string) {
|
|
94
|
+
const user = await User.findOne({ email })
|
|
95
|
+
if (!user) {
|
|
96
|
+
throw new Error('Invalid credentials')
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const validPassword = await bcrypt.compare(password, user.password)
|
|
100
|
+
if (!validPassword) {
|
|
101
|
+
throw new Error('Invalid credentials')
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (!user.emailVerified) {
|
|
105
|
+
throw new Error('Please verify your email')
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const accessToken = this.generateAccessToken(user)
|
|
109
|
+
const refreshToken = this.generateRefreshToken(user)
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
user: { id: user.id, email: user.email, name: user.name },
|
|
113
|
+
accessToken,
|
|
114
|
+
refreshToken
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async refreshToken(refreshToken: string) {
|
|
119
|
+
try {
|
|
120
|
+
const decoded = jwt.verify(refreshToken, process.env.JWT_REFRESH_SECRET!) as any
|
|
121
|
+
|
|
122
|
+
const user = await User.findById(decoded.userId)
|
|
123
|
+
if (!user) {
|
|
124
|
+
throw new Error('User not found')
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const accessToken = this.generateAccessToken(user)
|
|
128
|
+
return { accessToken }
|
|
129
|
+
} catch (error) {
|
|
130
|
+
throw new Error('Invalid refresh token')
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async verifyEmail(token: string) {
|
|
135
|
+
const decoded = jwt.verify(token, process.env.JWT_SECRET!) as any
|
|
136
|
+
|
|
137
|
+
if (decoded.type !== 'verify') {
|
|
138
|
+
throw new Error('Invalid token type')
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
await User.findByIdAndUpdate(decoded.userId, { emailVerified: true })
|
|
142
|
+
return { message: 'Email verified successfully' }
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async requestPasswordReset(email: string) {
|
|
146
|
+
const user = await User.findOne({ email })
|
|
147
|
+
if (!user) {
|
|
148
|
+
// Don't reveal if user exists
|
|
149
|
+
return { message: 'If email exists, reset link sent' }
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const resetToken = this.generateToken({ userId: user.id, type: 'reset' }, '1h')
|
|
153
|
+
await this.sendPasswordResetEmail(email, resetToken)
|
|
154
|
+
|
|
155
|
+
return { message: 'If email exists, reset link sent' }
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async resetPassword(token: string, newPassword: string) {
|
|
159
|
+
const decoded = jwt.verify(token, process.env.JWT_SECRET!) as any
|
|
160
|
+
|
|
161
|
+
if (decoded.type !== 'reset') {
|
|
162
|
+
throw new Error('Invalid token type')
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const hashedPassword = await bcrypt.hash(newPassword, 12)
|
|
166
|
+
await User.findByIdAndUpdate(decoded.userId, { password: hashedPassword })
|
|
167
|
+
|
|
168
|
+
return { message: 'Password reset successfully' }
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
private generateAccessToken(user: any) {
|
|
172
|
+
return jwt.sign(
|
|
173
|
+
{ userId: user.id, email: user.email },
|
|
174
|
+
process.env.JWT_SECRET!,
|
|
175
|
+
{ expiresIn: '15m' }
|
|
176
|
+
)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
private generateRefreshToken(user: any) {
|
|
180
|
+
return jwt.sign(
|
|
181
|
+
{ userId: user.id },
|
|
182
|
+
process.env.JWT_REFRESH_SECRET!,
|
|
183
|
+
{ expiresIn: '7d' }
|
|
184
|
+
)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
private generateToken(payload: any, expiresIn: string) {
|
|
188
|
+
return jwt.sign(payload, process.env.JWT_SECRET!, { expiresIn })
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
private async sendVerificationEmail(email: string, token: string) {
|
|
192
|
+
// Implement with SendGrid, Resend, etc.
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
private async sendPasswordResetEmail(email: string, token: string) {
|
|
196
|
+
// Implement with SendGrid, Resend, etc.
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### **OAuth2 Setup (Google)**
|
|
202
|
+
|
|
203
|
+
**oauth.controller.ts:**
|
|
204
|
+
```typescript
|
|
205
|
+
import { OAuth2Client } from 'google-auth-library'
|
|
206
|
+
|
|
207
|
+
const googleClient = new OAuth2Client(
|
|
208
|
+
process.env.GOOGLE_CLIENT_ID,
|
|
209
|
+
process.env.GOOGLE_CLIENT_SECRET,
|
|
210
|
+
process.env.GOOGLE_REDIRECT_URI
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
export class OAuthController {
|
|
214
|
+
async googleLogin(req: Request, res: Response) {
|
|
215
|
+
const authUrl = googleClient.generateAuthUrl({
|
|
216
|
+
access_type: 'offline',
|
|
217
|
+
scope: ['profile', 'email']
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
res.redirect(authUrl)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
async googleCallback(req: Request, res: Response) {
|
|
224
|
+
const { code } = req.query
|
|
225
|
+
|
|
226
|
+
const { tokens } = await googleClient.getToken(code as string)
|
|
227
|
+
googleClient.setCredentials(tokens)
|
|
228
|
+
|
|
229
|
+
const ticket = await googleClient.verifyIdToken({
|
|
230
|
+
idToken: tokens.id_token!,
|
|
231
|
+
audience: process.env.GOOGLE_CLIENT_ID
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
const payload = ticket.getPayload()
|
|
235
|
+
if (!payload) {
|
|
236
|
+
throw new Error('Invalid token')
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Find or create user
|
|
240
|
+
let user = await User.findOne({ email: payload.email })
|
|
241
|
+
|
|
242
|
+
if (!user) {
|
|
243
|
+
user = await User.create({
|
|
244
|
+
email: payload.email,
|
|
245
|
+
name: payload.name,
|
|
246
|
+
avatar: payload.picture,
|
|
247
|
+
emailVerified: true,
|
|
248
|
+
provider: 'google',
|
|
249
|
+
providerId: payload.sub
|
|
250
|
+
})
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Generate tokens
|
|
254
|
+
const accessToken = generateAccessToken(user)
|
|
255
|
+
const refreshToken = generateRefreshToken(user)
|
|
256
|
+
|
|
257
|
+
// Redirect with tokens
|
|
258
|
+
res.redirect(`/auth/success?token=${accessToken}&refresh=${refreshToken}`)
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### **Authentication Middleware**
|
|
264
|
+
|
|
265
|
+
**auth.middleware.ts:**
|
|
266
|
+
```typescript
|
|
267
|
+
import { Request, Response, NextFunction } from 'express'
|
|
268
|
+
import jwt from 'jsonwebtoken'
|
|
269
|
+
|
|
270
|
+
declare global {
|
|
271
|
+
namespace Express {
|
|
272
|
+
interface Request {
|
|
273
|
+
user?: {
|
|
274
|
+
userId: string
|
|
275
|
+
email: string
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
export async function authenticate(req: Request, res: Response, next: NextFunction) {
|
|
282
|
+
try {
|
|
283
|
+
const authHeader = req.headers.authorization
|
|
284
|
+
|
|
285
|
+
if (!authHeader?.startsWith('Bearer ')) {
|
|
286
|
+
return res.status(401).json({ error: 'No token provided' })
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const token = authHeader.split(' ')[1]
|
|
290
|
+
|
|
291
|
+
const decoded = jwt.verify(token, process.env.JWT_SECRET!) as any
|
|
292
|
+
|
|
293
|
+
req.user = {
|
|
294
|
+
userId: decoded.userId,
|
|
295
|
+
email: decoded.email
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
next()
|
|
299
|
+
} catch (error) {
|
|
300
|
+
if (error instanceof jwt.TokenExpiredError) {
|
|
301
|
+
return res.status(401).json({ error: 'Token expired' })
|
|
302
|
+
}
|
|
303
|
+
return res.status(401).json({ error: 'Invalid token' })
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
export function authorize(...roles: string[]) {
|
|
308
|
+
return async (req: Request, res: Response, next: NextFunction) => {
|
|
309
|
+
if (!req.user) {
|
|
310
|
+
return res.status(401).json({ error: 'Not authenticated' })
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const user = await User.findById(req.user.userId)
|
|
314
|
+
|
|
315
|
+
if (!user || !roles.includes(user.role)) {
|
|
316
|
+
return res.status(403).json({ error: 'Insufficient permissions' })
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
next()
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### **Rate Limiting**
|
|
325
|
+
|
|
326
|
+
```typescript
|
|
327
|
+
import rateLimit from 'express-rate-limit'
|
|
328
|
+
|
|
329
|
+
export const authLimiter = rateLimit({
|
|
330
|
+
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
331
|
+
max: 5, // 5 requests per window
|
|
332
|
+
message: 'Too many login attempts, please try again later',
|
|
333
|
+
standardHeaders: true,
|
|
334
|
+
legacyHeaders: false
|
|
335
|
+
})
|
|
336
|
+
|
|
337
|
+
// Usage
|
|
338
|
+
app.post('/api/auth/login', authLimiter, authController.login)
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
## Environment Variables
|
|
344
|
+
|
|
345
|
+
```bash
|
|
346
|
+
# JWT
|
|
347
|
+
JWT_SECRET=your-super-secret-key-min-32-chars
|
|
348
|
+
JWT_REFRESH_SECRET=your-refresh-secret-key
|
|
349
|
+
JWT_EXPIRES_IN=15m
|
|
350
|
+
JWT_REFRESH_EXPIRES_IN=7d
|
|
351
|
+
|
|
352
|
+
# OAuth - Google
|
|
353
|
+
GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com
|
|
354
|
+
GOOGLE_CLIENT_SECRET=your-client-secret
|
|
355
|
+
GOOGLE_REDIRECT_URI=http://localhost:3000/api/auth/google/callback
|
|
356
|
+
|
|
357
|
+
# OAuth - GitHub
|
|
358
|
+
GITHUB_CLIENT_ID=your-github-client-id
|
|
359
|
+
GITHUB_CLIENT_SECRET=your-github-client-secret
|
|
360
|
+
GITHUB_REDIRECT_URI=http://localhost:3000/api/auth/github/callback
|
|
361
|
+
|
|
362
|
+
# Email
|
|
363
|
+
SMTP_HOST=smtp.sendgrid.net
|
|
364
|
+
SMTP_PORT=587
|
|
365
|
+
SMTP_USER=apikey
|
|
366
|
+
SMTP_PASSWORD=your-sendgrid-api-key
|
|
367
|
+
FROM_EMAIL=[email protected]
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
---
|
|
371
|
+
|
|
372
|
+
## API Routes
|
|
373
|
+
|
|
374
|
+
```typescript
|
|
375
|
+
// routes/auth.routes.ts
|
|
376
|
+
import { Router } from 'express'
|
|
377
|
+
import { AuthController } from '../controllers/auth.controller'
|
|
378
|
+
import { authenticate } from '../middleware/auth.middleware'
|
|
379
|
+
import { authLimiter } from '../middleware/rate-limit'
|
|
380
|
+
|
|
381
|
+
const router = Router()
|
|
382
|
+
const authController = new AuthController()
|
|
383
|
+
|
|
384
|
+
// Registration & Login
|
|
385
|
+
router.post('/register', authController.register)
|
|
386
|
+
router.post('/login', authLimiter, authController.login)
|
|
387
|
+
router.post('/refresh', authController.refreshToken)
|
|
388
|
+
router.post('/logout', authenticate, authController.logout)
|
|
389
|
+
|
|
390
|
+
// Email Verification
|
|
391
|
+
router.post('/verify-email', authController.verifyEmail)
|
|
392
|
+
router.post('/resend-verification', authController.resendVerification)
|
|
393
|
+
|
|
394
|
+
// Password Reset
|
|
395
|
+
router.post('/forgot-password', authLimiter, authController.forgotPassword)
|
|
396
|
+
router.post('/reset-password', authController.resetPassword)
|
|
397
|
+
|
|
398
|
+
// OAuth
|
|
399
|
+
router.get('/google', authController.googleLogin)
|
|
400
|
+
router.get('/google/callback', authController.googleCallback)
|
|
401
|
+
router.get('/github', authController.githubLogin)
|
|
402
|
+
router.get('/github/callback', authController.githubCallback)
|
|
403
|
+
|
|
404
|
+
// Profile
|
|
405
|
+
router.get('/me', authenticate, authController.getProfile)
|
|
406
|
+
router.patch('/me', authenticate, authController.updateProfile)
|
|
407
|
+
router.post('/change-password', authenticate, authController.changePassword)
|
|
408
|
+
|
|
409
|
+
export default router
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
---
|
|
413
|
+
|
|
414
|
+
## Related Commands
|
|
415
|
+
|
|
416
|
+
- `/env-config-setup` - Generate environment config
|
|
417
|
+
- `/express-api-scaffold` - Generate Express API
|
|
418
|
+
- `/fastapi-scaffold` - Generate FastAPI
|
|
419
|
+
|
|
420
|
+
---
|
|
421
|
+
|
|
422
|
+
**Secure authentication. Easy integration. Production-ready.**
|