- Organized docs into numbered shelf/book/chapter/page structure - Created comprehensive README.md with project overview - Added Getting Started book (quick start, installation, configuration) - Added Development book (workflow, testing, type sharing) - Added Architecture book (design principles, PDA-friendly patterns) - Added API Reference book (conventions, authentication) - Moved TYPE-SHARING.md to proper location - Updated all cross-references in main README - Created docs/README.md as master index - Removed old QA automation reports - Removed deprecated SETUP.md (content split into new structure) Documentation structure follows Bookstack best practices: - Numbered books: 1-getting-started, 2-development, 3-architecture, 4-api - Numbered chapters and pages for ordering - Clear hierarchy and navigation - Cross-referenced throughout Complete documentation available at: docs/README.md Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
6.7 KiB
Authentication Endpoints
Complete reference for authentication API endpoints powered by BetterAuth.
Base URL
http://localhost:3001/auth
All authentication endpoints are prefixed with /auth.
Endpoints
Sign Up
Create a new user account with email and password.
POST /auth/sign-up
Request Body:
{
"email": "user@example.com",
"password": "SecurePass123!",
"name": "John Doe"
}
Response (201):
{
"user": {
"id": "user-uuid",
"email": "user@example.com",
"name": "John Doe",
"emailVerified": false
},
"session": {
"id": "session-uuid",
"token": "eyJhbGciOiJIUzI1NiIs...",
"expiresAt": "2026-01-29T12:00:00.000Z"
}
}
Errors:
409 Conflict— Email already exists422 Validation Error— Invalid input
Sign In
Authenticate with email and password.
POST /auth/sign-in
Request Body:
{
"email": "user@example.com",
"password": "SecurePass123!"
}
Response (200):
{
"user": {
"id": "user-uuid",
"email": "user@example.com",
"name": "John Doe"
},
"session": {
"id": "session-uuid",
"token": "eyJhbGciOiJIUzI1NiIs...",
"expiresAt": "2026-01-29T12:00:00.000Z"
}
}
Errors:
401 Unauthorized— Invalid credentials
Sign Out
Invalidate current session.
POST /auth/sign-out
Headers:
Authorization: Bearer {session_token}
Response (200):
{
"success": true
}
Get Session
Retrieve current session information.
GET /auth/session
Headers:
Authorization: Bearer {session_token}
Response (200):
{
"user": {
"id": "user-uuid",
"email": "user@example.com",
"name": "John Doe"
},
"session": {
"id": "session-uuid",
"expiresAt": "2026-01-29T12:00:00.000Z"
}
}
Errors:
401 Unauthorized— Invalid or expired session
Get Profile
Get authenticated user's profile (custom endpoint).
GET /auth/profile
Headers:
Authorization: Bearer {session_token}
Response (200):
{
"id": "user-uuid",
"email": "user@example.com",
"name": "John Doe",
"emailVerified": false
}
Errors:
401 Unauthorized— Not authenticated
OIDC Callback
OAuth callback handler for Authentik (and other OIDC providers).
GET /auth/callback/authentik
Query Parameters:
code— Authorization code from providerstate— CSRF protection token
This endpoint is called by the OIDC provider after successful authentication.
Response:
- Redirects to frontend with session token
Authentication Flow
Email/Password Flow
1. User submits credentials → POST /auth/sign-in
2. Server validates credentials
3. Server creates session
4. Server returns session token
5. Client stores token
6. Client includes token in subsequent requests
OIDC Flow
1. User clicks "Sign in with Authentik"
2. Frontend redirects to Authentik
3. User authenticates with Authentik
4. Authentik redirects to /auth/callback/authentik
5. Server exchanges code for tokens
6. Server creates/updates user
7. Server creates session
8. Server redirects to frontend with session token
Using Session Tokens
Include the session token in the Authorization header for all authenticated requests:
GET /api/tasks
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Token Storage
Frontend (Browser):
- Store in
httpOnlycookie (most secure) - Or
localStorage(less secure, XSS vulnerable)
Mobile/Desktop:
- Secure storage (Keychain on iOS, KeyStore on Android)
Token Expiration
Tokens expire after 24 hours (configurable via JWT_EXPIRATION).
Check expiration:
import { AuthSession } from '@mosaic/shared';
const isExpired = (session: AuthSession) => {
return new Date(session.session.expiresAt) < new Date();
};
Refresh flow (future implementation):
POST /auth/refresh
Error Responses
401 Unauthorized
{
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid or expired session token"
}
}
422 Validation Error
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Input validation failed",
"details": {
"email": "Invalid email format",
"password": "Must be at least 8 characters"
}
}
}
Examples
Sign Up
curl -X POST http://localhost:3001/auth/sign-up \
-H "Content-Type: application/json" \
-d '{
"email": "jane@example.com",
"password": "SecurePass123!",
"name": "Jane Doe"
}'
Sign In
curl -X POST http://localhost:3001/auth/sign-in \
-H "Content-Type: application/json" \
-d '{
"email": "jane@example.com",
"password": "SecurePass123!"
}'
Save the token from response:
TOKEN=$(curl -X POST http://localhost:3001/auth/sign-in \
-H "Content-Type: application/json" \
-d '{"email":"jane@example.com","password":"SecurePass123!"}' \
| jq -r '.session.token')
Get Profile
curl http://localhost:3001/auth/profile \
-H "Authorization: Bearer $TOKEN"
Sign Out
curl -X POST http://localhost:3001/auth/sign-out \
-H "Authorization: Bearer $TOKEN"
Security Considerations
Password Requirements
- Minimum 8 characters
- At least one uppercase letter
- At least one lowercase letter
- At least one number
Configure in apps/api/src/auth/auth.config.ts.
Rate Limiting
- Sign-up: 5 requests per hour per IP
- Sign-in: 10 requests per hour per IP (prevents brute force)
CSRF Protection
OIDC flow includes state parameter for CSRF protection.
Token Security
- Tokens are signed with
JWT_SECRET - Use strong secret (min 32 characters)
- Rotate secret regularly in production
- Never expose tokens in logs
TypeScript Types
All authentication types are available from @mosaic/shared:
import type {
AuthUser,
AuthSession,
LoginRequest,
LoginResponse,
} from '@mosaic/shared';
// Use in frontend
const login = async (req: LoginRequest): Promise<AuthSession> => {
const res = await fetch('/auth/sign-in', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(req),
});
return res.json();
};
// Use in backend
@Post('sign-in')
async signIn(@Body() request: LoginRequest): Promise<LoginResponse> {
// ...
}
See API Types for complete type definitions.
Next Steps
- Configure OIDC — Configuration → Authentik
- Review Types — Authentication Types
- Understand Architecture — Architecture → Authentication