Skip to content

Authentication Backend Implementation

Architecture

Authentication is handled by Better Auth integrated with AdonisJS. All auth routes are proxied through a single controller that converts between AdonisJS and Better Auth's Web API format.

Client Request → AdonisJS Router → BetterAuthController → Better Auth → Database/Redis

Key Components

Better Auth Configuration (app/lib/auth.ts)

  • Database: PostgreSQL connection pool
  • Secondary Storage: Redis for session caching
  • Authentication: Email/password with Argon2 hashing
  • Organizations: Multi-tenant workspace support
  • Sessions: 7-day expiry with 1-day update interval

Controller (app/controllers/better_auth_controller.ts)

Single handler that:

  1. Converts AdonisJS request → Web API Request
  2. Passes to Better Auth handler
  3. Converts Web API Response → AdonisJS response

Route Configuration (start/routes.ts)

typescript
// All auth routes handled by Better Auth
router.any('/auth/*', [BetterAuthController, 'handle'])

Data Models

User Model

typescript
class User {
  id: string // UUID primary key
  name: string | null // Display name
  email: string // Login identifier
  emailVerified: boolean // Email verification status
  image: string | null // Profile image URL
  createdAt: DateTime
  updatedAt: DateTime
}

Session Model

typescript
class Session {
  id: string // Session token
  userId: string // User reference
  expiresAt: DateTime // Session expiry
  ipAddress: string | null // Client IP
  userAgent: string | null // Client info
  activeOrganizationId: string | null // Current workspace
  createdAt: DateTime
  updatedAt: DateTime
}

Organization Models

typescript
class Organization {
  id: string // UUID
  ownerId: string // User who created it
  name: string // Organization name
  slug: string // URL slug
  logo: string | null // Logo URL
  metadata: string | null // Additional data
}

class Member {
  id: string // Membership record
  userId: string // User reference
  organizationId: string // Organization reference
  role: string // Role (admin, member, etc.)
  teamId: string | null // Optional team assignment
}

Security Implementation

Password Security

  • Hashing: Argon2 (industry standard)
  • Verification: Constant-time comparison
  • No plaintext storage: Passwords never stored unhashed

Session Security

  • HTTP-only cookies: JavaScript cannot access tokens
  • Secure cookies: HTTPS-only in production
  • Cross-subdomain: Works across app/api subdomains
  • Rate limiting: Built-in protection against brute force

Database Security

  • UUID primary keys: No sequential ID leaking
  • Connection pooling: Efficient database connections
  • Prepared statements: SQL injection protection

Redis Configuration

Used for session caching and secondary storage:

typescript
secondaryStorage: {
  get: async (key) => redis.get(key),
  set: async (key, value, ttl) => redis.set(key, value, 'EX', ttl),
  delete: async (key) => redis.del(key)
}

Benefits:

  • Fast session lookup: No database hit for every request
  • Automatic expiry: TTL-based session cleanup
  • Scalability: Shared state across multiple server instances

Organization Features

Multi-tenancy

  • Users can belong to multiple organizations
  • Each session tracks active organization
  • Role-based permissions per organization

Invitation System

  • Organization owners can invite users by email
  • Pending invitations stored in database
  • Email integration ready (TODO: implement email sending)

Team Support

  • Teams are sub-groups within organizations
  • Users can be assigned to specific teams
  • Enables department/project-based organization

Environment Configuration

Required environment variables:

bash
# Database
DB_HOST=localhost
DB_PORT=5433
DB_USER=jubiloop
DB_PASSWORD=password
DB_DATABASE=jubiloop

# Server
SERVER_URL=https://api.jubiloop.localhost
DOMAIN=jubiloop.localhost

# Security
ALLOWED_ORIGINS=https://app.jubiloop.localhost,https://jubiloop.localhost

API Endpoints

All authentication endpoints are prefixed with /auth/ and handled by Better Auth:

  • POST /auth/sign-up - User registration
  • POST /auth/sign-in - User login
  • POST /auth/sign-out - User logout
  • GET /auth/session - Get current session
  • Organization endpoints (managed by Better Auth plugin)

See API Reference for detailed endpoint documentation.

Database Naming Conventions

Better Auth is configured to use AdonisJS snake_case conventions:

  • email_verified instead of emailVerified
  • created_at instead of createdAt
  • user_id instead of userId

This ensures consistency with the rest of the AdonisJS application.

Future Enhancements

  • Email verification: Currently disabled, can be enabled
  • OAuth providers: Account model ready for Google/GitHub
  • Two-factor auth: Better Auth supports TOTP
  • Password reset: Email-based password recovery
  • Audit logging: Track authentication events

Built with ❤️ by the Jubiloop team