Authentication Architecture for customers.dev
Overview
The customers.dev application uses Clerk's satellite domain architecture, authenticating through auth.do.dev as the primary authentication domain. This approach provides centralized authentication while maintaining a focused customer management application. The architecture uses Convex for data storage with Role-Based Access Control (RBAC) for user permissions.
Architecture Diagram
graph TB
subgraph "Primary Auth Domain"
AUTH[auth.do.dev<br/>Primary Clerk Domain]
CLERK_PRIMARY[Clerk Authentication]
end
subgraph "Satellite Domain"
CUSTOMERS[customers.dev<br/>Port 3015<br/>Customer Management]
end
subgraph "Data Layer"
CONVEX[Convex Cloud<br/>Single Project]
CUSTOMERS_DATA[Customers]
LISTS_DATA[Lists]
USERS_DATA[Users]
RBAC[RBAC System]
end
subgraph "External Services"
CLERK_API[Clerk API]
OAUTH[OAuth Providers<br/>Google, GitHub]
end
AUTH --> CLERK_PRIMARY
CLERK_PRIMARY --> CLERK_API
CLERK_API --> OAUTH
CUSTOMERS --> AUTH
CUSTOMERS --> CONVEX
CONVEX --> CUSTOMERS_DATA
CONVEX --> LISTS_DATA
CONVEX --> USERS_DATA
CONVEX --> RBACCore Components
1. Primary Authentication Domain (auth.do.dev)
Purpose: Central authentication hub managed by the do.dev infrastructure
Key Features:
- Handles all authentication flows (sign-in, sign-up, sign-out)
- Manages OAuth integrations (Google, GitHub)
- Email verification and OTP
- User onboarding
- Session management across satellite domains
Technology Stack:
- Next.js with App Router
- Clerk (Primary Domain Mode)
- Managed by do.dev infrastructure
2. Satellite Domain (customers.dev)
Purpose: Customer management application that relies on auth.do.dev for authentication
Key Features:
- Seamless SSO through auth.do.dev
- Shared session with do.dev authentication
- Role-based access control
- Protected routes with automatic redirects to auth.do.dev
- Local user context from Clerk
Technology Stack:
- Next.js 15.4+ with App Router
- Clerk (Satellite Mode)
- TypeScript 5.7.3 (Strict Mode)
- Tailwind CSS v4
- Convex Cloud for backend
3. Data Layer (Convex)
Purpose: Serverless backend for all data and API needs
Key Features:
- Single Convex project (no multi-project setup)
- Real-time queries and subscriptions
- HTTP Actions for REST API
- Convex auth integration with Clerk
- Schema-defined TypeScript types
Data Models:
- Customers: Customer information and metadata
- Lists: Customer list management (e.g., waitlists)
- Users: User profiles synced from Clerk
- Roles: RBAC system for permissions
Authentication Flow
Sign-In Flow
- User visits
customers.dev/dashboard - Middleware detects unauthenticated request
- Redirects to
auth.do.dev/sign-in?redirect_url=customers.dev/dashboard - User authenticates via Clerk on auth.do.dev
- Clerk syncs session to customers.dev (satellite)
- User redirected back to
customers.dev/dashboard - Convex validates Clerk JWT and loads user data
Sign-Out Flow
- User clicks sign out on customers.dev
- Clerk signs out user across all domains
- Session cleared on customers.dev
- User redirected to public page
New User Registration
- User redirected to
auth.do.dev/sign-up - User completes registration (email, OAuth, etc.)
- Clerk creates user account
- Webhook triggers Convex user creation
- Default role assigned via Convex
- User redirected to
customers.dev/dashboard
Role-Based Access Control (RBAC)
Role System
Implemented in Convex with the following roles:
- admin: Full access to all features and data
- user: Standard customer management access
- viewer: Read-only access to customer data
- waitlist: Limited access for new users pending approval
See ROLES.md for complete role definitions.
Permission Checking
// Example: Checking permissions in Convex query
export const getCustomers = query({
handler: async (ctx) => {
const user = await ctx.auth.getUserIdentity()
if (!user) throw new Error("Not authenticated")
const userRoles = await ctx.db
.query("users")
.filter(q => q.eq(q.field("clerkId"), user.subject))
.first()
if (!userRoles || !["admin", "user"].includes(userRoles.role)) {
throw new Error("Insufficient permissions")
}
return await ctx.db.query("customers").collect()
}
})Environment Configuration
Development Environment
# Clerk Configuration (Satellite Mode)
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_***
CLERK_SECRET_KEY=sk_test_***
# Satellite configuration pointing to local auth domain
NEXT_PUBLIC_CLERK_IS_SATELLITE=true
NEXT_PUBLIC_CLERK_DOMAIN=localhost:3005
NEXT_PUBLIC_CLERK_SIGN_IN_URL=http://localhost:3005/sign-in
NEXT_PUBLIC_CLERK_SIGN_UP_URL=http://localhost:3005/sign-up
CLERK_SATELLITE_URL=http://localhost:3015
# Redirect URLs
NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL=http://localhost:3015/dashboard
NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL=http://localhost:3015/dashboard
# Convex (Single Project)
NEXT_PUBLIC_CONVEX_URL=https://impartial-panda-219.convex.cloud
CONVEX_DEPLOY_KEY=dev:impartial-panda-219|***Production Environment
# Clerk Configuration (Satellite Mode)
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_live_***
CLERK_SECRET_KEY=sk_live_***
# Satellite configuration pointing to production auth domain
NEXT_PUBLIC_CLERK_IS_SATELLITE=true
NEXT_PUBLIC_CLERK_DOMAIN=auth.do.dev
NEXT_PUBLIC_CLERK_SIGN_IN_URL=https://auth.do.dev/sign-in
NEXT_PUBLIC_CLERK_SIGN_UP_URL=https://auth.do.dev/sign-up
CLERK_SATELLITE_URL=https://customers.dev
# Redirect URLs
NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL=https://customers.dev/dashboard
NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL=https://customers.dev/dashboard
# Convex (Single Project)
NEXT_PUBLIC_CONVEX_URL=https://impartial-panda-219.convex.cloud
CONVEX_DEPLOY_KEY=prod:***Security Considerations
Session Management
- Sessions managed by Clerk across all domains
- JWT tokens validated by both Clerk and Convex
- Automatic session refresh handled by Clerk SDK
Data Access
- All Convex queries validate Clerk authentication
- Role-based permissions enforced at database level
- User identity from Clerk JWT used for data filtering
API Security
- Convex HTTP Actions use API key authentication (separate from user auth)
- Rate limiting implemented in Convex actions
- Input validation using Convex validators
Deployment Architecture
Frontend (Vercel)
- Automatic deployments on push to main
- Environment variables configured in Vercel dashboard
- Edge functions for middleware
- Global CDN distribution
Backend (Convex Cloud)
- Automatic deployments via Convex CLI
- Real-time data synchronization
- Serverless HTTP Actions for REST API
- Built-in authentication with Clerk
Troubleshooting
Common Issues
-
Infinite Redirect Loop
- Ensure
CLERK_SATELLITE_URLmatches actual domain - Verify
NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URLis set correctly
- Ensure
-
Session Not Syncing
- Verify both domains use the same Clerk publishable key
- Check that
isSatelliteis set totruein ClerkProvider - Confirm
domainpoints to auth.do.dev
-
Convex Auth Fails
- Ensure
convex/auth.config.tspoints to correct primary domain - Verify Clerk JWT is being passed to Convex
- Check ConvexProviderWithClerk is being used (not ConvexProvider)
- Ensure
Related Documentation
- CLERK_SATELLITE_IMPLEMENTATION.md - Detailed satellite setup guide
- ROLES.md - Complete role definitions
- PROJECT_PRD.md - Product requirements and architecture