Authentication Service β
Better Auth-based authentication with multi-tenant JWT support
Overview β
The Vulcan authentication service is built on Better Auth, a modern authentication library for TypeScript. It provides secure, scalable authentication with multi-tenancy built in from day one.
Tech Stack:
- Node.js + TypeScript
- Better Auth framework
- Hono web framework
- PostgreSQL database
- JWT tokens with organization context
Port: 3000 (accessed via gateway at /api/auth/*)
Architecture β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Clients β
β React Web | React Native Mobile β
ββββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β API Gateway (YARP) β
β /api/auth/* routes β
ββββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Auth Service (Port 3000) β
β βββββββββββββββ ββββββββββββββββ ββββββββββββββββββββββ β
β β Better Auth β βVulcan Tenant β β JWT Enrichment β β
β β Core β β Plugin β β (Org Context) β β
β βββββββββββββββ ββββββββββββββββ ββββββββββββββββββββββ β
ββββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β PostgreSQL (auth database) β
β user | session | account | organization | member β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββKey Features β
1. Email + Password Authentication β
Standard Better Auth email/password flow with secure password hashing.
Endpoints:
POST /api/auth/sign-up/email- Create new accountPOST /api/auth/sign-in/email- Sign in with credentialsPOST /api/auth/sign-out- Sign out current session
2. Multi-Tenancy (Organizations) β
Every user belongs to one or more organizations (companies). The organization context is embedded in JWT tokens.
Features:
- Create organizations
- Switch between organizations (without re-authentication)
- List user's organizations
- Organization-level permissions
Endpoints:
POST /api/auth/organization/create- Create new organizationPOST /api/auth/organization/switch- Switch active organizationGET /api/auth/organization/list- List user's organizations
3. Session Management β
Multi-device session support with the ability to view and revoke sessions.
Features:
- Multiple concurrent sessions
- Session expiration (7 days)
- Device tracking
- Session revocation
Endpoints:
GET /api/auth/session- Get current sessionGET /api/auth/sessions- List all user sessionsDELETE /api/auth/sessions/:id- Revoke specific session
4. Two-Factor Authentication (2FA) β
Time-based one-time passwords (TOTP) for additional security.
Features:
- QR code generation for authenticator apps
- Backup codes
- Required on sensitive operations
- Per-user 2FA settings
Endpoints:
POST /api/auth/two-factor/enable- Enable 2FAPOST /api/auth/two-factor/verify-totp- Verify TOTP codePOST /api/auth/two-factor/disable- Disable 2FA
5. JWT Token Enrichment β
Custom Vulcan plugin adds organization context to JWT tokens.
JWT Payload:
{
"sub": "user-uuid",
"email": "user@example.com",
"name": "John Doe",
"organizationId": "org-uuid",
"organizationName": "Acme Construction",
"role": "admin",
"iat": 1234567890,
"exp": 1234567899
}This allows microservices to:
- Know which organization data to query (multi-tenant filter)
- Apply organization-level permissions
- Audit actions by organization
- No additional database lookup needed
Database Schema β
Better Auth manages its own schema, which includes:
Core Tables:
user- User accounts (email, password hash, name)session- Active sessions (token, expiry, device info)account- OAuth accounts (if using social login)verification- Email verification tokensorganization- Organizations/companiesmember- Organization membership (user β organization)
Vulcan Extensions:
- Organization context in sessions
- Custom user metadata
- Organization-level settings
Integration with Microservices β
1. JWT Validation β
Each microservice validates JWT tokens from the auth service.
Process:
- Client sends request with
Authorization: Bearer <token> - API Gateway forwards request to microservice
- Microservice validates JWT signature
- Microservice extracts
organizationIdfrom token - Microservice applies global query filter:
WHERE organization_id = <organizationId>
2. Organization Context β
The organizationId from JWT is used for multi-tenant data isolation.
Example (.NET microservice):
// Global query filter applied to all entities
modelBuilder.Entity<TenantEntity>()
.HasQueryFilter(e =>
e.OrganizationId == _currentOrgId &&
!e.IsDeleted
);This ensures:
- Users only see their organization's data
- No accidental data leaks between organizations
- Simplified queries (no manual filters needed)
3. User Context Service β
Each microservice has a UserContextService that extracts user info from JWT.
public interface IUserContextService
{
Guid UserId { get; }
Guid OrganizationId { get; }
string Email { get; }
string Role { get; }
}Security Considerations β
Password Security β
- β Bcrypt hashing with salt
- β Password strength requirements
- β Rate limiting on login attempts
- β Account lockout after failed attempts
Token Security β
- β JWT with short expiration (15 minutes)
- β Refresh tokens for extended sessions
- β HttpOnly cookies (web)
- β Secure storage (mobile)
- β Token revocation support
Session Security β
- β CSRF protection
- β Session binding to IP/device
- β Session expiration (7 days)
- β Concurrent session limits
- β Session revocation API
Database Security β
- β Connection string in environment variables
- β Database credentials in Azure Key Vault
- β Encrypted connections (SSL)
- β Prepared statements (SQL injection protection)
Configuration β
Environment Variables β
Required:
BETTER_AUTH_SECRET=<32+ character secret>
DATABASE_URL=postgresql://user:pass@host:5432/authOptional:
BETTER_AUTH_URL=https://api.vulcan.se/api/auth
FRONTEND_URL=https://app.vulcan.se
PORT=3000
NODE_ENV=productionCORS Configuration β
Configured to allow requests from:
https://app.vulcan.se(production web)https://staging.vulcan.se(staging web)http://localhost:5173(local development)- Mobile app (via custom headers)
API Reference β
Sign Up β
Endpoint: POST /api/auth/sign-up/email
Request Body:
{
"email": "user@example.com",
"password": "SecurePassword123!",
"name": "John Doe"
}Response: 201 Created
{
"user": {
"id": "uuid",
"email": "user@example.com",
"name": "John Doe"
},
"session": {
"token": "jwt-token",
"expiresAt": "2026-01-22T12:00:00Z"
}
}Sign In β
Endpoint: POST /api/auth/sign-in/email
Request Body:
{
"email": "user@example.com",
"password": "SecurePassword123!"
}Response: 200 OK
{
"user": {
"id": "uuid",
"email": "user@example.com",
"name": "John Doe",
"organizationId": "org-uuid"
},
"session": {
"token": "jwt-token",
"expiresAt": "2026-01-22T12:00:00Z"
}
}Create Organization β
Endpoint: POST /api/auth/organization/create
Request Body:
{
"name": "Acme Construction AB",
"slug": "acme-construction"
}Response: 201 Created
{
"organization": {
"id": "org-uuid",
"name": "Acme Construction AB",
"slug": "acme-construction",
"createdAt": "2026-01-15T10:00:00Z"
}
}Switch Organization β
Endpoint: POST /api/auth/organization/switch
Request Body:
{
"organizationId": "org-uuid"
}Response: 200 OK
{
"session": {
"token": "new-jwt-with-org-context",
"expiresAt": "2026-01-22T12:00:00Z"
}
}Development β
Local Setup β
cd repos/vulcan-be-auth
# Install dependencies
npm install
# Set environment variables
cp .env.example .env
# Edit .env with your values
# Run database migrations (if any)
npm run migrate
# Start development server
npm run devTesting β
# Type check
npx tsc --noEmit
# Run tests (when added)
npm testDeployment β
Docker β
# Build image
docker build -t vulcan-be-auth .
# Run container
docker run -p 3000:3000 \
-e BETTER_AUTH_SECRET=your-secret \
-e DATABASE_URL=postgresql://... \
vulcan-be-authKubernetes β
Deployed via Helm chart in /repos/vulcan-be-auth/helm/vulcan-be-auth
# Deploy to AKS
helm upgrade --install vulcan-be-auth ./helm/vulcan-be-auth \
--namespace vulcan \
--set image.tag=latest \
--set secrets.betterAuthSecret=<secret> \
--set secrets.databaseUrl=<url>Monitoring β
Health Checks β
Endpoint: GET /api/auth/health
Response: 200 OK
{
"status": "healthy",
"database": "connected",
"uptime": 3600
}Metrics β
- Request count by endpoint
- Authentication success/failure rate
- Token validation performance
- Session creation/revocation rate
- Database connection pool status
Future Enhancements β
Planned Features β
- [ ] OAuth providers (Google, Microsoft)
- [ ] Passwordless login (magic links)
- [ ] Biometric authentication (WebAuthn)
- [ ] SSO for enterprise customers
- [ ] Advanced audit logging
- [ ] IP-based restrictions
- [ ] Device management (trusted devices)
