Skip to main content
The Starter Kit provides a complete authentication system that delegates to the Cloud API (https://api.devkit4ai.com), supporting end-user registration and universal login with JWT-based session management.

Authentication Architecture

The Starter Kit uses server actions defined in app/actions.ts to handle all authentication operations:
  • Server-Side Processing: All auth logic runs on the server to protect sensitive credentials
  • JWT Token Storage: Access tokens (30 min) and refresh tokens (7 days) stored in secure httpOnly cookies
  • Role-Based Registration: End users register via deployed project-mode applications
  • Universal Login: Single login flow for all user roles with automatic role-based redirects
  • Project-Scoped Authentication: End user requests include X-Project-ID header for project context
Backend Implementation:
  • FastAPI endpoint: POST /api/v1/auth/register (role determined from headers)
  • Command: RegisterUserCommand with email, password, role, project_id, full_name
  • Handler: RegisterUserHandler delegates to role-specific registration methods
  • Aggregate: UserActions.register() validates credentials and emits UserWasRegistered event
  • Projector: UserReadModelProjector rebuilds User table from events
(((REPLACE_THIS_WITH_IMAGE: auth-flow-diagram.png: Diagram showing authentication flow from registration to JWT storage to dashboard)))

Registration Flows

Unified Registration Endpoint

The backend uses a single /api/v1/auth/register endpoint that determines user role from request headers:
Backend Role Resolution:
  • X-Operator-Key header present → Creates DEVELOPER role
  • X-Developer-Key + X-Project-ID headers present → Creates END_USER role
  • No valid auth headers → Returns 400 error
The full_name field allows users to provide a display name for personalized UI elements like dashboard greetings.
(((REPLACE_THIS_WITH_IMAGE: registration-form-with-fullname.png: Screenshot of registration form showing email, password, and full name fields)))

End-User Registration

End users register through your deployed project-mode application with project-scoped access: Frontend Flow:
  1. User navigates to /register page
  2. Submits email, password, and optional full_name
  3. backendRegisterAction() validates input and constructs request
  4. Server action POSTs to /api/v1/auth/register with headers
  5. Backend creates user and returns JWT tokens
  6. Tokens stored in httpOnly cookies
  7. User redirected to /dashboard
Server Action:
Backend Implementation (10-step flow):
  1. Endpoint Receives Request: POST /api/v1/auth/register with X-Developer-Key and X-Project-ID headers
  2. Role Resolution: resolve_role_from_headers() returns UserRole.END_USER based on X-Developer-Key presence
  3. Project ID Validation: Parses X-Project-ID as UUID, validates format
  4. Developer Authentication: Verifies X-Developer-Key SHA-256 hash against DeveloperKey table
  5. Project Ownership Check: Queries Project table to verify developer owns project_id
  6. Email Availability Check: validate_email_availability() ensures email not taken within project scope
  7. Command Creation: Builds RegisterUserCommand with email, password, UserRole.END_USER, project_id, full_name
  8. Aggregate Registration: UserActions.register() validates password (min 8 chars, uppercase, lowercase, digit), hashes with bcrypt, emits UserWasRegistered event
  9. Event Persistence: EventSourcedRepository saves event to event_store table
  10. JWT Generation: Creates access_token (30 min expiry) and refresh_token (7 days) with HS256 algorithm
  11. Project Assignment: Handler calls _assign_user_to_project() to create ProjectUser record
  12. Email Verification: Emits EmailVerificationWasRequested event with 24h token
  13. Response: Returns RegistrationResponse with user data, project_id, access_token, refresh_token
Response Fields:
  • full_name: Optional display name provided during registration
  • project_id: UUID of the project the end user belongs to (required for END_USER)
  • is_active: Always false initially, requires email verification
  • access_token: JWT with claims: sub (user_id), type (“access”), exp (30 min), project_id
  • refresh_token: JWT with claims: sub (user_id), type (“refresh”), exp (7 days)
Database Schema:
Email Uniqueness Model:
  • END_USER emails must be unique within a project (enforced by unique constraint)
  • OPERATOR/DEVELOPER emails must be globally unique (enforced by partial index on NULL project_id)
  • Same email can exist as END_USER in multiple projects
  • END_USER email can coexist with OPERATOR/DEVELOPER email
(((REPLACE_THIS_WITH_IMAGE: end-user-registration-form.png: Screenshot of end-user registration form with full name field)))

Developer Registration

Developer registration is handled through the Cloud Admin console at devkit4ai.com or vibecoding.ad. The Starter Kit includes developer registration support for compatibility but redirects project mode users to Cloud Admin. In project mode, the developer registration page redirects to /login.
Developer registration flow (console/operator modes only): Frontend Flow:
  1. Developer navigates to /register/developer
  2. Submits email and password
  3. backendRegisterAction() sends request with role: "developer"
  4. Cloud API validates X-Operator-Key header
  5. Backend creates developer with auto-provisioning
  6. Returns provisioning bundle (project_id, developer_key, api_key)
  7. Stores provisioning in httpOnly cookie (24h TTL)
  8. Redirects to /register/developer/success?email=<email>
Server Action:
Backend Developer Provisioning: When a developer registers, the backend automatically provisions:
  1. Default Project: Created with name “Default Project”
  2. API Key: Generated for the project (prefix ak_ + 32 URL-safe chars via secrets.token_urlsafe)
  3. Developer Key: Generated and linked to project (prefix ak_ + 32 URL-safe chars, SHA-256 hashed)
Implementation:
Key Generation:
  • Format: ak_ + secrets.token_urlsafe(32) → 46 character string
  • Storage: SHA-256 hash in database, full key shown once
  • Key prefix changed from dk_ to ak_ in v1.5.0
(((REPLACE_THIS_WITH_IMAGE: developer-provisioning-credentials.png: Screenshot of provisioning credentials display page)))

Login Flow

Universal Login

The login page handles all user types with role-based redirects after authentication: Frontend Flow:
Backend Implementation (10-step flow):
  1. Endpoint Receives Request: POST /api/v1/auth/login with optional X-Project-ID header
  2. Command Creation: LoginUserCommand with email, password, project_id (if provided)
  3. User Lookup: Queries User table by email and project_id (for END_USER) or email only (for OPERATOR/DEVELOPER)
  4. Password Verification: Uses bcrypt via pwd_context.verify(password, user.hashed_password)
  5. Active Status Check: Validates user.is_active is True (email verified)
  6. Aggregate Loading: Reconstructs UserActions from event stream via from_events()
  7. Login Method: UserActions.login() emits UserWasLoggedIn event
  8. Event Persistence: EventSourcedRepository saves event to event_store
  9. JWT Generation: Creates access_token and refresh_token with HS256 algorithm
  10. Response: Returns TokenResponse with access_token, refresh_token, token_type
JWT Token Claims:
Token Creation:
Login Form Features:
  • Email and password validation
  • Return URL preservation with security validation
  • Error message display from query params
  • Link to registration page
  • 10 second timeout protection via AbortController
Role-Based Redirects:
(((REPLACE_THIS_WITH_IMAGE: login-form-interface.png: Screenshot of login form with email and password fields)))

Project-Scoped Authentication

End User Login Requirements: End users must provide project context for authentication:
Backend User Lookup:
Why Project Scoping?
  • Enables same email to exist as END_USER in multiple projects
  • Isolates user namespaces per project
  • Developer A’s end users cannot access Developer B’s project
  • JWT access tokens for END_USER include project_id claim
The X-Project-ID header is crucial for end user authentication. It ensures all requests are scoped to the correct project context. Without it, end user login will fail.

Return URL Handling

The login flow preserves the user’s intended destination with security validation:
Security Rules:
  • Only same-origin relative paths allowed
  • Must start with single forward slash /
  • Rejects // prefix (prevents open redirects to external sites)
  • Rejects backslashes and control characters
  • Maximum 2048 characters
  • URL-decoded before validation

JWT Token Management

Token Storage

Tokens stored in secure httpOnly cookies with protocol-based security:
Security Implementation:
httpOnly Cookie Benefits:
  • Not accessible via JavaScript (prevents XSS attacks)
  • Automatically sent with requests to same origin
  • Protected from client-side tampering
  • Server-side only access via cookies() from next/headers

Token Lifecycle

1. Registration/Login:
  • Backend generates both tokens with HS256 algorithm
  • Frontend stores in httpOnly cookies via storeTokensInCookies()
  • Cookies sent automatically with subsequent requests
2. API Requests:
3. Token Expiry:
  • Access token expires after 30 minutes (JWT exp claim)
  • Backend returns 401 Unauthorized for expired tokens
  • Frontend must use refresh token to obtain new access token
4. Token Refresh (Manual Implementation Required):
5. Logout:

Accessing Current User

Server Components:
getCurrentUser() Implementation:
React Cache Benefits:
  • Caches result per-request to avoid redundant API calls
  • Multiple calls to getCurrentUser() in same request cycle return same data
  • Cache automatically invalidated between requests
  • Fresh user data fetched on each new page load
Client Components:
User Data Structure:
The project_id field is only present for end users and identifies which project they belong to. Developers and operators do not have a project_id.

Personalized Greetings

Use full_name for personalized UI elements:
Output Examples:
  • With full_name: “Welcome, Sarah Johnson!”
  • Without full_name: “Welcome!”
(((REPLACE_THIS_WITH_IMAGE: dashboard-personalized-greeting.png: Screenshot of dashboard showing personalized greeting with user’s full name)))
requireAuth() Implementation:
getCurrentPath() Fallback Chain:
  1. x-invoke-path header (Next.js edge runtime)
  2. x-pathname header (custom header from middleware)
  3. x-url header (parse pathname and search)
  4. referer header (parse pathname and search)
  5. Default: /

Role-Based Protection

Use requireRole() to enforce role-based access control:
requireRole() Implementation:

Client-Side Protection

Use hooks for conditional rendering without redirects:
Auth Context Hooks:
Client-side hooks DO NOT redirect users. They only return null or false for unauthorized access. Use server-side requireAuth() or requireRole() for page-level protection with automatic redirects.
Frontend Validation:
Password Hashing:
  • Algorithm: bcrypt via passlib.context.CryptContext
  • Work factor: Default bcrypt rounds (2^12 iterations)
  • Stored in users.hashed_password column (VARCHAR 255)
  • Never logged or returned in API responses

Role-Based Headers

All API requests include role-specific headers for authentication and authorization: End User Requests (Project Mode):
Developer Requests (Console Mode):
Platform Operator Requests:
Header Resolution:
Developer keys and API keys changed from dk_ prefix to ak_ prefix in v1.5.0. The format is ak_ + 32 URL-safe characters generated by secrets.token_urlsafe(32).

Error Handling

Registration Errors

Common registration error scenarios with backend triggering conditions:
ErrorHTTP StatusCauseBackend ConditionResolution
”Email already registered”409 ConflictDuplicate accountvalidate_email_availability() finds existing user with same email+project_idUse existing account or contact support
”End user registration requires a developer key and project context”400 Bad RequestMissing developer keyX-Developer-Key header not present for END_USERConfigure DEVKIT4AI_DEVELOPER_KEY
”X-Project-ID header is required for END_USER registration”400 Bad RequestMissing project IDX-Project-ID header not present when X-Developer-Key providedConfigure DEVKIT4AI_PROJECT_ID
”Invalid X-Project-ID format. Must be a valid UUID.”400 Bad RequestMalformed project IDuuid.UUID(project_id_header) raises ValueErrorFix project ID format in env vars
”Project not found or you don’t have permission to add users to it”403 ForbiddenInvalid project ownershipProject query with developer_id + project_id returns NoneVerify project belongs to developer
”Password must be at least 8 characters long”500 InternalWeak passwordlen(password) < 8 in UserActions.register()Use stronger password
”Password must contain at least one uppercase letter”500 InternalMissing uppercasenot any(c.isupper() for c in password)Add uppercase letter
”Password must contain at least one lowercase letter”500 InternalMissing lowercasenot any(c.islower() for c in password)Add lowercase letter
”Password must contain at least one digit”500 InternalMissing digitnot any(c.isdigit() for c in password)Add digit
”Invalid email format”500 InternalBad emailEmail missing ’@’ or len < 5 in aggregateFix email format
”Network error”N/AAPI unreachableFetch throws network exceptionCheck NEXT_PUBLIC_API_URL
”Registration timed out”N/ARequest timeoutAbortController timeout after 10 secondsCheck backend availability
”Too many registration attempts”429 Too Many RequestsRate limitingBackend rate limiter triggeredWait and retry
”Operator key is not configured”N/AMissing operator keyDEVKIT4AI_OPERATOR_KEY not set for developer registrationConfigure operator key
”Provisioning data is missing”N/AIncomplete provisioningDeveloper registration response missing project_id/developer_key/api_keyContact platform support

Login Errors

Common login error scenarios with backend triggering conditions:
ErrorHTTP StatusCauseBackend ConditionResolution
”Invalid credentials”401 UnauthorizedWrong email/passwordpwd_context.verify() returns FalseCheck credentials or reset password
”Account not activated. Please check your email for the verification link.”401 UnauthorizedEmail not verifieduser.is_active == FalseCheck email for verification
”Session expired”401 UnauthorizedJWT token expiredJWT exp claim < current timeLog in again
”Request timeout”N/ARequest timeoutAbortController timeout after 10 secondsCheck backend availability
”Email and password are required”N/AMissing credentialsemail or password is emptyProvide both fields
”Application is not properly configured”N/AMissing backend URLNEXT_PUBLIC_API_URL not setConfigure backend URL
User not found401 UnauthorizedEmail doesn’t existUser query by email+project_id returns NoneCheck email or register
Project context missing401 UnauthorizedMissing X-Project-ID for END_USEREND_USER login without X-Project-ID headerConfigure project ID in project mode
Error Response Structure:

Frontend Error Handling

Provisioning Bundle

Provisioning bundles are only used for developer registration in console/operator modes. End users do not receive provisioning data. In project mode (Starter Kit), developer registration is disabled and redirects to Cloud Admin.
After developer registration, a provisioning bundle is stored temporarily in an httpOnly cookie:
Storage Implementation:
Cookie Security:
  • Name: devkit4ai-provisioning
  • Expiry: 24 hours (86400 seconds)
  • Flags: httpOnly (not accessible to JavaScript), secure (HTTPS only), sameSite=lax
  • Path: / (accessible to all routes)
Display on Success Page: The /register/developer/success page shows these credentials once using consumeProvisioningBundle():
One-Time Visibility:
Users must copy provisioning credentials before leaving the success page. The cookie is deleted after first read and credentials cannot be recovered.
(((REPLACE_THIS_WITH_IMAGE: developer-provisioning-credentials.png: Screenshot of provisioning credentials display page)))

Logout Flow

Sign out action clears all auth state and provisioning data:
Logout Process:
  1. Server action signOutAction() called from form
  2. clearTokensFromCookies() deletes all auth cookies
  3. User redirected to /login page
  4. No backend API call required (stateless JWT tokens)
Usage in Component:
What Gets Cleared:
  • devkit4ai-token: JWT access token
  • devkit4ai-refresh-token: JWT refresh token
  • devkit4ai-provisioning: Developer provisioning credentials (if present)
JWT tokens are stateless, so no backend invalidation is required. Clearing cookies on client side immediately revokes access. The backend cannot track or revoke issued tokens before expiration.

Security Best Practices

Never expose JWT tokens or API keys in client-side JavaScript. Always use httpOnly cookies for token storage and server-side environment variables for API keys.
Authentication Security:
  1. httpOnly Cookies for Token Storage
    • Prevents XSS attacks (JavaScript cannot access tokens)
    • Automatically sent with same-origin requests
    • Protected from client-side tampering
    • Implementation: All JWT tokens stored in httpOnly cookies via Next.js cookies() API
  2. CSRF Protection via SameSite
    • All cookies use sameSite: "lax" flag
    • Prevents cross-site request forgery attacks
    • Cookies not sent with cross-origin POST requests
    • Implementation: Set in cookie options for all auth cookies
  3. Return URL Validation
    • Sanitize return URLs via sanitizeReturnUrl() function
    • Only allow same-origin relative paths starting with /
    • Reject double-slash prefixes // (open redirect vulnerability)
    • Maximum 2048 characters to prevent abuse
    • Implementation: lib/return-url.ts with URL decoding and validation
  4. Request Timeout Protection
    • All fetch requests use AbortController with 10 second timeout
    • Prevents hanging requests and resource exhaustion
    • Implementation: AUTH_REQUEST_TIMEOUT constant in app/actions.ts
  5. Password Hashing with bcrypt
    • bcrypt work factor: 2^12 iterations (secure default)
    • Passwords never logged or returned in responses
    • Salt automatically generated per password
    • Implementation: passlib.context.CryptContext in backend
  6. Environment Variable Security
    • API keys stored in server-side environment variables only
    • Never exposed in client-side JavaScript or HTML
    • Next.js NEXT_PUBLIC_ prefix only for non-sensitive URLs
    • Implementation: .env.local file with strict access control
Event Sourcing Security:
  1. Immutable Event Log
    • All user actions recorded as immutable events in event_store table
    • Audit trail: UserWasRegistered, UserWasLoggedIn, DeveloperKeyWasGenerated
    • Events never modified or deleted
    • Implementation: EventSourcedRepository with append-only writes
  2. Project-Scoped Access Control
    • END_USER queries always include project_id filter
    • Email uniqueness enforced per project
    • Project ownership validated before user creation
    • Implementation: Database unique constraint + backend validation
Operational Security:
  1. Rate Limit Auth Endpoints (Backend Responsibility)
    • Prevent brute force attacks on login
    • Limit registration attempts per IP
    • Return 429 Too Many Requests status
    • Implementation: FastAPI rate limiting middleware (if configured)
  2. Monitor Failed Logins (Backend Responsibility)
    • Track suspicious activity patterns
    • Log failed authentication attempts
    • Alert on repeated failures from same IP
    • Implementation: Backend logging with UserWasLoggedIn event
  3. Rotate API Keys Regularly
    • Minimize exposure window if keys compromised
    • Developer can revoke and regenerate keys via console
    • Maximum 10 developer keys per developer (MAX_DEVELOPER_KEYS_PER_DEVELOPER)
    • Implementation: Backend developer keys management endpoints
  4. Email Verification Required
    • New accounts start with is_active: false
    • EmailVerificationWasRequested event emitted on registration
    • 24 hour verification token expiry
    • Implementation: Backend email verification flow (verification emails not yet sent)

Customization

Custom Registration Fields

Add additional fields to registration form beyond email, password, and full_name: Step 1: Extend Backend Request Model
Step 2: Update Database Schema
Step 3: Update UserWasRegistered Event
Step 4: Update Frontend Form
Step 5: Update Server Action

Custom Redirect Logic

Modify post-login redirects based on custom business logic: Backend Role Resolution:
Frontend Custom Redirects:

Email Verification

Email verification infrastructure is in place with is_active flags and EmailVerificationWasRequested events, but verification emails are not yet sent. This feature is planned for a future release.
Current Implementation:
  • All new users registered with is_active: false
  • EmailVerificationWasRequested event emitted with 24h token
  • Verification tokens stored in users table
  • Manual activation required (update is_active in database)
Planned Email Verification Flow:
  1. User registers → Backend emits EmailVerificationWasRequested event
  2. Email service (future) sends verification email with link
  3. User clicks link → GET /api/v1/auth/verify-email?token=…
  4. Backend validates token → Updates is_active: true → Emits UserWasActivated event
  5. User can now log in successfully
Current Workaround (Development):

Protected Routes

Secure pages with authentication guards

Role-Based Access

Implement role-based permissions

JWT Flow

Understand token lifecycle

User Dashboard

Build authenticated user experiences