@girardmedia/bootspring 1.2.0 → 2.0.3
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/README.md +107 -14
- package/bin/bootspring.js +166 -27
- package/cli/agent.js +189 -17
- package/cli/analyze.js +499 -0
- package/cli/audit.js +557 -0
- package/cli/auth.js +495 -38
- package/cli/billing.js +302 -0
- package/cli/build.js +695 -0
- package/cli/business.js +109 -26
- package/cli/checkpoint-utils.js +168 -0
- package/cli/checkpoint.js +639 -0
- package/cli/cloud-sync.js +447 -0
- package/cli/content.js +198 -0
- package/cli/context.js +1 -1
- package/cli/deploy.js +543 -0
- package/cli/fundraise.js +112 -50
- package/cli/github-cmd.js +435 -0
- package/cli/health.js +477 -0
- package/cli/init.js +84 -13
- package/cli/legal.js +107 -95
- package/cli/log.js +2 -2
- package/cli/loop.js +976 -73
- package/cli/manager.js +711 -0
- package/cli/metrics.js +480 -0
- package/cli/monitor.js +812 -0
- package/cli/onboard.js +521 -0
- package/cli/orchestrator.js +12 -24
- package/cli/prd.js +594 -0
- package/cli/preseed-start.js +1483 -0
- package/cli/preseed.js +2302 -0
- package/cli/project.js +436 -0
- package/cli/quality.js +233 -0
- package/cli/security.js +913 -0
- package/cli/seed.js +1441 -5
- package/cli/skill.js +273 -211
- package/cli/suggest.js +989 -0
- package/cli/switch.js +453 -0
- package/cli/visualize.js +527 -0
- package/cli/watch.js +769 -0
- package/cli/workspace.js +607 -0
- package/core/analyze-workflow.js +1134 -0
- package/core/api-client.js +535 -22
- package/core/audit-workflow.js +1350 -0
- package/core/build-orchestrator.js +480 -0
- package/core/build-state.js +577 -0
- package/core/checkpoint-engine.js +408 -0
- package/core/config.js +1109 -26
- package/core/context-loader.js +21 -1
- package/core/deploy-workflow.js +836 -0
- package/core/entitlements.js +93 -22
- package/core/github-sync.js +610 -0
- package/core/index.js +8 -1
- package/core/ingest.js +1111 -0
- package/core/metrics-engine.js +768 -0
- package/core/onboard-workflow.js +1007 -0
- package/core/preseed-workflow.js +934 -0
- package/core/preseed.js +1617 -0
- package/core/project-context.js +325 -0
- package/core/project-state.js +694 -0
- package/core/r2-sync.js +583 -0
- package/core/scaffold.js +525 -7
- package/core/session.js +258 -0
- package/core/task-extractor.js +758 -0
- package/core/telemetry.js +28 -6
- package/core/tier-enforcement.js +737 -0
- package/core/utils.js +38 -14
- package/generators/questionnaire.js +15 -12
- package/generators/sections/ai.js +7 -7
- package/generators/sections/content.js +300 -0
- package/generators/sections/index.js +3 -0
- package/generators/sections/plugins.js +7 -6
- package/generators/templates/build-planning.template.js +596 -0
- package/generators/templates/content.template.js +819 -0
- package/generators/templates/index.js +2 -1
- package/hooks/git-autopilot.js +1250 -0
- package/hooks/index.js +9 -0
- package/intelligence/agent-collab.js +2057 -0
- package/intelligence/auto-suggest.js +634 -0
- package/intelligence/content-gen.js +1589 -0
- package/intelligence/cross-project.js +1647 -0
- package/intelligence/index.js +184 -0
- package/intelligence/learning/insights.json +517 -7
- package/intelligence/learning/pattern-learner.js +1008 -14
- package/intelligence/memory/decision-tracker.js +1431 -31
- package/intelligence/memory/decisions.jsonl +0 -0
- package/intelligence/orchestrator.js +2896 -1
- package/intelligence/prd.js +92 -1
- package/intelligence/recommendation-weights.json +14 -2
- package/intelligence/recommendations.js +463 -9
- package/intelligence/workflow-composer.js +1451 -0
- package/marketplace/index.d.ts +324 -0
- package/marketplace/index.js +1921 -0
- package/mcp/contracts/mcp-contract.v1.json +342 -4
- package/mcp/registry.js +680 -3
- package/mcp/response-formatter.js +23 -0
- package/mcp/tools/assist-tool.js +78 -4
- package/mcp/tools/autopilot-tool.js +408 -0
- package/mcp/tools/content-tool.js +571 -0
- package/mcp/tools/dashboard-tool.js +251 -5
- package/mcp/tools/mvp-tool.js +344 -0
- package/mcp/tools/plugin-tool.js +23 -1
- package/mcp/tools/prd-tool.js +579 -0
- package/mcp/tools/seed-tool.js +447 -0
- package/mcp/tools/skill-tool.js +43 -14
- package/mcp/tools/suggest-tool.js +147 -0
- package/package.json +15 -6
- package/agents/README.md +0 -93
- package/agents/ai-integration-expert/context.md +0 -386
- package/agents/api-expert/context.md +0 -416
- package/agents/architecture-expert/context.md +0 -454
- package/agents/auth-expert/context.md +0 -399
- package/agents/backend-expert/context.md +0 -483
- package/agents/business-strategy-expert/context.md +0 -180
- package/agents/code-review-expert/context.md +0 -365
- package/agents/competitive-analysis-expert/context.md +0 -239
- package/agents/data-modeling-expert/context.md +0 -352
- package/agents/database-expert/context.md +0 -250
- package/agents/devops-expert/context.md +0 -446
- package/agents/email-expert/context.md +0 -379
- package/agents/financial-expert/context.md +0 -213
- package/agents/frontend-expert/context.md +0 -364
- package/agents/fundraising-expert/context.md +0 -257
- package/agents/growth-expert/context.md +0 -249
- package/agents/index.js +0 -140
- package/agents/investor-relations-expert/context.md +0 -266
- package/agents/legal-expert/context.md +0 -284
- package/agents/marketing-expert/context.md +0 -236
- package/agents/monitoring-expert/context.md +0 -362
- package/agents/operations-expert/context.md +0 -279
- package/agents/partnerships-expert/context.md +0 -286
- package/agents/payment-expert/context.md +0 -340
- package/agents/performance-expert/context.md +0 -377
- package/agents/private-equity-expert/context.md +0 -246
- package/agents/railway-expert/context.md +0 -284
- package/agents/research-expert/context.md +0 -245
- package/agents/sales-expert/context.md +0 -241
- package/agents/security-expert/context.md +0 -343
- package/agents/testing-expert/context.md +0 -414
- package/agents/ui-ux-expert/context.md +0 -448
- package/agents/vercel-expert/context.md +0 -426
- package/skills/index.js +0 -787
- package/skills/patterns/README.md +0 -163
- package/skills/patterns/ai/agents.md +0 -281
- package/skills/patterns/ai/claude.md +0 -138
- package/skills/patterns/ai/embeddings.md +0 -150
- package/skills/patterns/ai/rag.md +0 -266
- package/skills/patterns/ai/streaming.md +0 -170
- package/skills/patterns/ai/structured-output.md +0 -162
- package/skills/patterns/ai/tools.md +0 -154
- package/skills/patterns/analytics/tracking.md +0 -220
- package/skills/patterns/api/errors.md +0 -296
- package/skills/patterns/api/graphql.md +0 -440
- package/skills/patterns/api/middleware.md +0 -279
- package/skills/patterns/api/openapi.md +0 -285
- package/skills/patterns/api/rate-limiting.md +0 -231
- package/skills/patterns/api/route-handler.md +0 -217
- package/skills/patterns/api/server-action.md +0 -249
- package/skills/patterns/api/versioning.md +0 -443
- package/skills/patterns/api/webhooks.md +0 -247
- package/skills/patterns/auth/clerk.md +0 -132
- package/skills/patterns/auth/mfa.md +0 -313
- package/skills/patterns/auth/nextauth.md +0 -140
- package/skills/patterns/auth/oauth.md +0 -237
- package/skills/patterns/auth/rbac.md +0 -152
- package/skills/patterns/auth/session-management.md +0 -367
- package/skills/patterns/auth/session.md +0 -120
- package/skills/patterns/database/audit.md +0 -177
- package/skills/patterns/database/migrations.md +0 -177
- package/skills/patterns/database/pagination.md +0 -230
- package/skills/patterns/database/pooling.md +0 -357
- package/skills/patterns/database/prisma.md +0 -180
- package/skills/patterns/database/relations.md +0 -187
- package/skills/patterns/database/seeding.md +0 -246
- package/skills/patterns/database/soft-delete.md +0 -153
- package/skills/patterns/database/transactions.md +0 -162
- package/skills/patterns/deployment/ci-cd.md +0 -231
- package/skills/patterns/deployment/docker.md +0 -188
- package/skills/patterns/deployment/monitoring.md +0 -387
- package/skills/patterns/deployment/vercel.md +0 -160
- package/skills/patterns/email/resend.md +0 -143
- package/skills/patterns/email/templates.md +0 -245
- package/skills/patterns/email/transactional.md +0 -503
- package/skills/patterns/email/verification.md +0 -176
- package/skills/patterns/files/download.md +0 -243
- package/skills/patterns/files/upload.md +0 -239
- package/skills/patterns/i18n/nextintl.md +0 -188
- package/skills/patterns/logging/structured.md +0 -292
- package/skills/patterns/notifications/email-queue.md +0 -248
- package/skills/patterns/notifications/push.md +0 -279
- package/skills/patterns/payments/checkout.md +0 -303
- package/skills/patterns/payments/invoices.md +0 -287
- package/skills/patterns/payments/portal.md +0 -245
- package/skills/patterns/payments/stripe.md +0 -272
- package/skills/patterns/payments/subscriptions.md +0 -300
- package/skills/patterns/payments/usage.md +0 -279
- package/skills/patterns/performance/caching.md +0 -276
- package/skills/patterns/performance/code-splitting.md +0 -233
- package/skills/patterns/performance/edge.md +0 -254
- package/skills/patterns/performance/isr.md +0 -266
- package/skills/patterns/performance/lazy-loading.md +0 -281
- package/skills/patterns/realtime/sse.md +0 -327
- package/skills/patterns/realtime/websockets.md +0 -336
- package/skills/patterns/search/filtering.md +0 -329
- package/skills/patterns/search/fulltext.md +0 -260
- package/skills/patterns/security/audit-logging.md +0 -444
- package/skills/patterns/security/csrf.md +0 -234
- package/skills/patterns/security/headers.md +0 -252
- package/skills/patterns/security/sanitization.md +0 -258
- package/skills/patterns/security/secrets.md +0 -261
- package/skills/patterns/security/validation.md +0 -268
- package/skills/patterns/security/xss.md +0 -229
- package/skills/patterns/seo/metadata.md +0 -252
- package/skills/patterns/state/context.md +0 -349
- package/skills/patterns/state/react-query.md +0 -313
- package/skills/patterns/state/url-state.md +0 -482
- package/skills/patterns/state/zustand.md +0 -262
- package/skills/patterns/testing/api.md +0 -259
- package/skills/patterns/testing/component.md +0 -233
- package/skills/patterns/testing/coverage.md +0 -207
- package/skills/patterns/testing/fixtures.md +0 -225
- package/skills/patterns/testing/integration.md +0 -436
- package/skills/patterns/testing/mocking.md +0 -177
- package/skills/patterns/testing/playwright.md +0 -162
- package/skills/patterns/testing/snapshot.md +0 -175
- package/skills/patterns/testing/vitest.md +0 -307
- package/skills/patterns/ui/accordions.md +0 -395
- package/skills/patterns/ui/cards.md +0 -299
- package/skills/patterns/ui/dropdowns.md +0 -476
- package/skills/patterns/ui/empty-states.md +0 -320
- package/skills/patterns/ui/forms.md +0 -405
- package/skills/patterns/ui/inputs.md +0 -319
- package/skills/patterns/ui/layouts.md +0 -282
- package/skills/patterns/ui/loading.md +0 -291
- package/skills/patterns/ui/modals.md +0 -338
- package/skills/patterns/ui/navigation.md +0 -374
- package/skills/patterns/ui/tables.md +0 -407
- package/skills/patterns/ui/toasts.md +0 -300
- package/skills/patterns/ui/tooltips.md +0 -396
- package/skills/patterns/utils/dates.md +0 -435
- package/skills/patterns/utils/errors.md +0 -451
- package/skills/patterns/utils/formatting.md +0 -345
- package/skills/patterns/utils/validation.md +0 -434
- package/templates/bootspring.config.js +0 -83
- package/templates/business/business-model-canvas.md +0 -246
- package/templates/business/business-plan.md +0 -266
- package/templates/business/competitive-analysis.md +0 -312
- package/templates/fundraising/data-room-checklist.md +0 -300
- package/templates/fundraising/investor-research.md +0 -243
- package/templates/fundraising/pitch-deck-outline.md +0 -253
- package/templates/legal/gdpr-checklist.md +0 -339
- package/templates/legal/privacy-policy.md +0 -285
- package/templates/legal/terms-of-service.md +0 -222
- package/templates/mcp.json +0 -9
|
@@ -1,399 +0,0 @@
|
|
|
1
|
-
# Auth Expert Agent
|
|
2
|
-
|
|
3
|
-
## Role
|
|
4
|
-
Specialized in authentication patterns, session management, OAuth, protected routes, and security best practices for web applications.
|
|
5
|
-
|
|
6
|
-
## Core Expertise
|
|
7
|
-
|
|
8
|
-
### Clerk Authentication
|
|
9
|
-
|
|
10
|
-
```typescript
|
|
11
|
-
// middleware.ts
|
|
12
|
-
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server';
|
|
13
|
-
|
|
14
|
-
const isPublicRoute = createRouteMatcher([
|
|
15
|
-
'/',
|
|
16
|
-
'/sign-in(.*)',
|
|
17
|
-
'/sign-up(.*)',
|
|
18
|
-
'/api/webhooks(.*)',
|
|
19
|
-
]);
|
|
20
|
-
|
|
21
|
-
export default clerkMiddleware((auth, req) => {
|
|
22
|
-
if (!isPublicRoute(req)) {
|
|
23
|
-
auth().protect();
|
|
24
|
-
}
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
export const config = {
|
|
28
|
-
matcher: ['/((?!.*\\..*|_next).*)', '/', '/(api|trpc)(.*)'],
|
|
29
|
-
};
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
```tsx
|
|
33
|
-
// app/layout.tsx
|
|
34
|
-
import { ClerkProvider } from '@clerk/nextjs';
|
|
35
|
-
|
|
36
|
-
export default function RootLayout({
|
|
37
|
-
children,
|
|
38
|
-
}: {
|
|
39
|
-
children: React.ReactNode;
|
|
40
|
-
}) {
|
|
41
|
-
return (
|
|
42
|
-
<ClerkProvider>
|
|
43
|
-
<html lang="en">
|
|
44
|
-
<body>{children}</body>
|
|
45
|
-
</html>
|
|
46
|
-
</ClerkProvider>
|
|
47
|
-
);
|
|
48
|
-
}
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
```tsx
|
|
52
|
-
// components/auth-buttons.tsx
|
|
53
|
-
import { SignInButton, SignUpButton, UserButton, SignedIn, SignedOut } from '@clerk/nextjs';
|
|
54
|
-
|
|
55
|
-
export function AuthButtons() {
|
|
56
|
-
return (
|
|
57
|
-
<>
|
|
58
|
-
<SignedOut>
|
|
59
|
-
<SignInButton mode="modal">
|
|
60
|
-
<button>Sign In</button>
|
|
61
|
-
</SignInButton>
|
|
62
|
-
<SignUpButton mode="modal">
|
|
63
|
-
<button>Sign Up</button>
|
|
64
|
-
</SignUpButton>
|
|
65
|
-
</SignedOut>
|
|
66
|
-
<SignedIn>
|
|
67
|
-
<UserButton afterSignOutUrl="/" />
|
|
68
|
-
</SignedIn>
|
|
69
|
-
</>
|
|
70
|
-
);
|
|
71
|
-
}
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
### NextAuth.js (Auth.js)
|
|
75
|
-
|
|
76
|
-
```typescript
|
|
77
|
-
// auth.ts
|
|
78
|
-
import NextAuth from 'next-auth';
|
|
79
|
-
import GitHub from 'next-auth/providers/github';
|
|
80
|
-
import Google from 'next-auth/providers/google';
|
|
81
|
-
import Credentials from 'next-auth/providers/credentials';
|
|
82
|
-
import { PrismaAdapter } from '@auth/prisma-adapter';
|
|
83
|
-
import { prisma } from '@/lib/db';
|
|
84
|
-
import bcrypt from 'bcryptjs';
|
|
85
|
-
|
|
86
|
-
export const { handlers, auth, signIn, signOut } = NextAuth({
|
|
87
|
-
adapter: PrismaAdapter(prisma),
|
|
88
|
-
session: { strategy: 'jwt' },
|
|
89
|
-
providers: [
|
|
90
|
-
GitHub({
|
|
91
|
-
clientId: process.env.GITHUB_ID,
|
|
92
|
-
clientSecret: process.env.GITHUB_SECRET,
|
|
93
|
-
}),
|
|
94
|
-
Google({
|
|
95
|
-
clientId: process.env.GOOGLE_ID,
|
|
96
|
-
clientSecret: process.env.GOOGLE_SECRET,
|
|
97
|
-
}),
|
|
98
|
-
Credentials({
|
|
99
|
-
credentials: {
|
|
100
|
-
email: { type: 'email' },
|
|
101
|
-
password: { type: 'password' },
|
|
102
|
-
},
|
|
103
|
-
async authorize(credentials) {
|
|
104
|
-
const user = await prisma.user.findUnique({
|
|
105
|
-
where: { email: credentials.email as string },
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
if (!user?.password) return null;
|
|
109
|
-
|
|
110
|
-
const valid = await bcrypt.compare(
|
|
111
|
-
credentials.password as string,
|
|
112
|
-
user.password
|
|
113
|
-
);
|
|
114
|
-
|
|
115
|
-
if (!valid) return null;
|
|
116
|
-
|
|
117
|
-
return { id: user.id, email: user.email, name: user.name };
|
|
118
|
-
},
|
|
119
|
-
}),
|
|
120
|
-
],
|
|
121
|
-
callbacks: {
|
|
122
|
-
async jwt({ token, user }) {
|
|
123
|
-
if (user) {
|
|
124
|
-
token.id = user.id;
|
|
125
|
-
}
|
|
126
|
-
return token;
|
|
127
|
-
},
|
|
128
|
-
async session({ session, token }) {
|
|
129
|
-
if (session.user) {
|
|
130
|
-
session.user.id = token.id as string;
|
|
131
|
-
}
|
|
132
|
-
return session;
|
|
133
|
-
},
|
|
134
|
-
},
|
|
135
|
-
pages: {
|
|
136
|
-
signIn: '/login',
|
|
137
|
-
error: '/login',
|
|
138
|
-
},
|
|
139
|
-
});
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
```typescript
|
|
143
|
-
// app/api/auth/[...nextauth]/route.ts
|
|
144
|
-
import { handlers } from '@/auth';
|
|
145
|
-
|
|
146
|
-
export const { GET, POST } = handlers;
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
### Protected API Routes
|
|
150
|
-
|
|
151
|
-
```typescript
|
|
152
|
-
// lib/auth.ts (helper)
|
|
153
|
-
import { auth } from '@/auth';
|
|
154
|
-
import { NextResponse } from 'next/server';
|
|
155
|
-
|
|
156
|
-
export async function getSession() {
|
|
157
|
-
return auth();
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
export async function requireAuth() {
|
|
161
|
-
const session = await auth();
|
|
162
|
-
|
|
163
|
-
if (!session?.user) {
|
|
164
|
-
throw new Error('Unauthorized');
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
return session;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// Usage in API route
|
|
171
|
-
export async function GET() {
|
|
172
|
-
try {
|
|
173
|
-
const session = await requireAuth();
|
|
174
|
-
const userId = session.user.id;
|
|
175
|
-
|
|
176
|
-
// Your logic here
|
|
177
|
-
return Response.json({ userId });
|
|
178
|
-
} catch {
|
|
179
|
-
return Response.json({ error: 'Unauthorized' }, { status: 401 });
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
### Role-Based Access Control (RBAC)
|
|
185
|
-
|
|
186
|
-
```typescript
|
|
187
|
-
// lib/rbac.ts
|
|
188
|
-
import { auth } from '@/auth';
|
|
189
|
-
import { prisma } from '@/lib/db';
|
|
190
|
-
|
|
191
|
-
export type Role = 'USER' | 'ADMIN' | 'SUPER_ADMIN';
|
|
192
|
-
|
|
193
|
-
export async function getUserRole(userId: string): Promise<Role> {
|
|
194
|
-
const user = await prisma.user.findUnique({
|
|
195
|
-
where: { id: userId },
|
|
196
|
-
select: { role: true },
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
return (user?.role as Role) ?? 'USER';
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
export async function requireRole(allowedRoles: Role[]) {
|
|
203
|
-
const session = await auth();
|
|
204
|
-
|
|
205
|
-
if (!session?.user) {
|
|
206
|
-
throw new Error('Unauthorized');
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
const role = await getUserRole(session.user.id);
|
|
210
|
-
|
|
211
|
-
if (!allowedRoles.includes(role)) {
|
|
212
|
-
throw new Error('Forbidden');
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
return { session, role };
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
// Usage
|
|
219
|
-
export async function DELETE(req: Request, { params }: { params: { id: string } }) {
|
|
220
|
-
try {
|
|
221
|
-
await requireRole(['ADMIN', 'SUPER_ADMIN']);
|
|
222
|
-
|
|
223
|
-
// Admin-only logic
|
|
224
|
-
await prisma.user.delete({ where: { id: params.id } });
|
|
225
|
-
|
|
226
|
-
return Response.json({ success: true });
|
|
227
|
-
} catch (error) {
|
|
228
|
-
if (error.message === 'Forbidden') {
|
|
229
|
-
return Response.json({ error: 'Forbidden' }, { status: 403 });
|
|
230
|
-
}
|
|
231
|
-
return Response.json({ error: 'Unauthorized' }, { status: 401 });
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
```
|
|
235
|
-
|
|
236
|
-
### Session Management
|
|
237
|
-
|
|
238
|
-
```typescript
|
|
239
|
-
// lib/session.ts
|
|
240
|
-
import { cookies } from 'next/headers';
|
|
241
|
-
import { SignJWT, jwtVerify } from 'jose';
|
|
242
|
-
|
|
243
|
-
const secret = new TextEncoder().encode(process.env.JWT_SECRET);
|
|
244
|
-
|
|
245
|
-
export async function createSession(userId: string) {
|
|
246
|
-
const token = await new SignJWT({ userId })
|
|
247
|
-
.setProtectedHeader({ alg: 'HS256' })
|
|
248
|
-
.setIssuedAt()
|
|
249
|
-
.setExpirationTime('7d')
|
|
250
|
-
.sign(secret);
|
|
251
|
-
|
|
252
|
-
cookies().set('session', token, {
|
|
253
|
-
httpOnly: true,
|
|
254
|
-
secure: process.env.NODE_ENV === 'production',
|
|
255
|
-
sameSite: 'lax',
|
|
256
|
-
maxAge: 60 * 60 * 24 * 7, // 7 days
|
|
257
|
-
path: '/',
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
return token;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
export async function getSessionData() {
|
|
264
|
-
const token = cookies().get('session')?.value;
|
|
265
|
-
|
|
266
|
-
if (!token) return null;
|
|
267
|
-
|
|
268
|
-
try {
|
|
269
|
-
const { payload } = await jwtVerify(token, secret);
|
|
270
|
-
return payload as { userId: string };
|
|
271
|
-
} catch {
|
|
272
|
-
return null;
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
export async function destroySession() {
|
|
277
|
-
cookies().delete('session');
|
|
278
|
-
}
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
### OAuth Flow
|
|
282
|
-
|
|
283
|
-
```typescript
|
|
284
|
-
// app/api/auth/oauth/[provider]/route.ts
|
|
285
|
-
import { generateState, generateCodeVerifier } from 'oslo/oauth2';
|
|
286
|
-
import { cookies } from 'next/headers';
|
|
287
|
-
|
|
288
|
-
const providers = {
|
|
289
|
-
github: {
|
|
290
|
-
authUrl: 'https://github.com/login/oauth/authorize',
|
|
291
|
-
clientId: process.env.GITHUB_ID!,
|
|
292
|
-
scopes: ['read:user', 'user:email'],
|
|
293
|
-
},
|
|
294
|
-
google: {
|
|
295
|
-
authUrl: 'https://accounts.google.com/o/oauth2/v2/auth',
|
|
296
|
-
clientId: process.env.GOOGLE_ID!,
|
|
297
|
-
scopes: ['openid', 'email', 'profile'],
|
|
298
|
-
},
|
|
299
|
-
};
|
|
300
|
-
|
|
301
|
-
export async function GET(
|
|
302
|
-
req: Request,
|
|
303
|
-
{ params }: { params: { provider: string } }
|
|
304
|
-
) {
|
|
305
|
-
const provider = providers[params.provider];
|
|
306
|
-
if (!provider) {
|
|
307
|
-
return Response.json({ error: 'Invalid provider' }, { status: 400 });
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
const state = generateState();
|
|
311
|
-
const codeVerifier = generateCodeVerifier();
|
|
312
|
-
|
|
313
|
-
// Store state and verifier in cookies
|
|
314
|
-
cookies().set('oauth_state', state, { httpOnly: true, maxAge: 600 });
|
|
315
|
-
cookies().set('oauth_verifier', codeVerifier, { httpOnly: true, maxAge: 600 });
|
|
316
|
-
|
|
317
|
-
const url = new URL(provider.authUrl);
|
|
318
|
-
url.searchParams.set('client_id', provider.clientId);
|
|
319
|
-
url.searchParams.set('redirect_uri', `${process.env.NEXT_PUBLIC_APP_URL}/api/auth/callback/${params.provider}`);
|
|
320
|
-
url.searchParams.set('response_type', 'code');
|
|
321
|
-
url.searchParams.set('scope', provider.scopes.join(' '));
|
|
322
|
-
url.searchParams.set('state', state);
|
|
323
|
-
|
|
324
|
-
return Response.redirect(url.toString());
|
|
325
|
-
}
|
|
326
|
-
```
|
|
327
|
-
|
|
328
|
-
### Webhook for User Sync
|
|
329
|
-
|
|
330
|
-
```typescript
|
|
331
|
-
// app/api/webhooks/clerk/route.ts
|
|
332
|
-
import { Webhook } from 'svix';
|
|
333
|
-
import { headers } from 'next/headers';
|
|
334
|
-
import { WebhookEvent } from '@clerk/nextjs/server';
|
|
335
|
-
import { prisma } from '@/lib/db';
|
|
336
|
-
|
|
337
|
-
export async function POST(req: Request) {
|
|
338
|
-
const body = await req.text();
|
|
339
|
-
const headerPayload = headers();
|
|
340
|
-
|
|
341
|
-
const svix = new Webhook(process.env.CLERK_WEBHOOK_SECRET!);
|
|
342
|
-
|
|
343
|
-
let event: WebhookEvent;
|
|
344
|
-
|
|
345
|
-
try {
|
|
346
|
-
event = svix.verify(body, {
|
|
347
|
-
'svix-id': headerPayload.get('svix-id')!,
|
|
348
|
-
'svix-timestamp': headerPayload.get('svix-timestamp')!,
|
|
349
|
-
'svix-signature': headerPayload.get('svix-signature')!,
|
|
350
|
-
}) as WebhookEvent;
|
|
351
|
-
} catch {
|
|
352
|
-
return Response.json({ error: 'Invalid signature' }, { status: 400 });
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
switch (event.type) {
|
|
356
|
-
case 'user.created':
|
|
357
|
-
await prisma.user.create({
|
|
358
|
-
data: {
|
|
359
|
-
clerkId: event.data.id,
|
|
360
|
-
email: event.data.email_addresses[0].email_address,
|
|
361
|
-
name: `${event.data.first_name} ${event.data.last_name}`.trim(),
|
|
362
|
-
},
|
|
363
|
-
});
|
|
364
|
-
break;
|
|
365
|
-
|
|
366
|
-
case 'user.updated':
|
|
367
|
-
await prisma.user.update({
|
|
368
|
-
where: { clerkId: event.data.id },
|
|
369
|
-
data: {
|
|
370
|
-
email: event.data.email_addresses[0].email_address,
|
|
371
|
-
name: `${event.data.first_name} ${event.data.last_name}`.trim(),
|
|
372
|
-
},
|
|
373
|
-
});
|
|
374
|
-
break;
|
|
375
|
-
|
|
376
|
-
case 'user.deleted':
|
|
377
|
-
await prisma.user.delete({
|
|
378
|
-
where: { clerkId: event.data.id },
|
|
379
|
-
});
|
|
380
|
-
break;
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
return Response.json({ received: true });
|
|
384
|
-
}
|
|
385
|
-
```
|
|
386
|
-
|
|
387
|
-
## Auth Checklist
|
|
388
|
-
|
|
389
|
-
- [ ] Auth provider configured
|
|
390
|
-
- [ ] Sign in/up flows working
|
|
391
|
-
- [ ] Protected routes set up
|
|
392
|
-
- [ ] Session management secure
|
|
393
|
-
- [ ] OAuth providers configured
|
|
394
|
-
- [ ] Role-based access implemented
|
|
395
|
-
- [ ] User sync webhooks set up
|
|
396
|
-
- [ ] Security headers configured
|
|
397
|
-
|
|
398
|
-
## Trigger Keywords
|
|
399
|
-
auth, authentication, authorization, login, session, OAuth, JWT, protected, middleware, Clerk, NextAuth, RBAC, role
|