@nextsparkjs/ai-workflow 0.1.0-beta.100
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/LICENSE +21 -0
- package/README.md +115 -0
- package/claude/_docs/workflows-optimizations.md +359 -0
- package/claude/agents/api-tester.md +634 -0
- package/claude/agents/architecture-supervisor.md +1351 -0
- package/claude/agents/backend-developer.md +997 -0
- package/claude/agents/backend-validator.md +417 -0
- package/claude/agents/bdd-docs-writer.md +737 -0
- package/claude/agents/block-developer.md +677 -0
- package/claude/agents/code-reviewer.md +1432 -0
- package/claude/agents/db-developer.md +721 -0
- package/claude/agents/db-validator.md +407 -0
- package/claude/agents/demo-video-generator.md +493 -0
- package/claude/agents/documentation-writer.md +1268 -0
- package/claude/agents/frontend-developer.md +1234 -0
- package/claude/agents/frontend-validator.md +777 -0
- package/claude/agents/functional-validator.md +630 -0
- package/claude/agents/mock-analyst.md +387 -0
- package/claude/agents/product-manager.md +963 -0
- package/claude/agents/qa-automation.md +1762 -0
- package/claude/agents/release-manager.md +634 -0
- package/claude/agents/selectors-translator.md +262 -0
- package/claude/agents/unit-test-writer.md +785 -0
- package/claude/agents/visual-comparator.md +329 -0
- package/claude/agents/workflow-maintainer.md +352 -0
- package/claude/commands/do/README.md +88 -0
- package/claude/commands/do/create-api.md +64 -0
- package/claude/commands/do/create-entity.md +66 -0
- package/claude/commands/do/create-migration.md +64 -0
- package/claude/commands/do/create-plugin.md +56 -0
- package/claude/commands/do/create-theme.md +70 -0
- package/claude/commands/do/mock-data.md +67 -0
- package/claude/commands/do/reset-db.md +71 -0
- package/claude/commands/do/setup-scheduled-action.md +75 -0
- package/claude/commands/do/sync-code-review.md +117 -0
- package/claude/commands/do/update-selectors.md +112 -0
- package/claude/commands/do/use-skills.md +90 -0
- package/claude/commands/do/validate-blocks.md +69 -0
- package/claude/commands/how-to/README.md +261 -0
- package/claude/commands/how-to/add-metadata.md +692 -0
- package/claude/commands/how-to/add-taxonomies.md +806 -0
- package/claude/commands/how-to/add-translations.md +571 -0
- package/claude/commands/how-to/create-api.md +577 -0
- package/claude/commands/how-to/create-block.md +575 -0
- package/claude/commands/how-to/create-child-entities.md +771 -0
- package/claude/commands/how-to/create-entity.md +597 -0
- package/claude/commands/how-to/create-migrations.md +605 -0
- package/claude/commands/how-to/create-plugin.md +654 -0
- package/claude/commands/how-to/customize-app.md +481 -0
- package/claude/commands/how-to/customize-dashboard.md +553 -0
- package/claude/commands/how-to/customize-theme.md +438 -0
- package/claude/commands/how-to/define-features-flows.md +632 -0
- package/claude/commands/how-to/deploy.md +507 -0
- package/claude/commands/how-to/handle-file-uploads.md +746 -0
- package/claude/commands/how-to/implement-search.md +1001 -0
- package/claude/commands/how-to/install-plugins.md +352 -0
- package/claude/commands/how-to/manage-test-coverage.md +984 -0
- package/claude/commands/how-to/run-tests.md +400 -0
- package/claude/commands/how-to/set-app-languages.md +601 -0
- package/claude/commands/how-to/set-plans-and-permissions.md +575 -0
- package/claude/commands/how-to/set-scheduled-actions.md +527 -0
- package/claude/commands/how-to/set-user-roles-and-permissions.md +550 -0
- package/claude/commands/how-to/setup-authentication.md +388 -0
- package/claude/commands/how-to/setup-claude-code.md +440 -0
- package/claude/commands/how-to/setup-database.md +274 -0
- package/claude/commands/how-to/setup-email-providers.md +598 -0
- package/claude/commands/how-to/setup-mobile-dev.md +627 -0
- package/claude/commands/how-to/start.md +500 -0
- package/claude/commands/how-to/use-devtools.md +639 -0
- package/claude/commands/how-to/use-superadmin.md +622 -0
- package/claude/commands/session/README.md +193 -0
- package/claude/commands/session/block-create.md +190 -0
- package/claude/commands/session/block-list.md +203 -0
- package/claude/commands/session/block-update.md +192 -0
- package/claude/commands/session/block-validate.md +218 -0
- package/claude/commands/session/changelog.md +115 -0
- package/claude/commands/session/close.md +225 -0
- package/claude/commands/session/commit.md +174 -0
- package/claude/commands/session/db-entity.md +206 -0
- package/claude/commands/session/db-fix.md +212 -0
- package/claude/commands/session/db-sample.md +206 -0
- package/claude/commands/session/demo.md +178 -0
- package/claude/commands/session/doc-bdd.md +207 -0
- package/claude/commands/session/doc-feature.md +218 -0
- package/claude/commands/session/doc-read.md +225 -0
- package/claude/commands/session/execute.md +204 -0
- package/claude/commands/session/explain.md +202 -0
- package/claude/commands/session/fix-bug.md +210 -0
- package/claude/commands/session/fix-build.md +182 -0
- package/claude/commands/session/fix-test.md +189 -0
- package/claude/commands/session/pending.md +232 -0
- package/claude/commands/session/refine.md +188 -0
- package/claude/commands/session/resume.md +192 -0
- package/claude/commands/session/review.md +192 -0
- package/claude/commands/session/scope-change.md +181 -0
- package/claude/commands/session/start-blocks.md +347 -0
- package/claude/commands/session/start.md +604 -0
- package/claude/commands/session/status.md +169 -0
- package/claude/commands/session/test-fix.md +221 -0
- package/claude/commands/session/test-run.md +203 -0
- package/claude/commands/session/test-write.md +242 -0
- package/claude/commands/session/validate.md +162 -0
- package/claude/config/context.json +40 -0
- package/claude/config/github.json +69 -0
- package/claude/config/github.schema.json +106 -0
- package/claude/config/team.json +46 -0
- package/claude/config/team.schema.json +106 -0
- package/claude/config/workspace.json +43 -0
- package/claude/config/workspace.schema.json +75 -0
- package/claude/skills/README.md +228 -0
- package/claude/skills/accessibility/SKILL.md +573 -0
- package/claude/skills/api-bypass-layers/SKILL.md +550 -0
- package/claude/skills/asana-integration/SKILL.md +499 -0
- package/claude/skills/better-auth/SKILL.md +666 -0
- package/claude/skills/billing-subscriptions/SKILL.md +660 -0
- package/claude/skills/block-decision-matrix/SKILL.md +359 -0
- package/claude/skills/clickup-integration/SKILL.md +434 -0
- package/claude/skills/core-theme-responsibilities/SKILL.md +485 -0
- package/claude/skills/create-plugin/SKILL.md +425 -0
- package/claude/skills/create-theme/SKILL.md +331 -0
- package/claude/skills/cypress-api/SKILL.md +511 -0
- package/claude/skills/cypress-api/scripts/generate-api-controller.py +329 -0
- package/claude/skills/cypress-api/scripts/generate-api-test.py +930 -0
- package/claude/skills/cypress-e2e/SKILL.md +526 -0
- package/claude/skills/cypress-e2e/scripts/extract-selectors.py +383 -0
- package/claude/skills/cypress-e2e/scripts/generate-uat-test.py +788 -0
- package/claude/skills/cypress-selectors/SKILL.md +309 -0
- package/claude/skills/cypress-selectors/scripts/extract-missing.py +243 -0
- package/claude/skills/cypress-selectors/scripts/generate-block-selectors.py +283 -0
- package/claude/skills/cypress-selectors/scripts/validate-selectors.py +145 -0
- package/claude/skills/database-migrations/SKILL.md +335 -0
- package/claude/skills/database-migrations/scripts/generate-sample-data.py +284 -0
- package/claude/skills/database-migrations/scripts/validate-migration.py +323 -0
- package/claude/skills/design-system/SKILL.md +682 -0
- package/claude/skills/documentation/SKILL.md +540 -0
- package/claude/skills/entity-api/SKILL.md +482 -0
- package/claude/skills/entity-system/SKILL.md +635 -0
- package/claude/skills/entity-system/scripts/generate-child-migration.py +298 -0
- package/claude/skills/entity-system/scripts/generate-metas-migration.py +233 -0
- package/claude/skills/entity-system/scripts/generate-migration.py +382 -0
- package/claude/skills/entity-system/scripts/generate-sample-data.py +418 -0
- package/claude/skills/entity-system/scripts/scaffold-entity.py +661 -0
- package/claude/skills/github/SKILL.md +467 -0
- package/claude/skills/i18n-nextintl/SKILL.md +302 -0
- package/claude/skills/i18n-nextintl/scripts/add-translation.py +243 -0
- package/claude/skills/i18n-nextintl/scripts/extract-hardcoded.py +246 -0
- package/claude/skills/i18n-nextintl/scripts/validate-translations.py +260 -0
- package/claude/skills/impact-analysis/SKILL.md +203 -0
- package/claude/skills/jest-unit/SKILL.md +306 -0
- package/claude/skills/jest-unit/references/component-testing.md +371 -0
- package/claude/skills/jest-unit/references/mocking-patterns.md +380 -0
- package/claude/skills/jest-unit/references/service-hook-testing.md +454 -0
- package/claude/skills/jira-integration/SKILL.md +539 -0
- package/claude/skills/media-library/SKILL.md +743 -0
- package/claude/skills/mock-analysis/SKILL.md +276 -0
- package/claude/skills/monorepo-architecture/SKILL.md +162 -0
- package/claude/skills/nextjs-api-development/SKILL.md +364 -0
- package/claude/skills/nextjs-api-development/scripts/generate-crud-tests.py +456 -0
- package/claude/skills/nextjs-api-development/scripts/scaffold-endpoint.py +481 -0
- package/claude/skills/nextjs-api-development/scripts/validate-api.py +283 -0
- package/claude/skills/notion-integration/SKILL.md +641 -0
- package/claude/skills/npm-development-workflow/SKILL.md +480 -0
- package/claude/skills/page-builder-blocks/SKILL.md +530 -0
- package/claude/skills/page-builder-blocks/scripts/scaffold-block.py +444 -0
- package/claude/skills/permissions-system/SKILL.md +619 -0
- package/claude/skills/plugins/SKILL.md +340 -0
- package/claude/skills/plugins/references/plugin-templates.md +414 -0
- package/claude/skills/plugins/references/plugin-testing.md +353 -0
- package/claude/skills/plugins/references/plugin-types.md +198 -0
- package/claude/skills/plugins/scripts/scaffold-plugin.py +443 -0
- package/claude/skills/pom-patterns/SKILL.md +452 -0
- package/claude/skills/pom-patterns/scripts/generate-pom.py +392 -0
- package/claude/skills/rate-limiting/SKILL.md +342 -0
- package/claude/skills/react-best-practices/AGENTS.md +2410 -0
- package/claude/skills/react-best-practices/README.md +123 -0
- package/claude/skills/react-best-practices/SKILL.md +125 -0
- package/claude/skills/react-best-practices/metadata.json +15 -0
- package/claude/skills/react-best-practices/rules/_sections.md +46 -0
- package/claude/skills/react-best-practices/rules/_template.md +28 -0
- package/claude/skills/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/claude/skills/react-best-practices/rules/advanced-use-latest.md +49 -0
- package/claude/skills/react-best-practices/rules/async-api-routes.md +38 -0
- package/claude/skills/react-best-practices/rules/async-defer-await.md +80 -0
- package/claude/skills/react-best-practices/rules/async-dependencies.md +36 -0
- package/claude/skills/react-best-practices/rules/async-parallel.md +28 -0
- package/claude/skills/react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/claude/skills/react-best-practices/rules/bundle-barrel-imports.md +59 -0
- package/claude/skills/react-best-practices/rules/bundle-conditional.md +31 -0
- package/claude/skills/react-best-practices/rules/bundle-defer-third-party.md +49 -0
- package/claude/skills/react-best-practices/rules/bundle-dynamic-imports.md +35 -0
- package/claude/skills/react-best-practices/rules/bundle-preload.md +50 -0
- package/claude/skills/react-best-practices/rules/client-event-listeners.md +74 -0
- package/claude/skills/react-best-practices/rules/client-localstorage-schema.md +71 -0
- package/claude/skills/react-best-practices/rules/client-passive-event-listeners.md +48 -0
- package/claude/skills/react-best-practices/rules/client-swr-dedup.md +56 -0
- package/claude/skills/react-best-practices/rules/js-batch-dom-css.md +82 -0
- package/claude/skills/react-best-practices/rules/js-cache-function-results.md +80 -0
- package/claude/skills/react-best-practices/rules/js-cache-property-access.md +28 -0
- package/claude/skills/react-best-practices/rules/js-cache-storage.md +70 -0
- package/claude/skills/react-best-practices/rules/js-combine-iterations.md +32 -0
- package/claude/skills/react-best-practices/rules/js-early-exit.md +50 -0
- package/claude/skills/react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/claude/skills/react-best-practices/rules/js-index-maps.md +37 -0
- package/claude/skills/react-best-practices/rules/js-length-check-first.md +49 -0
- package/claude/skills/react-best-practices/rules/js-min-max-loop.md +82 -0
- package/claude/skills/react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/claude/skills/react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/claude/skills/react-best-practices/rules/rendering-activity.md +26 -0
- package/claude/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
- package/claude/skills/react-best-practices/rules/rendering-conditional-render.md +40 -0
- package/claude/skills/react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/claude/skills/react-best-practices/rules/rendering-hoist-jsx.md +46 -0
- package/claude/skills/react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
- package/claude/skills/react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/claude/skills/react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/claude/skills/react-best-practices/rules/rerender-dependencies.md +45 -0
- package/claude/skills/react-best-practices/rules/rerender-derived-state.md +29 -0
- package/claude/skills/react-best-practices/rules/rerender-functional-setstate.md +74 -0
- package/claude/skills/react-best-practices/rules/rerender-lazy-state-init.md +58 -0
- package/claude/skills/react-best-practices/rules/rerender-memo.md +44 -0
- package/claude/skills/react-best-practices/rules/rerender-transitions.md +40 -0
- package/claude/skills/react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/claude/skills/react-best-practices/rules/server-cache-lru.md +41 -0
- package/claude/skills/react-best-practices/rules/server-cache-react.md +76 -0
- package/claude/skills/react-best-practices/rules/server-parallel-fetching.md +83 -0
- package/claude/skills/react-best-practices/rules/server-serialization.md +38 -0
- package/claude/skills/react-patterns/SKILL.md +688 -0
- package/claude/skills/registry-system/SKILL.md +331 -0
- package/claude/skills/scheduled-actions/SKILL.md +671 -0
- package/claude/skills/scope-enforcement/SKILL.md +542 -0
- package/claude/skills/scope-enforcement/scripts/validate-scope.py +357 -0
- package/claude/skills/server-actions/SKILL.md +493 -0
- package/claude/skills/service-layer/SKILL.md +587 -0
- package/claude/skills/session-management/SKILL.md +266 -0
- package/claude/skills/session-management/scripts/create-session.py +166 -0
- package/claude/skills/session-management/scripts/iteration-close.sh +105 -0
- package/claude/skills/session-management/scripts/iteration-init.sh +180 -0
- package/claude/skills/session-management/scripts/session-archive.sh +87 -0
- package/claude/skills/session-management/scripts/session-close.sh +133 -0
- package/claude/skills/session-management/scripts/session-init.sh +225 -0
- package/claude/skills/session-management/scripts/session-list.sh +163 -0
- package/claude/skills/session-management/scripts/split-plan.sh +116 -0
- package/claude/skills/shadcn-components/SKILL.md +586 -0
- package/claude/skills/shadcn-theming/SKILL.md +446 -0
- package/claude/skills/suspense-loading/SKILL.md +280 -0
- package/claude/skills/tailwind-theming/SKILL.md +507 -0
- package/claude/skills/tanstack-query/SKILL.md +608 -0
- package/claude/skills/test-coverage/SKILL.md +239 -0
- package/claude/skills/web-design-guidelines/SKILL.md +39 -0
- package/claude/skills/zod-validation/SKILL.md +537 -0
- package/claude/templates/blocks/progress.md +86 -0
- package/claude/templates/iteration/changes.md +61 -0
- package/claude/templates/iteration/progress.md +55 -0
- package/claude/templates/log.md +31 -0
- package/claude/templates/story/context.md +77 -0
- package/claude/templates/story/pendings.md +37 -0
- package/claude/templates/story/plan.md +299 -0
- package/claude/templates/story/requirements.md +109 -0
- package/claude/templates/story/scope.json +10 -0
- package/claude/templates/story/tests.md +91 -0
- package/claude/templates/task/progress.md +58 -0
- package/claude/templates/task/requirements.md +54 -0
- package/claude/workflows/README.md +154 -0
- package/claude/workflows/blocks.md +614 -0
- package/claude/workflows/story.md +1207 -0
- package/claude/workflows/task.md +927 -0
- package/claude/workflows/tweak.md +527 -0
- package/cursor/.gitkeep +0 -0
- package/package.json +35 -0
- package/scripts/postinstall.mjs +198 -0
- package/scripts/setup.mjs +282 -0
- package/scripts/sync.mjs +209 -0
|
@@ -0,0 +1,666 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: better-auth
|
|
3
|
+
description: |
|
|
4
|
+
Authentication patterns with Better Auth for this Next.js application.
|
|
5
|
+
Covers session authentication, API key authentication, dual auth, OAuth, and testing.
|
|
6
|
+
Use this skill when implementing or modifying authentication features.
|
|
7
|
+
allowed-tools: Read, Glob, Grep
|
|
8
|
+
version: 1.0.0
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Better Auth Skill
|
|
12
|
+
|
|
13
|
+
Authentication patterns and best practices for this Next.js application using Better Auth.
|
|
14
|
+
|
|
15
|
+
## Architecture Overview
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
core/lib/
|
|
19
|
+
├── auth.ts # Better Auth configuration
|
|
20
|
+
├── auth-client.ts # Client-side auth utilities
|
|
21
|
+
└── api/auth/
|
|
22
|
+
├── dual-auth.ts # Dual authentication (API Key + Session)
|
|
23
|
+
├── index.ts # API key validation
|
|
24
|
+
└── scopes.ts # Permission scopes
|
|
25
|
+
|
|
26
|
+
app/hooks/
|
|
27
|
+
└── useAuth.ts # Client-side auth hook
|
|
28
|
+
|
|
29
|
+
app/(auth)/
|
|
30
|
+
├── login/ # Login page
|
|
31
|
+
├── register/ # Registration page
|
|
32
|
+
├── verify-email/ # Email verification
|
|
33
|
+
└── forgot-password/ # Password reset
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## When to Use This Skill
|
|
37
|
+
|
|
38
|
+
- Implementing authentication flows (login, register, logout)
|
|
39
|
+
- Adding API key authentication to endpoints
|
|
40
|
+
- Implementing role-based access control
|
|
41
|
+
- Setting up OAuth providers
|
|
42
|
+
- Testing authentication flows
|
|
43
|
+
- Securing API endpoints with dual auth
|
|
44
|
+
|
|
45
|
+
## Authentication Methods
|
|
46
|
+
|
|
47
|
+
### 1. Session Authentication (Dashboard)
|
|
48
|
+
|
|
49
|
+
Used for authenticated users in the dashboard UI.
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
// Server-side session check
|
|
53
|
+
import { auth } from '@/core/lib/auth'
|
|
54
|
+
|
|
55
|
+
const session = await auth.api.getSession({
|
|
56
|
+
headers: request.headers
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
if (!session?.user) {
|
|
60
|
+
return redirect('/login')
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
// Client-side with useAuth hook
|
|
66
|
+
import { useAuth } from '@/app/hooks/useAuth'
|
|
67
|
+
|
|
68
|
+
function Dashboard() {
|
|
69
|
+
const { user, session, isLoading, isAuthenticated } = useAuth()
|
|
70
|
+
|
|
71
|
+
if (isLoading) return <Loading />
|
|
72
|
+
if (!isAuthenticated) return redirect('/login')
|
|
73
|
+
|
|
74
|
+
return <div>Welcome, {user.name}</div>
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### 2. API Key Authentication (External APIs)
|
|
79
|
+
|
|
80
|
+
Used for external integrations and programmatic access.
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
// API Key headers
|
|
84
|
+
// Option 1: Authorization header
|
|
85
|
+
Authorization: Bearer sk_live_xxxxxxxxxxxxx
|
|
86
|
+
|
|
87
|
+
// Option 2: x-api-key header
|
|
88
|
+
x-api-key: sk_live_xxxxxxxxxxxxx
|
|
89
|
+
|
|
90
|
+
// CRITICAL: Always include team context
|
|
91
|
+
x-team-id: team-tmt-001
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### 3. Dual Authentication (Unified Endpoints)
|
|
95
|
+
|
|
96
|
+
All `/api/v1/` endpoints support both authentication methods.
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
import { authenticateRequest } from '@/core/lib/api/auth/dual-auth'
|
|
100
|
+
|
|
101
|
+
export async function GET(request: NextRequest) {
|
|
102
|
+
// Tries API Key first, then Session
|
|
103
|
+
const authResult = await authenticateRequest(request)
|
|
104
|
+
|
|
105
|
+
if (!authResult.success) {
|
|
106
|
+
return createAuthError('Unauthorized', 401)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// authResult contains:
|
|
110
|
+
// - type: 'api-key' | 'session'
|
|
111
|
+
// - user: { id, email, role, defaultTeamId }
|
|
112
|
+
// - scopes: string[] (for API keys) or ['all'] (for sessions)
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## User Roles vs Team Roles
|
|
117
|
+
|
|
118
|
+
### CRITICAL: Two Separate Role Systems
|
|
119
|
+
|
|
120
|
+
This application has **two completely separate role systems**:
|
|
121
|
+
|
|
122
|
+
| Aspect | User Roles (App-Level) | Team Roles (Team-Level) |
|
|
123
|
+
|--------|------------------------|-------------------------|
|
|
124
|
+
| **Stored in** | `user.role` column | `teamMembers.role` column |
|
|
125
|
+
| **Scope** | Global (entire app) | Per-team membership |
|
|
126
|
+
| **Extensible** | ❌ NO - Fixed in core | ✅ YES - Themes can add custom roles |
|
|
127
|
+
| **Purpose** | System access (routes) | Entity permissions within team |
|
|
128
|
+
|
|
129
|
+
### User Roles (App-Level, FIXED)
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
// 3 fixed system roles - CANNOT be extended by themes
|
|
133
|
+
type UserRole = 'member' | 'superadmin' | 'developer'
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
| Role | Hierarchy | Access | Routes |
|
|
137
|
+
|------|-----------|--------|--------|
|
|
138
|
+
| `member` | 1 | Standard user | `/dashboard/*` |
|
|
139
|
+
| `superadmin` | 99 | System admin, bypasses team permissions | `/superadmin/*` |
|
|
140
|
+
| `developer` | 100 | Full access, debugging APIs | `/devtools/*` |
|
|
141
|
+
|
|
142
|
+
**Check user role:**
|
|
143
|
+
```typescript
|
|
144
|
+
import { roleHelpers } from '@/core/lib/role-helpers'
|
|
145
|
+
|
|
146
|
+
// CORRECT: Use roleHelpers for user roles
|
|
147
|
+
if (roleHelpers.isDeveloper(user.role)) {
|
|
148
|
+
// Access to /devtools/*
|
|
149
|
+
}
|
|
150
|
+
if (roleHelpers.isSuperAdmin(user.role)) {
|
|
151
|
+
// Access to /superadmin/*, bypass team checks
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// WRONG: Don't check user roles like team roles
|
|
155
|
+
if (membership.hasRole('superadmin')) {} // Wrong context!
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Team Roles (Team-Level, EXTENSIBLE)
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
// Core team roles (protected, always available)
|
|
162
|
+
type CoreTeamRole = 'owner' | 'admin' | 'member' | 'viewer'
|
|
163
|
+
|
|
164
|
+
// Theme can add custom roles
|
|
165
|
+
type TeamRole = CoreTeamRole | 'editor' | 'contributor' | string
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
| Role | Hierarchy | Description |
|
|
169
|
+
|------|-----------|-------------|
|
|
170
|
+
| `owner` | 100 | Team creator, all permissions, cannot be removed |
|
|
171
|
+
| `admin` | 50 | Team management, member roles, billing |
|
|
172
|
+
| `member` | 10 | Standard entity access |
|
|
173
|
+
| `viewer` | 1 | Read-only access |
|
|
174
|
+
| `editor`* | 5 | Theme-defined: Edit without delete |
|
|
175
|
+
| `contributor`* | 3 | Theme-defined: Limited create/edit |
|
|
176
|
+
|
|
177
|
+
*Custom roles defined in `permissions.config.ts`
|
|
178
|
+
|
|
179
|
+
**Check team role:**
|
|
180
|
+
```typescript
|
|
181
|
+
import { MembershipService } from '@/core/lib/services'
|
|
182
|
+
|
|
183
|
+
// Get membership context
|
|
184
|
+
const membership = await MembershipService.get(userId, teamId)
|
|
185
|
+
|
|
186
|
+
// Check team role
|
|
187
|
+
if (membership.hasRole('admin')) {
|
|
188
|
+
// Can manage team members
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Check specific permission
|
|
192
|
+
if (membership.can('products.create')) {
|
|
193
|
+
// Can create products in this team
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Why Two Systems?
|
|
198
|
+
|
|
199
|
+
| User Roles | Team Roles |
|
|
200
|
+
|------------|------------|
|
|
201
|
+
| Control **where** users can go | Control **what** users can do |
|
|
202
|
+
| Route-level access | Entity-level permissions |
|
|
203
|
+
| Fixed for security | Flexible for business logic |
|
|
204
|
+
| Checked by middleware | Checked by API/UI |
|
|
205
|
+
|
|
206
|
+
## User Metadata (users_metas)
|
|
207
|
+
|
|
208
|
+
User metadata is stored in a **separate table** (`users_metas`) using the standard entity meta pattern.
|
|
209
|
+
|
|
210
|
+
### Schema
|
|
211
|
+
|
|
212
|
+
```sql
|
|
213
|
+
-- core/migrations/003_user_metas.sql
|
|
214
|
+
CREATE TABLE "users_metas" (
|
|
215
|
+
id TEXT PRIMARY KEY,
|
|
216
|
+
"userId" TEXT NOT NULL REFERENCES "users"(id) ON DELETE CASCADE,
|
|
217
|
+
"metaKey" TEXT NOT NULL,
|
|
218
|
+
"metaValue" JSONB NOT NULL DEFAULT '{}',
|
|
219
|
+
"dataType" TEXT, -- 'string' | 'number' | 'boolean' | 'json'
|
|
220
|
+
"isPublic" BOOLEAN DEFAULT FALSE,
|
|
221
|
+
"isSearchable" BOOLEAN DEFAULT FALSE,
|
|
222
|
+
CONSTRAINT users_metas_unique_key UNIQUE ("userId", "metaKey")
|
|
223
|
+
);
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Common Meta Keys
|
|
227
|
+
|
|
228
|
+
| Key | Type | Description | isPublic |
|
|
229
|
+
|-----|------|-------------|----------|
|
|
230
|
+
| `preferences.theme` | string | 'light' \| 'dark' \| 'system' | false |
|
|
231
|
+
| `preferences.language` | string | Locale code ('en', 'es') | false |
|
|
232
|
+
| `preferences.timezone` | string | Timezone identifier | false |
|
|
233
|
+
| `preferences.sidebarCollapsed` | boolean | UI state | false |
|
|
234
|
+
| `onboarding.completed` | boolean | Onboarding status | false |
|
|
235
|
+
| `onboarding.step` | number | Current onboarding step | false |
|
|
236
|
+
| `notifications.email` | boolean | Email notification preference | false |
|
|
237
|
+
| `profile.bio` | string | User biography | true |
|
|
238
|
+
| `profile.socialLinks` | json | Social media links | true |
|
|
239
|
+
|
|
240
|
+
### Usage
|
|
241
|
+
|
|
242
|
+
```typescript
|
|
243
|
+
import { MetaService } from '@/core/lib/services'
|
|
244
|
+
|
|
245
|
+
// Get user meta
|
|
246
|
+
const theme = await MetaService.get('users', userId, 'preferences.theme')
|
|
247
|
+
|
|
248
|
+
// Set user meta
|
|
249
|
+
await MetaService.set('users', userId, 'preferences.theme', 'dark')
|
|
250
|
+
|
|
251
|
+
// Get multiple metas
|
|
252
|
+
const prefs = await MetaService.getMany('users', userId, [
|
|
253
|
+
'preferences.theme',
|
|
254
|
+
'preferences.language'
|
|
255
|
+
])
|
|
256
|
+
|
|
257
|
+
// Delete meta
|
|
258
|
+
await MetaService.delete('users', userId, 'onboarding.step')
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Client-Side Hook
|
|
262
|
+
|
|
263
|
+
```typescript
|
|
264
|
+
import { useUserSettings } from '@/core/hooks/useUserSettings'
|
|
265
|
+
|
|
266
|
+
function SettingsPage() {
|
|
267
|
+
const { settings, updateSetting, isLoading } = useUserSettings()
|
|
268
|
+
|
|
269
|
+
const toggleTheme = () => {
|
|
270
|
+
updateSetting('preferences.theme', settings.theme === 'dark' ? 'light' : 'dark')
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return (
|
|
274
|
+
<Button onClick={toggleTheme}>
|
|
275
|
+
{settings.theme === 'dark' ? 'Light Mode' : 'Dark Mode'}
|
|
276
|
+
</Button>
|
|
277
|
+
)
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### RLS Policies
|
|
282
|
+
|
|
283
|
+
- **Owner access:** Users can read/write their own metas
|
|
284
|
+
- **Admin access:** Superadmins can read/write any user's metas
|
|
285
|
+
- **Public read:** Metas with `isPublic = true` are readable by anyone
|
|
286
|
+
|
|
287
|
+
## API Key Scopes
|
|
288
|
+
|
|
289
|
+
```typescript
|
|
290
|
+
// Scope format: {entity}:{action}
|
|
291
|
+
// Examples:
|
|
292
|
+
// - tasks:read → Read tasks
|
|
293
|
+
// - tasks:write → Create/update tasks
|
|
294
|
+
// - tasks:delete → Delete tasks
|
|
295
|
+
// - tasks:* → Full tasks access
|
|
296
|
+
// - * → Superadmin full access
|
|
297
|
+
|
|
298
|
+
// Check scopes in API
|
|
299
|
+
const hasAccess = authResult.scopes?.includes('tasks:read') ||
|
|
300
|
+
authResult.scopes?.includes('tasks:*') ||
|
|
301
|
+
authResult.scopes?.includes('*')
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
## Test Users
|
|
305
|
+
|
|
306
|
+
### Theme Users (password: `Test1234`)
|
|
307
|
+
|
|
308
|
+
```typescript
|
|
309
|
+
DEFAULT_THEME_USERS = {
|
|
310
|
+
OWNER: 'carlos.mendoza@tmt.dev', // Everpoint Labs (owner)
|
|
311
|
+
ADMIN: 'james.wilson@tmt.dev', // Everpoint Labs (admin)
|
|
312
|
+
MEMBER: 'emily.johnson@tmt.dev', // Everpoint Labs (member)
|
|
313
|
+
EDITOR: 'diego.ramirez@tmt.dev', // Everpoint Labs (editor)
|
|
314
|
+
VIEWER: 'sarah.davis@tmt.dev', // Ironvale Global (viewer)
|
|
315
|
+
}
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### Core Users (password: `Pandora1234`)
|
|
319
|
+
|
|
320
|
+
```typescript
|
|
321
|
+
CORE_USERS = {
|
|
322
|
+
SUPERADMIN: 'superadmin@tmt.dev', // Global superadmin
|
|
323
|
+
DEVELOPER: 'developer@tmt.dev', // Global developer
|
|
324
|
+
}
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### Test Teams
|
|
328
|
+
|
|
329
|
+
| Team ID | Team Name | Description |
|
|
330
|
+
|---------|-----------|-------------|
|
|
331
|
+
| `team-tmt-001` | Everpoint Labs | Primary test team |
|
|
332
|
+
| `team-tmt-002` | Ironvale Global | Secondary test team |
|
|
333
|
+
|
|
334
|
+
## Authentication Flows
|
|
335
|
+
|
|
336
|
+
### Email/Password Registration
|
|
337
|
+
|
|
338
|
+
```
|
|
339
|
+
1. User submits registration form
|
|
340
|
+
2. Validate email format and password strength
|
|
341
|
+
3. Create user with emailVerified = false
|
|
342
|
+
4. Send verification email with token
|
|
343
|
+
5. User clicks verification link
|
|
344
|
+
6. Set emailVerified = true
|
|
345
|
+
7. Redirect to login
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
### Email/Password Login
|
|
349
|
+
|
|
350
|
+
```
|
|
351
|
+
1. User submits login form
|
|
352
|
+
2. Validate credentials
|
|
353
|
+
3. Check emailVerified = true
|
|
354
|
+
4. Create session
|
|
355
|
+
5. Set HttpOnly cookie
|
|
356
|
+
6. Redirect to dashboard
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
### Google OAuth
|
|
360
|
+
|
|
361
|
+
```
|
|
362
|
+
1. User clicks "Sign in with Google"
|
|
363
|
+
2. Redirect to Google OAuth
|
|
364
|
+
3. Google returns profile data
|
|
365
|
+
4. Map profile to user (mapProfileToUser)
|
|
366
|
+
5. Create/update user record
|
|
367
|
+
6. Create session
|
|
368
|
+
7. Redirect to dashboard
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
### Password Reset
|
|
372
|
+
|
|
373
|
+
```
|
|
374
|
+
1. User requests password reset
|
|
375
|
+
2. Generate reset token (1-hour expiry)
|
|
376
|
+
3. Send reset email
|
|
377
|
+
4. User clicks reset link
|
|
378
|
+
5. Validate token
|
|
379
|
+
6. User enters new password
|
|
380
|
+
7. Update password hash (bcrypt)
|
|
381
|
+
8. Invalidate token
|
|
382
|
+
9. Redirect to login
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
## Security Rules
|
|
386
|
+
|
|
387
|
+
### Password Requirements
|
|
388
|
+
|
|
389
|
+
```typescript
|
|
390
|
+
// Minimum requirements
|
|
391
|
+
const passwordSchema = z.string()
|
|
392
|
+
.min(8, 'Password must be at least 8 characters')
|
|
393
|
+
.max(128, 'Password cannot exceed 128 characters')
|
|
394
|
+
.regex(/[A-Z]/, 'Password must contain uppercase letter')
|
|
395
|
+
.regex(/[0-9]/, 'Password must contain a number')
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
### Session Configuration
|
|
399
|
+
|
|
400
|
+
```typescript
|
|
401
|
+
// Session settings
|
|
402
|
+
const sessionConfig = {
|
|
403
|
+
expiresIn: 60 * 60 * 24 * 7, // 7 days
|
|
404
|
+
updateAge: 60 * 60 * 24, // Update every day
|
|
405
|
+
cookieOptions: {
|
|
406
|
+
httpOnly: true,
|
|
407
|
+
secure: process.env.NODE_ENV === 'production',
|
|
408
|
+
sameSite: 'lax'
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
### Rate Limiting
|
|
414
|
+
|
|
415
|
+
| Action | Limit | Window |
|
|
416
|
+
|--------|-------|--------|
|
|
417
|
+
| Login attempts | 5 | 15 minutes |
|
|
418
|
+
| Password reset | 3 | 1 hour |
|
|
419
|
+
| Verification email | 5 | 1 hour |
|
|
420
|
+
| API requests | 100 | 1 minute |
|
|
421
|
+
|
|
422
|
+
### Token Expiration
|
|
423
|
+
|
|
424
|
+
| Token Type | Expiration |
|
|
425
|
+
|------------|------------|
|
|
426
|
+
| Email verification | 24 hours |
|
|
427
|
+
| Password reset | 1 hour |
|
|
428
|
+
| Session | 7 days |
|
|
429
|
+
| API Key | No expiration (revocable) |
|
|
430
|
+
|
|
431
|
+
## useAuth Hook
|
|
432
|
+
|
|
433
|
+
```typescript
|
|
434
|
+
import { useAuth } from '@/app/hooks/useAuth'
|
|
435
|
+
|
|
436
|
+
function Component() {
|
|
437
|
+
const {
|
|
438
|
+
// State
|
|
439
|
+
user, // Current user object
|
|
440
|
+
session, // Session data
|
|
441
|
+
isLoading, // Loading state
|
|
442
|
+
isAuthenticated, // Boolean auth status
|
|
443
|
+
|
|
444
|
+
// Actions
|
|
445
|
+
signIn, // (email, password) => Promise
|
|
446
|
+
signUp, // (email, password, name) => Promise
|
|
447
|
+
signOut, // () => Promise
|
|
448
|
+
resetPassword, // (email) => Promise
|
|
449
|
+
updateProfile, // (data) => Promise
|
|
450
|
+
} = useAuth()
|
|
451
|
+
|
|
452
|
+
// Example: Sign in
|
|
453
|
+
const handleLogin = async () => {
|
|
454
|
+
try {
|
|
455
|
+
await signIn(email, password)
|
|
456
|
+
router.push('/dashboard')
|
|
457
|
+
} catch (error) {
|
|
458
|
+
setError(error.message)
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
## Protected Routes
|
|
465
|
+
|
|
466
|
+
### Middleware Protection
|
|
467
|
+
|
|
468
|
+
```typescript
|
|
469
|
+
// middleware.ts
|
|
470
|
+
export { auth as middleware } from "@/core/lib/auth"
|
|
471
|
+
|
|
472
|
+
export const config = {
|
|
473
|
+
matcher: [
|
|
474
|
+
'/dashboard/:path*',
|
|
475
|
+
'/profile/:path*',
|
|
476
|
+
'/api/user/:path*'
|
|
477
|
+
]
|
|
478
|
+
}
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
### Page-Level Protection
|
|
482
|
+
|
|
483
|
+
```typescript
|
|
484
|
+
// app/dashboard/page.tsx
|
|
485
|
+
import { auth } from '@/core/lib/auth'
|
|
486
|
+
import { redirect } from 'next/navigation'
|
|
487
|
+
|
|
488
|
+
export default async function DashboardPage() {
|
|
489
|
+
const session = await auth.api.getSession({
|
|
490
|
+
headers: headers()
|
|
491
|
+
})
|
|
492
|
+
|
|
493
|
+
if (!session) {
|
|
494
|
+
redirect('/login')
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
return <Dashboard user={session.user} />
|
|
498
|
+
}
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
## Error Codes
|
|
502
|
+
|
|
503
|
+
| Code | HTTP Status | Description |
|
|
504
|
+
|------|-------------|-------------|
|
|
505
|
+
| `AUTHENTICATION_REQUIRED` | 401 | No auth credentials provided |
|
|
506
|
+
| `INVALID_API_KEY` | 401 | API key invalid or expired |
|
|
507
|
+
| `INVALID_CREDENTIALS` | 401 | Wrong email/password |
|
|
508
|
+
| `EMAIL_NOT_VERIFIED` | 401 | User hasn't verified email |
|
|
509
|
+
| `INSUFFICIENT_PERMISSIONS` | 403 | User lacks required scope |
|
|
510
|
+
| `TEAM_CONTEXT_REQUIRED` | 400 | Missing x-team-id header |
|
|
511
|
+
| `SESSION_EXPIRED` | 401 | Session no longer valid |
|
|
512
|
+
|
|
513
|
+
## Testing Authentication
|
|
514
|
+
|
|
515
|
+
### Cypress Session Pattern
|
|
516
|
+
|
|
517
|
+
```typescript
|
|
518
|
+
describe('Authenticated Tests', () => {
|
|
519
|
+
beforeEach(() => {
|
|
520
|
+
cy.session('owner-session', () => {
|
|
521
|
+
cy.visit('/login')
|
|
522
|
+
cy.get('[data-cy="email-input"]').type('carlos.mendoza@tmt.dev')
|
|
523
|
+
cy.get('[data-cy="password-input"]').type('Test1234')
|
|
524
|
+
cy.get('[data-cy="login-submit"]').click()
|
|
525
|
+
cy.url().should('include', '/dashboard')
|
|
526
|
+
}, {
|
|
527
|
+
validate: () => {
|
|
528
|
+
cy.visit('/dashboard')
|
|
529
|
+
cy.url().should('include', '/dashboard')
|
|
530
|
+
}
|
|
531
|
+
})
|
|
532
|
+
})
|
|
533
|
+
|
|
534
|
+
it('should access protected page', () => {
|
|
535
|
+
cy.visit('/dashboard/tasks')
|
|
536
|
+
cy.get('[data-cy="tasks-table"]').should('exist')
|
|
537
|
+
})
|
|
538
|
+
})
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
### API Test Pattern
|
|
542
|
+
|
|
543
|
+
```typescript
|
|
544
|
+
describe('API Auth Tests', () => {
|
|
545
|
+
const API_KEY = Cypress.env('SUPERADMIN_API_KEY')
|
|
546
|
+
const TEAM_ID = 'team-tmt-001'
|
|
547
|
+
|
|
548
|
+
it('should authenticate with API key', () => {
|
|
549
|
+
cy.request({
|
|
550
|
+
method: 'GET',
|
|
551
|
+
url: '/api/v1/tasks',
|
|
552
|
+
headers: {
|
|
553
|
+
'Authorization': `Bearer ${API_KEY}`,
|
|
554
|
+
'x-team-id': TEAM_ID
|
|
555
|
+
}
|
|
556
|
+
}).then((response) => {
|
|
557
|
+
expect(response.status).to.eq(200)
|
|
558
|
+
})
|
|
559
|
+
})
|
|
560
|
+
|
|
561
|
+
it('should reject without API key', () => {
|
|
562
|
+
cy.request({
|
|
563
|
+
method: 'GET',
|
|
564
|
+
url: '/api/v1/tasks',
|
|
565
|
+
failOnStatusCode: false
|
|
566
|
+
}).then((response) => {
|
|
567
|
+
expect(response.status).to.eq(401)
|
|
568
|
+
})
|
|
569
|
+
})
|
|
570
|
+
})
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
## OAuth Configuration
|
|
574
|
+
|
|
575
|
+
### Google OAuth
|
|
576
|
+
|
|
577
|
+
```typescript
|
|
578
|
+
// core/lib/auth.ts
|
|
579
|
+
socialProviders: {
|
|
580
|
+
google: {
|
|
581
|
+
clientId: process.env.GOOGLE_CLIENT_ID!,
|
|
582
|
+
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
|
|
583
|
+
mapProfileToUser: (profile) => ({
|
|
584
|
+
email: profile.email,
|
|
585
|
+
name: profile.name,
|
|
586
|
+
image: profile.picture,
|
|
587
|
+
emailVerified: profile.email_verified
|
|
588
|
+
})
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
```
|
|
592
|
+
|
|
593
|
+
### Environment Variables
|
|
594
|
+
|
|
595
|
+
```env
|
|
596
|
+
# Better Auth
|
|
597
|
+
BETTER_AUTH_SECRET=your-secret-key
|
|
598
|
+
NEXT_PUBLIC_APP_URL=http://localhost:5173
|
|
599
|
+
|
|
600
|
+
# Google OAuth
|
|
601
|
+
GOOGLE_CLIENT_ID=your-client-id
|
|
602
|
+
GOOGLE_CLIENT_SECRET=your-client-secret
|
|
603
|
+
|
|
604
|
+
# Email (Resend)
|
|
605
|
+
RESEND_API_KEY=your-resend-key
|
|
606
|
+
RESEND_FROM_EMAIL=noreply@yourdomain.com
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
## Anti-Patterns
|
|
610
|
+
|
|
611
|
+
```typescript
|
|
612
|
+
// NEVER: Store passwords in plain text
|
|
613
|
+
user.password = 'plain-text-password'
|
|
614
|
+
|
|
615
|
+
// CORRECT: Use bcrypt via Better Auth
|
|
616
|
+
await auth.api.signUp({ email, password, name })
|
|
617
|
+
|
|
618
|
+
// NEVER: Skip email verification
|
|
619
|
+
if (!user.emailVerified) {
|
|
620
|
+
// Allow access anyway... WRONG!
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
// CORRECT: Require verification
|
|
624
|
+
if (!user.emailVerified) {
|
|
625
|
+
redirect('/verify-email')
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
// NEVER: Expose API keys in client code
|
|
629
|
+
const API_KEY = 'sk_live_xxxxx' // In browser!
|
|
630
|
+
|
|
631
|
+
// CORRECT: Use environment variables (server-side only)
|
|
632
|
+
const API_KEY = process.env.API_KEY // Server only
|
|
633
|
+
|
|
634
|
+
// NEVER: Skip team context for entity operations
|
|
635
|
+
// Missing x-team-id header
|
|
636
|
+
|
|
637
|
+
// CORRECT: Always include team context
|
|
638
|
+
headers: { 'x-team-id': teamId }
|
|
639
|
+
|
|
640
|
+
// NEVER: Trust client-provided user data
|
|
641
|
+
const userId = request.body.userId // User can fake this!
|
|
642
|
+
|
|
643
|
+
// CORRECT: Get user from authenticated session
|
|
644
|
+
const { user } = await authenticateRequest(request)
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
## Checklist
|
|
648
|
+
|
|
649
|
+
Before finalizing authentication code:
|
|
650
|
+
|
|
651
|
+
- [ ] Uses Better Auth for all auth operations
|
|
652
|
+
- [ ] Sessions use HttpOnly, Secure cookies
|
|
653
|
+
- [ ] Email verification required for email/password
|
|
654
|
+
- [ ] Password meets minimum requirements (8+ chars)
|
|
655
|
+
- [ ] API endpoints use dual auth pattern
|
|
656
|
+
- [ ] Protected routes redirect unauthenticated users
|
|
657
|
+
- [ ] Error responses use proper status codes
|
|
658
|
+
- [ ] Rate limiting implemented for sensitive endpoints
|
|
659
|
+
- [ ] OAuth profile mapping configured correctly
|
|
660
|
+
- [ ] Test users documented and working
|
|
661
|
+
|
|
662
|
+
## Related Skills
|
|
663
|
+
|
|
664
|
+
- `cypress-e2e` - UAT testing with session authentication
|
|
665
|
+
- `cypress-api` - API testing with API key authentication
|
|
666
|
+
- `nextjs-api-development` - API route patterns
|