469 lines
13 KiB
Markdown
469 lines
13 KiB
Markdown
# 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
|
|
|
|
1. **users** - Core user accounts
|
|
- `id` (PRIMARY KEY)
|
|
- `email` (UNIQUE)
|
|
- `email_verified`
|
|
- `name`
|
|
- `created_at`, `updated_at`
|
|
|
|
2. **user_credentials** - Password hashes
|
|
- `user_id` (FOREIGN KEY → users.id)
|
|
- `password_hash` (Argon2)
|
|
|
|
3. **sessions** - JWT sessions
|
|
- `id` (PRIMARY KEY)
|
|
- `user_id` (FOREIGN KEY → users.id)
|
|
- `expires_at`
|
|
- `ip_address`, `user_agent`
|
|
|
|
4. **passkeys** - WebAuthn credentials
|
|
- `id` (PRIMARY KEY)
|
|
- `user_id` (FOREIGN KEY → users.id)
|
|
- `credential_id` (UNIQUE)
|
|
- `credential_public_key`
|
|
- `counter`, `transports`
|
|
|
|
5. **verification_tokens** - Email verification, password reset
|
|
- `identifier`, `token`, `expires_at`
|
|
|
|
### Integration Tables
|
|
|
|
6. **user_licenses** - User authorization & feature flags
|
|
- `user_id` (FOREIGN KEY → users.id)
|
|
- `license_type` (free, pro, enterprise)
|
|
- `features` (JSONB)
|
|
- `resource_limits` (JSONB)
|
|
|
|
7. **user_channel_links** - Multi-channel support
|
|
- `user_id` (FOREIGN KEY → users.id)
|
|
- `channel_type` (telegram, slack, discord, websocket)
|
|
- `channel_user_id`
|
|
|
|
## Installation
|
|
|
|
1. **Install dependencies:**
|
|
|
|
```bash
|
|
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 utilities
|
|
- `argon2` - Secure password hashing
|
|
|
|
2. **Apply database schema:**
|
|
|
|
```bash
|
|
psql $DATABASE_URL -f schema.sql
|
|
```
|
|
|
|
3. **Configure secrets:**
|
|
|
|
Copy `secrets.example.yaml` to your actual secrets file and update:
|
|
|
|
```yaml
|
|
auth:
|
|
secret: "YOUR-SUPER-SECRET-KEY-HERE" # Generate with: openssl rand -base64 32
|
|
```
|
|
|
|
4. **Configure server:**
|
|
|
|
Update `config.yaml`:
|
|
|
|
```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/password
|
|
- `POST /api/auth/sign-in/email` - Sign in with email/password
|
|
- `POST /api/auth/sign-out` - Sign out
|
|
- `GET /api/auth/session` - Get current session
|
|
- `POST /api/auth/passkey/register` - Register passkey
|
|
- `POST /api/auth/passkey/authenticate` - Authenticate with passkey
|
|
|
|
### Custom Routes (Simplified)
|
|
|
|
- `POST /auth/register` - Register and auto sign-in
|
|
- `POST /auth/login` - Sign in
|
|
- `POST /auth/logout` - Sign out
|
|
- `GET /auth/session` - Get session
|
|
- `GET /auth/health` - Auth system health check
|
|
|
|
### Example Usage
|
|
|
|
#### Register a new user
|
|
|
|
```bash
|
|
curl -X POST http://localhost:3000/auth/register \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"email": "user@example.com",
|
|
"password": "SecurePassword123!",
|
|
"name": "John Doe"
|
|
}'
|
|
```
|
|
|
|
Response:
|
|
```json
|
|
{
|
|
"success": true,
|
|
"userId": "user_1234567890_abc123",
|
|
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
|
}
|
|
```
|
|
|
|
#### Sign in
|
|
|
|
```bash
|
|
curl -X POST http://localhost:3000/auth/login \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"email": "user@example.com",
|
|
"password": "SecurePassword123!"
|
|
}'
|
|
```
|
|
|
|
Response:
|
|
```json
|
|
{
|
|
"success": true,
|
|
"userId": "user_1234567890_abc123",
|
|
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
|
}
|
|
```
|
|
|
|
#### Connect to WebSocket with JWT
|
|
|
|
```javascript
|
|
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:
|
|
```javascript
|
|
const ws = new WebSocket('ws://localhost:3000/ws/chat', {
|
|
headers: {
|
|
'Authorization': 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
|
|
}
|
|
});
|
|
```
|
|
|
|
#### Get current session
|
|
|
|
```bash
|
|
curl http://localhost:3000/auth/session \
|
|
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
|
```
|
|
|
|
## Passkey (WebAuthn) Support
|
|
|
|
### Server Setup
|
|
|
|
Passkeys are automatically configured in `better-auth-config.ts`:
|
|
|
|
```typescript
|
|
passkey({
|
|
rpName: 'Dexorder AI',
|
|
rpID: new URL(config.baseUrl).hostname,
|
|
origin: config.baseUrl,
|
|
})
|
|
```
|
|
|
|
### Client-Side Integration
|
|
|
|
```typescript
|
|
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:
|
|
|
|
```typescript
|
|
// 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:
|
|
|
|
```sql
|
|
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_origins` for 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:
|
|
|
|
1. **Create users in new schema:**
|
|
```sql
|
|
INSERT INTO users (id, email, email_verified, name)
|
|
SELECT user_id, email, true, name FROM old_users_table;
|
|
```
|
|
|
|
2. **Migrate licenses:**
|
|
```sql
|
|
-- Ensure user_licenses references users.id
|
|
UPDATE user_licenses SET user_id = users.id WHERE ...;
|
|
```
|
|
|
|
3. **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 `users` table
|
|
- Check that `user_credentials` has 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 `rpID` matches your domain
|
|
- Ensure user is authenticated before registering passkey
|
|
|
|
## Development Tips
|
|
|
|
### Testing with dev user
|
|
|
|
A development user is created automatically:
|
|
|
|
```javascript
|
|
// Email: dev@example.com
|
|
// User ID: dev-user-001
|
|
// License: pro
|
|
```
|
|
|
|
Generate a token for testing:
|
|
```bash
|
|
curl -X POST http://localhost:3000/auth/login \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"email":"dev@example.com","password":"<set-in-db>"}'
|
|
```
|
|
|
|
### Inspecting tokens
|
|
|
|
```bash
|
|
# Decode JWT (header and payload only, signature verification needed)
|
|
echo "eyJhbGc..." | cut -d. -f2 | base64 -d | jq
|
|
```
|
|
|
|
### Database queries
|
|
|
|
```sql
|
|
-- 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
|
|
|
|
## References
|
|
|
|
- [Better Auth Documentation](https://better-auth.com/)
|
|
- [SimpleWebAuthn Guide](https://simplewebauthn.dev/)
|
|
- [WebAuthn Guide](https://webauthn.guide/)
|
|
- [FIDO Alliance](https://fidoalliance.org/)
|
|
- [Fastify Authentication](https://fastify.dev/docs/latest/Guides/Getting-Started/#your-first-plugin)
|