- 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>
394 lines
6.7 KiB
Markdown
394 lines
6.7 KiB
Markdown
# 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.
|
|
|
|
```http
|
|
POST /auth/sign-up
|
|
```
|
|
|
|
**Request Body:**
|
|
```json
|
|
{
|
|
"email": "user@example.com",
|
|
"password": "SecurePass123!",
|
|
"name": "John Doe"
|
|
}
|
|
```
|
|
|
|
**Response (201):**
|
|
```json
|
|
{
|
|
"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 exists
|
|
- `422 Validation Error` — Invalid input
|
|
|
|
---
|
|
|
|
### Sign In
|
|
|
|
Authenticate with email and password.
|
|
|
|
```http
|
|
POST /auth/sign-in
|
|
```
|
|
|
|
**Request Body:**
|
|
```json
|
|
{
|
|
"email": "user@example.com",
|
|
"password": "SecurePass123!"
|
|
}
|
|
```
|
|
|
|
**Response (200):**
|
|
```json
|
|
{
|
|
"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.
|
|
|
|
```http
|
|
POST /auth/sign-out
|
|
```
|
|
|
|
**Headers:**
|
|
```http
|
|
Authorization: Bearer {session_token}
|
|
```
|
|
|
|
**Response (200):**
|
|
```json
|
|
{
|
|
"success": true
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### Get Session
|
|
|
|
Retrieve current session information.
|
|
|
|
```http
|
|
GET /auth/session
|
|
```
|
|
|
|
**Headers:**
|
|
```http
|
|
Authorization: Bearer {session_token}
|
|
```
|
|
|
|
**Response (200):**
|
|
```json
|
|
{
|
|
"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).
|
|
|
|
```http
|
|
GET /auth/profile
|
|
```
|
|
|
|
**Headers:**
|
|
```http
|
|
Authorization: Bearer {session_token}
|
|
```
|
|
|
|
**Response (200):**
|
|
```json
|
|
{
|
|
"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).
|
|
|
|
```http
|
|
GET /auth/callback/authentik
|
|
```
|
|
|
|
**Query Parameters:**
|
|
- `code` — Authorization code from provider
|
|
- `state` — 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:
|
|
|
|
```http
|
|
GET /api/tasks
|
|
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
|
|
```
|
|
|
|
### Token Storage
|
|
|
|
**Frontend (Browser):**
|
|
- Store in `httpOnly` cookie (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:**
|
|
```typescript
|
|
import { AuthSession } from '@mosaic/shared';
|
|
|
|
const isExpired = (session: AuthSession) => {
|
|
return new Date(session.session.expiresAt) < new Date();
|
|
};
|
|
```
|
|
|
|
**Refresh flow** (future implementation):
|
|
```http
|
|
POST /auth/refresh
|
|
```
|
|
|
|
## Error Responses
|
|
|
|
### 401 Unauthorized
|
|
|
|
```json
|
|
{
|
|
"error": {
|
|
"code": "UNAUTHORIZED",
|
|
"message": "Invalid or expired session token"
|
|
}
|
|
}
|
|
```
|
|
|
|
### 422 Validation Error
|
|
|
|
```json
|
|
{
|
|
"error": {
|
|
"code": "VALIDATION_ERROR",
|
|
"message": "Input validation failed",
|
|
"details": {
|
|
"email": "Invalid email format",
|
|
"password": "Must be at least 8 characters"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## Examples
|
|
|
|
### Sign Up
|
|
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
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:**
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
curl http://localhost:3001/auth/profile \
|
|
-H "Authorization: Bearer $TOKEN"
|
|
```
|
|
|
|
### Sign Out
|
|
|
|
```bash
|
|
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`:
|
|
|
|
```typescript
|
|
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](2-types.md) for complete type definitions.
|
|
|
|
## Next Steps
|
|
|
|
- **Configure OIDC** — [Configuration → Authentik](../../1-getting-started/3-configuration/2-authentik.md)
|
|
- **Review Types** — [Authentication Types](2-types.md)
|
|
- **Understand Architecture** — [Architecture → Authentication](../../3-architecture/2-authentication/1-betterauth.md)
|