13 KiB
Authentication System Setup
This document describes the multi-channel authentication system for the Dexorder AI Gateway.
Overview
The gateway now implements a comprehensive authentication system using Better Auth with support for:
- ✅ Email/Password authentication
- ✅ Passkey/WebAuthn (passwordless biometric auth)
- ✅ JWT token-based sessions
- ✅ Multi-channel support (WebSocket, Telegram, REST API)
- ✅ PostgreSQL-based user management
- ✅ Secure password hashing with Argon2
Architecture
┌─────────────────────────────────────────────────────────────┐
│ Client Apps │
│ (Web, Mobile, CLI, Telegram, etc.) │
└────────────┬────────────────────────────────┬───────────────┘
│ │
│ HTTP/REST │ WebSocket
│ │
┌────────────▼────────────────────────────────▼───────────────┐
│ Gateway (Fastify) │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌─────────────────┐ │
│ │ Auth Routes │ │ WebSocket │ │ Telegram │ │
│ │ /auth/* │ │ Handler │ │ Handler │ │
│ └──────┬───────┘ └──────┬───────┘ └────────┬────────┘ │
│ │ │ │ │
│ └─────────────────┴────────────────────┘ │
│ │ │
│ ┌────────▼──────────┐ │
│ │ Auth Service │ │
│ │ (Better Auth) │ │
│ └────────┬──────────┘ │
│ │ │
└───────────────────────────┼────────────────────────────────┘
│
┌────────▼────────┐
│ PostgreSQL │
│ - users │
│ - sessions │
│ - passkeys │
│ - credentials │
└─────────────────┘
Database Schema
The authentication system uses the following PostgreSQL tables:
Core Tables
-
users - Core user accounts
id(PRIMARY KEY)email(UNIQUE)email_verifiednamecreated_at,updated_at
-
user_credentials - Password hashes
user_id(FOREIGN KEY → users.id)password_hash(Argon2)
-
sessions - JWT sessions
id(PRIMARY KEY)user_id(FOREIGN KEY → users.id)expires_atip_address,user_agent
-
passkeys - WebAuthn credentials
id(PRIMARY KEY)user_id(FOREIGN KEY → users.id)credential_id(UNIQUE)credential_public_keycounter,transports
-
verification_tokens - Email verification, password reset
identifier,token,expires_at
Integration Tables
-
user_licenses - User authorization & feature flags
user_id(FOREIGN KEY → users.id)license_type(free, pro, enterprise)features(JSONB)resource_limits(JSONB)
-
user_channel_links - Multi-channel support
user_id(FOREIGN KEY → users.id)channel_type(telegram, slack, discord, websocket)channel_user_id
Installation
- Install dependencies:
cd gateway
npm install
The following packages are added:
better-auth- Main authentication framework@simplewebauthn/server- WebAuthn/passkey support@simplewebauthn/browser- Client-side passkey helpers@fastify/jwt- JWT utilitiesargon2- Secure password hashing
- Apply database schema:
psql $DATABASE_URL -f schema.sql
- Configure secrets:
Copy secrets.example.yaml to your actual secrets file and update:
auth:
secret: "YOUR-SUPER-SECRET-KEY-HERE" # Generate with: openssl rand -base64 32
- Configure server:
Update config.yaml:
server:
base_url: http://localhost:3000 # Or your production URL
trusted_origins:
- http://localhost:3000
- http://localhost:5173 # Your web app
- https://yourdomain.com
API Endpoints
Authentication Routes
All Better Auth automatic routes are available at /api/auth/*:
POST /api/auth/sign-up/email- Register with email/passwordPOST /api/auth/sign-in/email- Sign in with email/passwordPOST /api/auth/sign-out- Sign outGET /api/auth/session- Get current sessionPOST /api/auth/passkey/register- Register passkeyPOST /api/auth/passkey/authenticate- Authenticate with passkey
Custom Routes (Simplified)
POST /auth/register- Register and auto sign-inPOST /auth/login- Sign inPOST /auth/logout- Sign outGET /auth/session- Get sessionGET /auth/health- Auth system health check
Example Usage
Register a new user
curl -X POST http://localhost:3000/auth/register \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"password": "SecurePassword123!",
"name": "John Doe"
}'
Response:
{
"success": true,
"userId": "user_1234567890_abc123",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
Sign in
curl -X POST http://localhost:3000/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"password": "SecurePassword123!"
}'
Response:
{
"success": true,
"userId": "user_1234567890_abc123",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
Connect to WebSocket with JWT
const ws = new WebSocket('ws://localhost:3000/ws/chat');
ws.addEventListener('open', () => {
// Send auth token in initial message
ws.send(JSON.stringify({
type: 'auth',
token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
}));
});
Or use Authorization header:
const ws = new WebSocket('ws://localhost:3000/ws/chat', {
headers: {
'Authorization': 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
}
});
Get current session
curl http://localhost:3000/auth/session \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
Passkey (WebAuthn) Support
Server Setup
Passkeys are automatically configured in better-auth-config.ts:
passkey({
rpName: 'Dexorder AI',
rpID: new URL(config.baseUrl).hostname,
origin: config.baseUrl,
})
Client-Side Integration
import { startRegistration, startAuthentication } from '@simplewebauthn/browser';
// 1. Register a passkey (user must be logged in)
async function registerPasskey(token: string) {
// Get options from server
const optionsResponse = await fetch('/auth/passkey/register/options', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`
}
});
const options = await optionsResponse.json();
// Start WebAuthn registration
const credential = await startRegistration(options);
// Send credential to server
const response = await fetch('/api/auth/passkey/register', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({ credential })
});
return response.json();
}
// 2. Authenticate with passkey
async function authenticateWithPasskey() {
// Get challenge from server
const optionsResponse = await fetch('/api/auth/passkey/authenticate/options');
const options = await optionsResponse.json();
// Start WebAuthn authentication
const credential = await startAuthentication(options);
// Verify with server
const response = await fetch('/auth/passkey/authenticate', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ credential })
});
const { token, userId } = await response.json();
return { token, userId };
}
Multi-Channel Support
WebSocket Authentication
The WebSocket handler (websocket-handler.ts) now properly verifies JWT tokens:
// User connects with JWT token in Authorization header
const authContext = await authenticator.authenticateWebSocket(request);
// Returns: { userId, sessionId, license, ... }
Telegram Bot Authentication
Users link their Telegram account via the user_channel_links table:
INSERT INTO user_channel_links (user_id, channel_type, channel_user_id)
VALUES ('user_1234567890_abc123', 'telegram', '987654321');
The authenticator.authenticateTelegram() method resolves the user from their Telegram ID.
API Authentication
All REST API calls use the Authorization: Bearer <token> header.
Security Considerations
Production Checklist
- Generate a strong random secret:
openssl rand -base64 32 - Enable email verification: Set
requireEmailVerification: true - Configure HTTPS only in production
- Set proper
trusted_originsfor CORS - Implement rate limiting (consider adding
@fastify/rate-limit) - Set up email service for password reset
- Configure session expiry based on security requirements
- Enable 2FA for sensitive operations
- Implement audit logging for auth events
- Set up monitoring for failed login attempts
Password Security
- Uses Argon2 (winner of Password Hashing Competition)
- Automatically salted and hashed by Better Auth
- Never stored or logged in plain text
JWT Security
- Tokens expire after 7 days (configurable)
- Sessions update every 24 hours
- Tokens signed with HMAC-SHA256
- Store secret in k8s secrets, never in code
Passkey Security
- Uses FIDO2/WebAuthn standards
- Hardware-backed authentication
- Phishing-resistant
- No passwords to leak or forget
Migration Guide
If you have existing users with a different auth system:
- Create users in new schema:
INSERT INTO users (id, email, email_verified, name)
SELECT user_id, email, true, name FROM old_users_table;
- Migrate licenses:
-- Ensure user_licenses references users.id
UPDATE user_licenses SET user_id = users.id WHERE ...;
- User must reset password or register passkey on first login with new system
Troubleshooting
"Authentication failed" on WebSocket
- Check that the JWT token is valid and not expired
- Verify the Authorization header format:
Bearer <token> - Check server logs for detailed error messages
"Invalid credentials" on login
- Verify the user exists in the
userstable - Check that
user_credentialshas a password_hash for the user - Passwords are case-sensitive
Passkey registration fails
- Check browser support for WebAuthn
- Verify HTTPS is enabled (required for WebAuthn in production)
- Check
rpIDmatches your domain - Ensure user is authenticated before registering passkey
Development Tips
Testing with dev user
A development user is created automatically:
// Email: dev@example.com
// User ID: dev-user-001
// License: pro
Generate a token for testing:
curl -X POST http://localhost:3000/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"dev@example.com","password":"<set-in-db>"}'
Inspecting tokens
# Decode JWT (header and payload only, signature verification needed)
echo "eyJhbGc..." | cut -d. -f2 | base64 -d | jq
Database queries
-- List all users
SELECT id, email, name, created_at FROM users;
-- List active sessions
SELECT s.id, s.user_id, u.email, s.expires_at
FROM sessions s
JOIN users u ON s.user_id = u.id
WHERE s.expires_at > NOW();
-- List passkeys
SELECT p.id, p.name, u.email, p.created_at
FROM passkeys p
JOIN users u ON p.user_id = u.id;
Future Enhancements
Potential additions to consider:
- OAuth providers (Google, GitHub, etc.)
- Magic link authentication
- Two-factor authentication (TOTP)
- Session management dashboard
- Audit log for security events
- IP-based restrictions
- Device management (trusted devices)
- Anonymous authentication for trials