Skip to content

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 account
  • POST /api/auth/sign-in/email - Sign in with credentials
  • POST /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 organization
  • POST /api/auth/organization/switch - Switch active organization
  • GET /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 session
  • GET /api/auth/sessions - List all user sessions
  • DELETE /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 2FA
  • POST /api/auth/two-factor/verify-totp - Verify TOTP code
  • POST /api/auth/two-factor/disable - Disable 2FA

5. JWT Token Enrichment ​

Custom Vulcan plugin adds organization context to JWT tokens.

JWT Payload:

json
{
  "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 tokens
  • organization - Organizations/companies
  • member - 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:

  1. Client sends request with Authorization: Bearer <token>
  2. API Gateway forwards request to microservice
  3. Microservice validates JWT signature
  4. Microservice extracts organizationId from token
  5. 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):

csharp
// 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.

csharp
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:

bash
BETTER_AUTH_SECRET=<32+ character secret>
DATABASE_URL=postgresql://user:pass@host:5432/auth

Optional:

bash
BETTER_AUTH_URL=https://api.vulcan.se/api/auth
FRONTEND_URL=https://app.vulcan.se
PORT=3000
NODE_ENV=production

CORS 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:

json
{
  "email": "user@example.com",
  "password": "SecurePassword123!",
  "name": "John Doe"
}

Response: 201 Created

json
{
  "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:

json
{
  "email": "user@example.com",
  "password": "SecurePassword123!"
}

Response: 200 OK

json
{
  "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:

json
{
  "name": "Acme Construction AB",
  "slug": "acme-construction"
}

Response: 201 Created

json
{
  "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:

json
{
  "organizationId": "org-uuid"
}

Response: 200 OK

json
{
  "session": {
    "token": "new-jwt-with-org-context",
    "expiresAt": "2026-01-22T12:00:00Z"
  }
}

Development ​

Local Setup ​

bash
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 dev

Testing ​

bash
# Type check
npx tsc --noEmit

# Run tests (when added)
npm test

Deployment ​

Docker ​

bash
# 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-auth

Kubernetes ​

Deployed via Helm chart in /repos/vulcan-be-auth/helm/vulcan-be-auth

bash
# 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

json
{
  "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)

Want to integrate with auth? Check the Authentication API docs or explore API Gateway for routing details.

Built with VitePress | v1.2.0 | πŸš€ Week One Sprint