redesign fully scaffolded and web login works
This commit is contained in:
468
doc/auth.md
Normal file
468
doc/auth.md
Normal file
@@ -0,0 +1,468 @@
|
||||
# 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)
|
||||
Reference in New Issue
Block a user