Architecture¶
The COORDINATOR authentication system is built on a modular, framework-agnostic architecture using Better Auth as the core authentication framework.
System Design¶
flowchart TB
subgraph Clients
A[Web App]
B[Mobile App]
end
subgraph Auth Service
C[Hono Server]
D[Better Auth]
E[Drizzle ORM]
end
subgraph External
F[(PostgreSQL)]
G[Resend Email]
H[Stripe]
I[Go Backend]
end
A --> C
B --> C
C --> D
D --> E
E --> F
D --> G
D --> H
D --> I
Core Components¶
src/
├── lib/
│ ├── auth.tsx # Main Better Auth configuration
│ ├── db.ts # Database connection
│ ├── logger.ts # Logging configuration
│ └── resend.ts # Email service
├── server/
│ └── auth.ts # Authorization helper functions
└── index.ts # Hono server setup
Better Auth Configuration¶
The authentication system is configured in src/lib/auth.tsx with the following key settings:
- Base URL:
https://auth.getcoordinator.ai(configurable viaBASE_URLenv var) - Trusted Origins: Multiple domains including localhost, dev, and production environments
- Database Adapter: Drizzle adapter for PostgreSQL
- Session Management: JWT tokens with 7-day expiration
- Cross-Subdomain Cookies: Enabled for
.getcoordinator.aidomain
Organizational Structure¶
Hierarchy¶
flowchart TD
A[Organization] --> B[Members]
A --> C[Teams]
B --> D["Owner (full control)"]
B --> E["Admin (manage teams)"]
B --> F["Member (basic access)"]
C --> G[Team Members]
Organization Roles¶
Owner¶
- Full administrative control over the organization
- Can create, update, and delete teams
- Can manage organization members and their roles
- Can delete the organization
- Must maintain at least one owner per organization
Admin¶
- Can create and manage teams
- Can manage team members
- Can invite new organization members
- Cannot delete the organization or modify organization settings
Member¶
- Can access teams they are assigned to
- Can perform team-specific operations
- Cannot manage other team members or create teams
Authentication Methods¶
Email & Password¶
- Standard email/password authentication
- Password reset via email token
- Email verification required
Two-Factor Authentication (2FA)¶
- TOTP-based authentication
- Backup codes for recovery
- Optional per user
Passkeys¶
- WebAuthn-based authentication
- Platform-native biometric or PIN authentication
- Enhanced security without password dependency
Phone & Email OTP¶
- One-time passwords via SMS or email
- Configurable expiration windows
- Used for phone number verification
Session Management¶
Session Architecture¶
Sessions are stored in the database with the following context:
- Active Organization: Current organization context for the user
- Active Team: Current team context within the organization
- Impersonation: Support for admin impersonation (optional)
- Token: JWT-based session token with 7-day expiration
- IP Address & User Agent: Tracked for security auditing
Cookie Settings¶
session_token: SameSite=lax, Secure=truesession_data: SameSite=lax, Secure=truedont_remember: SameSite=lax, Secure=true
Cross-domain cookie support enables seamless authentication across .getcoordinator.ai subdomains.
Input Validation¶
Email Validation¶
- Regex pattern:
/^[^\s@]+@[^\s@]+\.[^\s@]+$/ - Applied to: User emails, invitation emails
- Prevents invalid email addresses from entering the system
Slug Validation¶
- Pattern:
/^[a-z0-9]+(?:-[a-z0-9]+)*$/ - Length: 3-63 characters
- Applied to: Organization slugs
- Ensures URL-friendly organization identifiers
Name Validation¶
- Organization names: 2-100 characters
- Team names: 2-100 characters
- Case-insensitive uniqueness checks
- Prevents duplicate or invalid names
Security Features¶
Protection Against Race Conditions¶
Race Condition Handling
The system uses database constraints as the source of truth for uniqueness. Application-level checks provide better UX but are not authoritative.
- Uniqueness Checks: Application-level checks for UX, database constraints as source of truth
- Limit Checks: Best-effort checks, database constraints/triggers recommended
- Member Addition: Retry logic for org membership checks during team creation
Database Constraints¶
Unique Constraints: - User email (globally unique) - User phone number (globally unique) - Organization slug (globally unique) - Session token (globally unique) - Organization name (case-insensitive, enforced in application)
Foreign Key Constraints: - Cascade deletes for related records - Prevents orphaned records
Indexes: - User lookups (email, phone) - Organization lookups (slug) - Member lookups (userId, organizationId) - Team member lookups (userId, teamId) - Session lookups (userId, token)
Rate Limiting¶
- Invitations: Maximum 50 per organization per 24 hours
- Password Reset: Token expires after 1 hour
- Invitations: Expire after 7 days
CORS Configuration¶
Allowed Origins:
- http://localhost:3000
- https://dev-app.getcoordinator.ai
- https://app.getcoordinator.ai
- https://auth.getcoordinator.ai
- https://dev.getcoordinator.ai
Allowed Methods: POST, GET, PUT, DELETE, OPTIONS
Credentials: Enabled
Backend Integration¶
The authentication system synchronizes with a Go backend service:
- Webhook Endpoint: Triggered after organization/team changes
- Service Account: Uses
INTERNAL_API_SECRETfor authentication - Sync Events: Organization creation, team management, member changes
This ensures the backend system maintains consistent organization and team data.
Environment Variables¶
Required Configuration
All environment variables must be set for the auth service to function properly.
| Variable | Description | Example |
|---|---|---|
DATABASE_URL |
PostgreSQL connection string | postgresql://... |
BASE_URL |
Auth service base URL | https://auth.getcoordinator.ai |
PORT |
Server port | 8787 |
FRONTEND_URL |
Frontend application URL | https://app.getcoordinator.ai |
GO_BACKEND_URL |
Go backend service URL | https://backend.getcoordinator.ai |
INTERNAL_API_SECRET |
Service-to-service auth secret | *** |
STRIPE_SECRET_KEY |
Stripe API key | sk_... |
STRIPE_WEBHOOK_SECRET |
Stripe webhook secret | whsec_... |
RESEND_API_KEY |
Resend email API key | re_... |