@polymorphism-tech/morph-spec 4.16.0 → 4.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/CLAUDE.md +36 -0
  2. package/README.md +2 -2
  3. package/bin/morph-spec.js +3 -0
  4. package/bin/task-manager.js +999 -962
  5. package/claude-plugin.json +1 -1
  6. package/docs/CHEATSHEET.md +1 -1
  7. package/docs/QUICKSTART.md +1 -1
  8. package/framework/agents/ai-agents/vector-search-expert.md +247 -233
  9. package/framework/agents/backend/ms-agent-expert.md +1 -1
  10. package/framework/agents/backend/neon-expert.md +379 -0
  11. package/framework/agents/frontend/gsap-animator.md +131 -0
  12. package/framework/agents/integrations/neon-auth.md +151 -0
  13. package/framework/agents.json +1183 -1085
  14. package/framework/commands/morph-preflight.md +200 -192
  15. package/framework/hooks/claude-code/session-start/inject-morph-context.js +1 -1
  16. package/framework/rules/gsap-standards.md +13 -0
  17. package/framework/rules/neon-auth-standards.md +13 -0
  18. package/framework/rules/neon-standards.md +26 -0
  19. package/framework/skills/level-0-meta/mcp-registry.json +1 -1
  20. package/framework/skills/level-0-meta/morph-agent-teams/SKILL.md +472 -472
  21. package/framework/skills/level-0-meta/morph-frontend-review/SKILL.md +1 -1
  22. package/framework/skills/level-0-meta/morph-init/SKILL.md +12 -12
  23. package/framework/skills/level-0-meta/morph-post-implementation/SKILL.md +5 -5
  24. package/framework/skills/level-0-meta/morph-tool-usage-guide/SKILL.md +5 -5
  25. package/framework/skills/level-0-meta/morph-tool-usage-guide/references/tools-per-phase.md +7 -7
  26. package/framework/skills/level-1-workflows/morph-phase-codebase-analysis/SKILL.md +24 -24
  27. package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +12 -12
  28. package/framework/skills/level-1-workflows/morph-phase-implement/SKILL.md +9 -9
  29. package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +1 -1
  30. package/framework/skills/level-1-workflows/morph-phase-tasks/SKILL.md +1 -1
  31. package/framework/skills/level-1-workflows/morph-phase-tasks/references/task-planning-patterns.md +143 -143
  32. package/framework/skills/level-1-workflows/morph-phase-uiux/SKILL.md +1 -1
  33. package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +1 -1
  34. package/framework/standards/STANDARDS.json +197 -116
  35. package/framework/standards/ai-agents/production.md +1 -0
  36. package/framework/standards/ai-agents/setup.md +3 -2
  37. package/framework/standards/ai-agents/workflows.md +1 -0
  38. package/framework/standards/architecture/vertical-slice/vertical-slice.md +4 -4
  39. package/framework/standards/backend/database/postgresql/database.md +63 -83
  40. package/framework/standards/backend/dotnet/scalar-ui.md +172 -172
  41. package/framework/standards/backend/dotnet/vsa-handler-patterns.md +274 -274
  42. package/framework/standards/backend/integrations/asaas/asaas-api.md +2 -2
  43. package/framework/standards/backend/integrations/neon-auth/neon-auth.md +364 -0
  44. package/framework/standards/frontend/design-system/animations.md +9 -0
  45. package/framework/standards/frontend/gsap/gsap-core.md +463 -0
  46. package/framework/standards/frontend/gsap/gsap-plugins.md +294 -0
  47. package/framework/standards/frontend/gsap/gsap-scrolltrigger.md +318 -0
  48. package/framework/standards/frontend/nextjs/motion-patterns.md +1 -0
  49. package/framework/standards/frontend/nextjs/nextjs-patterns.md +47 -37
  50. package/framework/standards/infrastructure/docker/easypanel-deploy.md +4 -8
  51. package/framework/standards/infrastructure/neon/neon-pgrag.md +239 -0
  52. package/framework/standards/infrastructure/neon/neon-pgvector.md +236 -0
  53. package/framework/standards/infrastructure/neon/neon-semantic-kernel.md +310 -0
  54. package/framework/standards/infrastructure/neon/neon-setup.md +186 -0
  55. package/framework/standards/integration/mcp/mcp-tools.md +32 -34
  56. package/framework/templates/README.md +20 -20
  57. package/framework/templates/REGISTRY.json +54 -101
  58. package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -7
  59. package/framework/templates/code/dotnet/backend/repository.cs +148 -148
  60. package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +2 -2
  61. package/framework/templates/code/dotnet/database/vector-entity.cs +93 -0
  62. package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +1 -1
  63. package/framework/templates/code/sql/{supabase-migration.sql → postgresql-migration.sql} +12 -21
  64. package/framework/templates/code/sql/{supabase-migration.template.sql → postgresql-migration.template.sql} +13 -18
  65. package/framework/templates/code/sql/{rls-policy.sql → postgresql-rls-policy.sql} +6 -9
  66. package/framework/templates/code/typescript/contracts.ts +167 -168
  67. package/framework/templates/context/CONTEXT.md +3 -3
  68. package/framework/templates/docs/proposal.md +6 -6
  69. package/framework/templates/docs/ui-components.md +1 -1
  70. package/framework/templates/feature/decisions.md +122 -121
  71. package/framework/templates/feature/recap.md +145 -145
  72. package/framework/templates/feature/tasks.md +199 -199
  73. package/framework/templates/frontend/nextjs/gsap-scroll-section.tsx.hbs +44 -0
  74. package/framework/templates/infrastructure/docker/docker-compose.template.yml +50 -67
  75. package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
  76. package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
  77. package/framework/templates/infrastructure/docker/easypanel.template.json +46 -54
  78. package/framework/templates/infrastructure/github/README.md +56 -56
  79. package/framework/templates/integrations/neon-auth-config.cs +133 -0
  80. package/framework/templates/integrations/semantic-kernel-neon.cs +125 -0
  81. package/framework/workflows/README.md +1 -1
  82. package/framework/workflows/docs/full-morph.md +69 -69
  83. package/package.json +1 -1
  84. package/src/commands/agents/dispatch-agents.js +19 -15
  85. package/src/commands/state/advance-phase.js +5 -0
  86. package/src/commands/tasks/task.js +3 -0
  87. package/src/core/state/state-manager.js +45 -1
  88. package/src/core/templates/template-registry.js +1 -66
  89. package/src/lib/detectors/index.js +4 -4
  90. package/src/lib/generators/architecture-scanner.js +639 -640
  91. package/src/lib/installers/mcp-installer.js +1 -1
  92. package/src/lib/tasks/task-classifier.js +15 -1
  93. package/src/lib/tasks/test-runner.js +5 -1
  94. package/src/lib/validators/content/content-validator.js +9 -10
  95. package/src/lib/validators/validation-runner.js +7 -5
  96. package/framework/agents/backend/supabase-expert.md +0 -256
  97. package/framework/agents/integrations/clerk-auth.md +0 -109
  98. package/framework/rules/clerk-standards.md +0 -13
  99. package/framework/rules/supabase-standards.md +0 -29
  100. package/framework/standards/backend/integrations/clerk/clerk-auth.md +0 -290
  101. package/framework/standards/infrastructure/supabase/mcp-setup.md +0 -142
  102. package/framework/standards/infrastructure/supabase/supabase-auth.md +0 -176
  103. package/framework/standards/infrastructure/supabase/supabase-custom-jwt.md +0 -206
  104. package/framework/standards/infrastructure/supabase/supabase-pgvector.md +0 -169
  105. package/framework/standards/infrastructure/supabase/supabase-rls.md +0 -184
  106. package/framework/standards/infrastructure/supabase/supabase-storage.md +0 -153
  107. package/framework/templates/integrations/clerk-config.cs +0 -217
@@ -1,290 +0,0 @@
1
- # Clerk Authentication Integration Standard
2
-
3
- > **Scope:** universal
4
- > **Layer:** 2 (on keyword)
5
- > **Keywords:** clerk, auth, authentication, nextjs, user management
6
- > **Load When:** clerk authentication keywords detected
7
-
8
- Modern authentication and user management for Next.js applications.
9
-
10
- ---
11
-
12
- ## Overview
13
-
14
- Clerk provides:
15
- - Pre-built UI components (SignIn, SignUp, UserProfile)
16
- - Social auth (Google, GitHub, etc.)
17
- - Multi-factor authentication
18
- - Session management
19
- - User metadata and organizations
20
-
21
- **Primary Use Case:** Next.js 15 + App Router
22
-
23
- ---
24
-
25
- ## Core Principles
26
-
27
- 1. **Server-First**: Use server-side auth checks with `auth()` helper
28
- 2. **Route Protection**: Middleware-based protection for private routes
29
- 3. **Type Safety**: Use Clerk's TypeScript types
30
- 4. **Edge Compatible**: Works with Edge Runtime
31
-
32
- ---
33
-
34
- ## Installation & Setup
35
-
36
- ### Install Packages
37
-
38
- ```bash
39
- npm install @clerk/nextjs
40
- ```
41
-
42
- ### Environment Variables
43
-
44
- ```bash
45
- # .env.local
46
- NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...
47
- CLERK_SECRET_KEY=sk_test_...
48
- NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
49
- NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up
50
- NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL=/dashboard
51
- NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL=/onboarding
52
- ```
53
-
54
- ### Root Layout Wrapper
55
-
56
- ```typescript
57
- // app/layout.tsx
58
- import { ClerkProvider } from '@clerk/nextjs';
59
-
60
- export default function RootLayout({ children }: { children: React.ReactNode }) {
61
- return (
62
- <ClerkProvider>
63
- <html lang="en">
64
- <body>{children}</body>
65
- </html>
66
- </ClerkProvider>
67
- );
68
- }
69
- ```
70
-
71
- ### Middleware Protection
72
-
73
- ```typescript
74
- // middleware.ts
75
- import { authMiddleware } from '@clerk/nextjs';
76
-
77
- export default authMiddleware({
78
- publicRoutes: ['/', '/api/webhooks/clerk'],
79
- ignoredRoutes: ['/api/public']
80
- });
81
-
82
- export const config = {
83
- matcher: ['/((?!.+\\.[\\w]+$|_next).*)', '/', '/(api|trpc)(.*)'],
84
- };
85
- ```
86
-
87
- ---
88
-
89
- ## Common Patterns
90
-
91
- ### Sign In Page
92
-
93
- ```typescript
94
- // app/sign-in/[[...sign-in]]/page.tsx
95
- import { SignIn } from '@clerk/nextjs';
96
-
97
- export default function SignInPage() {
98
- return (
99
- <div className="flex items-center justify-center min-h-screen">
100
- <SignIn />
101
- </div>
102
- );
103
- }
104
- ```
105
-
106
- ### Server-Side Auth Check
107
-
108
- ```typescript
109
- // app/dashboard/page.tsx
110
- import { auth } from '@clerk/nextjs';
111
- import { redirect } from 'next/navigation';
112
-
113
- export default async function DashboardPage() {
114
- const { userId } = auth();
115
-
116
- if (!userId) {
117
- redirect('/sign-in');
118
- }
119
-
120
- return <div>Dashboard for user {userId}</div>;
121
- }
122
- ```
123
-
124
- ### Client Component with User Data
125
-
126
- ```typescript
127
- // components/UserProfile.tsx
128
- 'use client';
129
- import { useUser } from '@clerk/nextjs';
130
-
131
- export function UserProfile() {
132
- const { user, isLoaded, isSignedIn } = useUser();
133
-
134
- if (!isLoaded) return <div>Loading...</div>;
135
- if (!isSignedIn) return <div>Not signed in</div>;
136
-
137
- return (
138
- <div>
139
- <p>Welcome, {user.firstName}!</p>
140
- <p>Email: {user.primaryEmailAddress?.emailAddress}</p>
141
- </div>
142
- );
143
- }
144
- ```
145
-
146
- ### API Route Protection
147
-
148
- ```typescript
149
- // app/api/protected/route.ts
150
- import { auth } from '@clerk/nextjs';
151
-
152
- export async function GET() {
153
- const { userId } = auth();
154
-
155
- if (!userId) {
156
- return new Response('Unauthorized', { status: 401 });
157
- }
158
-
159
- // Fetch user-specific data
160
- const data = await fetchUserData(userId);
161
-
162
- return Response.json(data);
163
- }
164
- ```
165
-
166
- ---
167
-
168
- ## Webhook Integration
169
-
170
- ### Setup Webhook Endpoint
171
-
172
- ```typescript
173
- // app/api/webhooks/clerk/route.ts
174
- import { Webhook } from 'svix';
175
- import { headers } from 'next/headers';
176
-
177
- export async function POST(req: Request) {
178
- const WEBHOOK_SECRET = process.env.CLERK_WEBHOOK_SECRET!;
179
-
180
- const headerPayload = headers();
181
- const svix_id = headerPayload.get('svix-id');
182
- const svix_timestamp = headerPayload.get('svix-timestamp');
183
- const svix_signature = headerPayload.get('svix-signature');
184
-
185
- if (!svix_id || !svix_timestamp || !svix_signature) {
186
- return new Response('Missing svix headers', { status: 400 });
187
- }
188
-
189
- const payload = await req.json();
190
- const body = JSON.stringify(payload);
191
-
192
- const wh = new Webhook(WEBHOOK_SECRET);
193
-
194
- let evt;
195
- try {
196
- evt = wh.verify(body, {
197
- 'svix-id': svix_id,
198
- 'svix-timestamp': svix_timestamp,
199
- 'svix-signature': svix_signature,
200
- });
201
- } catch (err) {
202
- return new Response('Invalid signature', { status: 400 });
203
- }
204
-
205
- // Handle events
206
- switch (evt.type) {
207
- case 'user.created':
208
- await handleUserCreated(evt.data);
209
- break;
210
- case 'user.updated':
211
- await handleUserUpdated(evt.data);
212
- break;
213
- case 'user.deleted':
214
- await handleUserDeleted(evt.data);
215
- break;
216
- }
217
-
218
- return new Response('OK');
219
- }
220
- ```
221
-
222
- ---
223
-
224
- ## Supabase Integration
225
-
226
- ### Sync Users to Supabase
227
-
228
- ```typescript
229
- // lib/clerk/sync-user.ts
230
- import { createClient } from '@supabase/supabase-js';
231
-
232
- async function handleUserCreated(userData: any) {
233
- const supabase = createClient(
234
- process.env.NEXT_PUBLIC_SUPABASE_URL!,
235
- process.env.SUPABASE_SERVICE_ROLE_KEY!
236
- );
237
-
238
- await supabase.from('profiles').insert({
239
- clerk_user_id: userData.id,
240
- email: userData.email_addresses[0].email_address,
241
- first_name: userData.first_name,
242
- last_name: userData.last_name,
243
- avatar_url: userData.image_url
244
- });
245
- }
246
- ```
247
-
248
- ---
249
-
250
- ## Best Practices
251
-
252
- ### Custom Claims
253
-
254
- ```typescript
255
- // Add custom claims in Clerk Dashboard or via API
256
- const { sessionClaims } = auth();
257
- const role = sessionClaims?.metadata?.role as string;
258
-
259
- if (role !== 'admin') {
260
- return new Response('Forbidden', { status: 403 });
261
- }
262
- ```
263
-
264
- ### Organizations
265
-
266
- ```typescript
267
- import { auth } from '@clerk/nextjs';
268
-
269
- const { orgId, orgRole } = auth();
270
-
271
- if (!orgId) {
272
- redirect('/create-organization');
273
- }
274
-
275
- if (orgRole !== 'admin') {
276
- return <div>Admin access required</div>;
277
- }
278
- ```
279
-
280
- ---
281
-
282
- ## References
283
-
284
- - [Clerk Next.js Documentation](https://clerk.com/docs/nextjs)
285
- - [Clerk API Reference](https://clerk.com/docs/reference/backend-api)
286
- - [Webhooks Guide](https://clerk.com/docs/integrations/webhooks)
287
-
288
- ---
289
-
290
- *MORPH-SPEC by Polymorphism Tech*
@@ -1,142 +0,0 @@
1
- # Supabase Local Development Guide
2
-
3
- > **Scope:** nextjs-supabase
4
- > **Layer:** 2 (on keyword)
5
- > **Keywords:** supabase, cli, local, docker, setup, migrations, psql
6
- > **Load When:** supabase setup keywords detected
7
-
8
- Local-first database management via Supabase CLI and psql — always targeting the local Docker instance.
9
-
10
- ## CRITICAL WARNING
11
-
12
- ```
13
- ==========================================================
14
- NEVER connect to a PRODUCTION Supabase project.
15
-
16
- All database operations MUST target the local Docker
17
- instance started via `npx supabase start`.
18
-
19
- Production schema may diverge from local (renamed tables,
20
- columns, migrations). Local is the single source of truth
21
- during development.
22
- ==========================================================
23
- ```
24
-
25
- ## Setup
26
-
27
- ### 1. Install Supabase CLI
28
-
29
- ```bash
30
- npm install -g supabase
31
- # or use npx (no global install needed)
32
- npx supabase --version
33
- ```
34
-
35
- ### 2. Start Local Instance
36
-
37
- ```bash
38
- npx supabase start
39
- ```
40
-
41
- This starts PostgreSQL, GoTrue (auth), Storage, and other services in Docker containers.
42
-
43
- ### 3. Get Connection Details
44
-
45
- ```bash
46
- npx supabase status
47
- ```
48
-
49
- Output includes:
50
- - **DB URL**: `postgresql://postgres:postgres@127.0.0.1:54322/postgres` (port may vary)
51
- - **API URL**: `http://127.0.0.1:54321`
52
- - **anon key** and **service_role key** for local testing
53
-
54
- > **Important:** The port (54322) may vary when running multiple Supabase instances. Always use `npx supabase status` to get the current DB URL — never hardcode it.
55
-
56
- ## Common CLI Commands
57
-
58
- ### Schema Inspection
59
-
60
- ```bash
61
- # Full schema dump
62
- npx supabase db dump --local --schema public
63
-
64
- # List tables (from dump output)
65
- npx supabase db dump --local --schema public 2>/dev/null | grep "CREATE TABLE"
66
-
67
- # Detailed table schema via psql
68
- psql $DB_URL -c "SELECT column_name, data_type, is_nullable, column_default \
69
- FROM information_schema.columns WHERE table_name = 'TABLE_NAME' ORDER BY ordinal_position"
70
-
71
- # Foreign key relationships
72
- psql $DB_URL -c "SELECT tc.table_name, kcu.column_name, ccu.table_name AS foreign_table, \
73
- ccu.column_name AS foreign_column FROM information_schema.table_constraints tc \
74
- JOIN information_schema.key_column_usage kcu ON tc.constraint_name = kcu.constraint_name \
75
- JOIN information_schema.constraint_column_usage ccu ON tc.constraint_name = ccu.constraint_name \
76
- WHERE tc.constraint_type = 'FOREIGN KEY' AND tc.table_name = 'TABLE_NAME'"
77
-
78
- # RLS policies
79
- psql $DB_URL -c "SELECT tablename, policyname, cmd, qual FROM pg_policies WHERE schemaname = 'public'"
80
-
81
- # Indexes
82
- psql $DB_URL -c "SELECT indexname, indexdef FROM pg_indexes WHERE tablename = 'TABLE_NAME'"
83
- ```
84
-
85
- ### Migrations
86
-
87
- ```bash
88
- # Create a new migration
89
- npx supabase migration new add_status_to_leads
90
-
91
- # Apply pending migrations to local
92
- npx supabase migration up --local
93
-
94
- # List applied migrations
95
- npx supabase migration list --local
96
-
97
- # Reset local database (destructive — re-applies all migrations)
98
- npx supabase db reset
99
- ```
100
-
101
- ### Data Operations
102
-
103
- ```bash
104
- # Run arbitrary SQL against local
105
- psql $DB_URL -c "SELECT * FROM leads LIMIT 5"
106
-
107
- # Seed data
108
- npx supabase db seed --local
109
- ```
110
-
111
- ## Usage with MORPH-SPEC Workflow
112
-
113
- During MORPH-SPEC phases, the Supabase CLI is used for:
114
-
115
- | Phase | CLI Usage | Commands |
116
- |-------|-----------|----------|
117
- | **Phase 2 (Design)** | Inspect existing schema to inform spec | `npx supabase status` → `psql` queries |
118
- | **Phase 4 (Tasks)** | Verify migration dependencies | `npx supabase migration list --local` |
119
- | **Phase 5 (Implement)** | Apply migrations, verify RLS, test queries | `npx supabase migration up --local` + `psql` |
120
- | **Post-Implementation** | Verify RLS policies are correct | `psql` pg_policies queries |
121
-
122
- ### Example Workflow
123
-
124
- ```
125
- 1. Design phase: `npx supabase status` → `psql $DB_URL` to inspect schema
126
- 2. Write migration SQL in supabase/migrations/
127
- 3. Apply: `npx supabase migration up --local`
128
- 4. Verify: `psql $DB_URL -c "SELECT policyname, cmd FROM pg_policies WHERE tablename = 'search_logs'"`
129
- 5. Test: `psql $DB_URL -c "SELECT * FROM search_logs LIMIT 5"`
130
- ```
131
-
132
- ## Best Practices
133
-
134
- 1. **Always use local Supabase** (`npx supabase start`) — never connect to remote/production
135
- 2. **Use `npx supabase status`** to detect the DB URL dynamically — port may vary
136
- 3. **Write migrations as SQL files** in `supabase/migrations/` — apply via CLI
137
- 4. **Verify RLS after every table change** — missing policies are a CRITICAL security issue
138
- 5. **Reset local DB when needed** — `npx supabase db reset` re-applies all migrations cleanly
139
-
140
- ---
141
-
142
- *MORPH-SPEC by Polymorphism Tech*
@@ -1,176 +0,0 @@
1
- # Supabase Authentication Standard
2
-
3
- > **Scope:** nextjs-supabase
4
- > **Layer:** 2 (on keyword)
5
- > **Keywords:** supabase, auth, rls, user, authentication
6
- > **Load When:** supabase auth keywords detected
7
-
8
- Stack: Next.js 15 + Supabase + .NET Backend
9
-
10
- ## Core Rules
11
-
12
- - NEVER use `supabase.auth.getSession()` on server -- reads from cookies without validation
13
- - ALWAYS use `supabase.auth.getUser()` on server -- validates JWT with Supabase
14
- - NEVER expose `service_role` key on frontend -- bypasses RLS
15
- - ALWAYS use `@supabase/ssr` for Next.js -- not `@supabase/auth-helpers-nextjs` (deprecated)
16
- - ALWAYS use PKCE flow for SSR auth
17
-
18
- ## Client Setup
19
-
20
- ### Browser Client
21
-
22
- ```ts
23
- // lib/supabase/client.ts
24
- import { createBrowserClient } from "@supabase/ssr";
25
-
26
- export function createClient() {
27
- return createBrowserClient(
28
- process.env.NEXT_PUBLIC_SUPABASE_URL!,
29
- process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
30
- );
31
- }
32
- ```
33
-
34
- ### Server Client
35
-
36
- ```ts
37
- // lib/supabase/server.ts
38
- import { createServerClient } from "@supabase/ssr";
39
- import { cookies } from "next/headers";
40
-
41
- export async function createClient() {
42
- const cookieStore = await cookies();
43
- return createServerClient(
44
- process.env.NEXT_PUBLIC_SUPABASE_URL!,
45
- process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
46
- {
47
- cookies: {
48
- getAll() { return cookieStore.getAll(); },
49
- setAll(cookiesToSet) {
50
- cookiesToSet.forEach(({ name, value, options }) =>
51
- cookieStore.set(name, value, options));
52
- },
53
- },
54
- }
55
- );
56
- }
57
- ```
58
-
59
- ## Auth Flows
60
-
61
- ```ts
62
- // Email/Password sign up
63
- await supabase.auth.signUp({ email, password,
64
- options: { emailRedirectTo: `${origin}/auth/callback` } });
65
-
66
- // Email/Password sign in
67
- await supabase.auth.signInWithPassword({ email, password });
68
-
69
- // OAuth (Google / GitHub)
70
- await supabase.auth.signInWithOAuth({
71
- provider: "google", // or "github"
72
- options: { redirectTo: `${origin}/auth/callback`,
73
- queryParams: { access_type: "offline", prompt: "consent" } } // Google-specific
74
- });
75
-
76
- // Magic Link
77
- await supabase.auth.signInWithOtp({ email,
78
- options: { emailRedirectTo: `${origin}/auth/callback` } });
79
- ```
80
-
81
- ## Auth Callback Route (PKCE)
82
-
83
- ```ts
84
- // app/auth/callback/route.ts
85
- import { createClient } from "@/lib/supabase/server";
86
- import { NextResponse } from "next/server";
87
-
88
- export async function GET(request: Request) {
89
- const { searchParams, origin } = new URL(request.url);
90
- const code = searchParams.get("code");
91
- const next = searchParams.get("next") ?? "/dashboard";
92
- if (code) {
93
- const supabase = await createClient();
94
- const { error } = await supabase.auth.exchangeCodeForSession(code);
95
- if (!error) return NextResponse.redirect(`${origin}${next}`);
96
- }
97
- return NextResponse.redirect(`${origin}/auth/error`);
98
- }
99
- ```
100
-
101
- ## Middleware Pattern
102
-
103
- ```ts
104
- // middleware.ts
105
- import { createServerClient } from "@supabase/ssr";
106
- import { NextResponse, type NextRequest } from "next/server";
107
-
108
- export async function middleware(request: NextRequest) {
109
- let supabaseResponse = NextResponse.next({ request });
110
- const supabase = createServerClient(
111
- process.env.NEXT_PUBLIC_SUPABASE_URL!,
112
- process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
113
- {
114
- cookies: {
115
- getAll() { return request.cookies.getAll(); },
116
- setAll(cookiesToSet) {
117
- cookiesToSet.forEach(({ name, value, options }) => {
118
- request.cookies.set(name, value);
119
- supabaseResponse.cookies.set(name, value, options);
120
- });
121
- },
122
- },
123
- }
124
- );
125
- const { data: { user } } = await supabase.auth.getUser();
126
- if (!user && request.nextUrl.pathname.startsWith("/dashboard"))
127
- return NextResponse.redirect(new URL("/login", request.url));
128
- return supabaseResponse;
129
- }
130
-
131
- export const config = {
132
- matcher: ["/((?!_next/static|_next/image|favicon.ico|api/webhooks).*)"],
133
- };
134
- ```
135
-
136
- ## .NET JWT Validation
137
-
138
- ```csharp
139
- builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
140
- .AddJwtBearer(options => {
141
- options.TokenValidationParameters = new TokenValidationParameters {
142
- ValidateIssuer = true,
143
- ValidIssuer = $"https://{supabaseProjectRef}.supabase.co/auth/v1",
144
- ValidateAudience = true,
145
- ValidAudience = "authenticated",
146
- ValidateIssuerSigningKey = true,
147
- IssuerSigningKey = new SymmetricSecurityKey(
148
- Encoding.UTF8.GetBytes(supabaseJwtSecret)),
149
- ValidateLifetime = true,
150
- ClockSkew = TimeSpan.FromSeconds(30)
151
- };
152
- });
153
-
154
- // Extract user ID: maps to auth.uid()
155
- var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
156
- ```
157
-
158
- ## Environment Variables
159
-
160
- | Variable | Where | Purpose |
161
- |----------|-------|---------|
162
- | `NEXT_PUBLIC_SUPABASE_URL` | Frontend | Supabase project URL |
163
- | `NEXT_PUBLIC_SUPABASE_ANON_KEY` | Frontend | Public anon key (respects RLS) |
164
- | `SUPABASE_SERVICE_ROLE_KEY` | Backend ONLY | Bypasses RLS -- NEVER on frontend |
165
- | `SUPABASE_JWT_SECRET` | Backend ONLY | JWT validation secret |
166
-
167
- ## Common Mistakes
168
-
169
- | Wrong | Right | Why |
170
- |-------|-------|-----|
171
- | `getSession()` on server | `getUser()` on server | getSession reads unvalidated cookie data |
172
- | `@supabase/auth-helpers-nextjs` | `@supabase/ssr` | auth-helpers is deprecated |
173
- | `service_role` in `NEXT_PUBLIC_*` | `anon` key in `NEXT_PUBLIC_*` | service_role bypasses all RLS |
174
- | Implicit flow for SSR | PKCE flow with code exchange | Implicit exposes tokens in URL fragments |
175
- | Auth only in page components | Auth check in middleware.ts | Middleware prevents flash of content |
176
- | Missing `setAll` in cookie config | Both `getAll` and `setAll` | Session refresh silently fails without setAll |