Files
dashboard/API_DOCUMENTATION.md
Matthias Hochmeister f09748f4a1 inital
2026-02-23 17:08:58 +01:00

716 lines
14 KiB
Markdown

# API Documentation
Complete REST API documentation for the Feuerwehr Dashboard backend.
## Table of Contents
- [Base URL](#base-url)
- [Authentication](#authentication)
- [Rate Limiting](#rate-limiting)
- [Response Format](#response-format)
- [Error Codes](#error-codes)
- [Health Check](#health-check)
- [Authentication Endpoints](#authentication-endpoints)
- [User Endpoints](#user-endpoints)
- [Request Examples](#request-examples)
## Base URL
### Development
```
http://localhost:3000
```
### Production
```
https://api.yourdomain.com
```
## Authentication
The API uses JWT (JSON Web Token) bearer authentication for protected endpoints.
### Authentication Header Format
```http
Authorization: Bearer <your-jwt-token>
```
### How to Get Tokens
1. User authenticates via Authentik OAuth flow
2. Frontend receives authorization code
3. Frontend sends code to `/api/auth/callback`
4. Backend returns `accessToken` and `refreshToken`
5. Frontend includes `accessToken` in subsequent requests
### Token Expiration
- **Access Token**: 1 hour (3600 seconds)
- **Refresh Token**: 24 hours (86400 seconds)
Use `/api/auth/refresh` endpoint to get a new access token before it expires.
## Rate Limiting
All `/api/*` endpoints are rate-limited to prevent abuse.
### Limits
- **Window**: 15 minutes
- **Max Requests**: 100 requests per IP
### Rate Limit Headers
```http
RateLimit-Limit: 100
RateLimit-Remaining: 95
RateLimit-Reset: 1645564800
```
### Rate Limit Exceeded Response
```http
HTTP/1.1 429 Too Many Requests
```
```json
{
"success": false,
"message": "Too many requests from this IP, please try again later."
}
```
## Response Format
All API responses follow a consistent format:
### Success Response
```json
{
"success": true,
"message": "Operation completed successfully",
"data": {
// Response data here
}
}
```
### Error Response
```json
{
"success": false,
"message": "Error description",
"error": "Optional error details"
}
```
## Error Codes
### HTTP Status Codes
| Code | Meaning | Description |
|------|---------|-------------|
| 200 | OK | Request successful |
| 201 | Created | Resource created successfully |
| 400 | Bad Request | Invalid request parameters |
| 401 | Unauthorized | Authentication required or failed |
| 403 | Forbidden | Authenticated but not authorized |
| 404 | Not Found | Resource not found |
| 429 | Too Many Requests | Rate limit exceeded |
| 500 | Internal Server Error | Server error occurred |
### Common Error Messages
```json
// Missing authentication
{
"success": false,
"message": "No token provided"
}
// Invalid token
{
"success": false,
"message": "Invalid or expired token"
}
// Inactive user
{
"success": false,
"message": "User account is inactive"
}
```
## Health Check
Check if the API is running and healthy.
### GET /health
**Authentication**: Not required
**Request**:
```http
GET /health HTTP/1.1
Host: api.yourdomain.com
```
**Response**:
```http
HTTP/1.1 200 OK
Content-Type: application/json
```
```json
{
"status": "ok",
"timestamp": "2026-02-23T10:30:00.000Z",
"uptime": 3600.5,
"environment": "production"
}
```
**Response Fields**:
- `status` (string): Always "ok" if server is running
- `timestamp` (string): Current server time in ISO 8601 format
- `uptime` (number): Server uptime in seconds
- `environment` (string): Current environment (development/production)
## Authentication Endpoints
### POST /api/auth/callback
Handle OAuth callback and exchange authorization code for tokens.
**Authentication**: Not required
**Request Body**:
```json
{
"code": "authorization_code_from_authentik"
}
```
**Request Example**:
```http
POST /api/auth/callback HTTP/1.1
Host: api.yourdomain.com
Content-Type: application/json
```
**Success Response**:
```http
HTTP/1.1 200 OK
Content-Type: application/json
```
```json
{
"success": true,
"message": "Authentication successful",
"data": {
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": 1,
"email": "john.doe@feuerwehr.de",
"name": "John Doe",
"preferredUsername": "john.doe",
"givenName": "John",
"familyName": "Doe",
"profilePictureUrl": "https://auth.example.com/media/avatars/john.jpg",
"isActive": true
}
}
}
```
**Error Responses**:
```http
HTTP/1.1 400 Bad Request
```
```json
{
"success": false,
"message": "Authorization code is required"
}
```
```http
HTTP/1.1 403 Forbidden
```
```json
{
"success": false,
"message": "User account is inactive"
}
```
```http
HTTP/1.1 500 Internal Server Error
```
```json
{
"success": false,
"message": "Authentication failed"
}
```
**Response Fields**:
- `accessToken` (string): JWT access token for API authentication
- `refreshToken` (string): JWT refresh token for getting new access tokens
- `user` (object): User profile information
- `id` (number): User database ID
- `email` (string): User email address
- `name` (string): Full name
- `preferredUsername` (string): Preferred username
- `givenName` (string): First name
- `familyName` (string): Last name
- `profilePictureUrl` (string): URL to profile picture
- `isActive` (boolean): Whether user account is active
---
### POST /api/auth/refresh
Refresh an expired access token using a refresh token.
**Authentication**: Not required (uses refresh token)
**Request Body**:
```json
{
"refreshToken": "your_refresh_token"
}
```
**Request Example**:
```http
POST /api/auth/refresh HTTP/1.1
Host: api.yourdomain.com
Content-Type: application/json
```
**Success Response**:
```http
HTTP/1.1 200 OK
Content-Type: application/json
```
```json
{
"success": true,
"message": "Token refreshed successfully",
"data": {
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
}
```
**Error Responses**:
```http
HTTP/1.1 400 Bad Request
```
```json
{
"success": false,
"message": "Refresh token is required"
}
```
```http
HTTP/1.1 401 Unauthorized
```
```json
{
"success": false,
"message": "Invalid refresh token"
}
```
```http
HTTP/1.1 403 Forbidden
```
```json
{
"success": false,
"message": "User account is inactive"
}
```
**Response Fields**:
- `accessToken` (string): New JWT access token
---
### POST /api/auth/logout
Logout the current user.
**Authentication**: Optional (for logging purposes)
**Request Headers**:
```http
Authorization: Bearer <access-token>
```
**Request Example**:
```http
POST /api/auth/logout HTTP/1.1
Host: api.yourdomain.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
```
**Success Response**:
```http
HTTP/1.1 200 OK
Content-Type: application/json
```
```json
{
"success": true,
"message": "Logout successful"
}
```
**Note**: Since this API uses stateless JWT authentication, logout is primarily handled client-side by discarding the tokens. This endpoint exists for audit logging purposes.
---
## User Endpoints
### GET /api/user/me
Get the currently authenticated user's profile.
**Authentication**: Required
**Request Headers**:
```http
Authorization: Bearer <access-token>
```
**Request Example**:
```http
GET /api/user/me HTTP/1.1
Host: api.yourdomain.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
```
**Success Response**:
```http
HTTP/1.1 200 OK
Content-Type: application/json
```
```json
{
"success": true,
"data": {
"id": 1,
"email": "john.doe@feuerwehr.de",
"name": "John Doe",
"preferredUsername": "john.doe",
"givenName": "John",
"familyName": "Doe",
"profilePictureUrl": "https://auth.example.com/media/avatars/john.jpg",
"isActive": true,
"lastLoginAt": "2026-02-23T10:30:00.000Z",
"createdAt": "2026-01-01T12:00:00.000Z"
}
}
```
**Error Responses**:
```http
HTTP/1.1 401 Unauthorized
```
```json
{
"success": false,
"message": "Not authenticated"
}
```
```http
HTTP/1.1 404 Not Found
```
```json
{
"success": false,
"message": "User not found"
}
```
**Response Fields**:
- `id` (number): User database ID
- `email` (string): User email address
- `name` (string): Full name
- `preferredUsername` (string): Preferred username
- `givenName` (string): First name
- `familyName` (string): Last name
- `profilePictureUrl` (string): URL to profile picture
- `isActive` (boolean): Whether user account is active
- `lastLoginAt` (string): Last login timestamp (ISO 8601)
- `createdAt` (string): Account creation timestamp (ISO 8601)
---
## Request Examples
### Full Authentication Flow Example
#### Step 1: User Clicks Login
Frontend redirects to Authentik:
```javascript
const authentikAuthUrl = `https://auth.yourdomain.com/application/o/authorize/`;
const params = new URLSearchParams({
client_id: 'your_client_id',
redirect_uri: 'https://dashboard.yourdomain.com/auth/callback',
response_type: 'code',
scope: 'openid profile email'
});
window.location.href = `${authentikAuthUrl}?${params}`;
```
#### Step 2: Authentik Redirects Back
After authentication, Authentik redirects to:
```
https://dashboard.yourdomain.com/auth/callback?code=abc123def456
```
#### Step 3: Exchange Code for Tokens
```bash
curl -X POST https://api.yourdomain.com/api/auth/callback \
-H "Content-Type: application/json" \
-d '{
"code": "abc123def456"
}'
```
Response:
```json
{
"success": true,
"message": "Authentication successful",
"data": {
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEsImVtYWlsIjoiam9obkBleGFtcGxlLmNvbSIsImlhdCI6MTcwODY5MTQwMCwiZXhwIjoxNzA4Njk1MDAwfQ.signature",
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEsImVtYWlsIjoiam9obkBleGFtcGxlLmNvbSIsImlhdCI6MTcwODY5MTQwMCwiZXhwIjoxNzA4Nzc3ODAwfQ.signature",
"user": {
"id": 1,
"email": "john.doe@feuerwehr.de",
"name": "John Doe",
"preferredUsername": "john.doe",
"givenName": "John",
"familyName": "Doe",
"profilePictureUrl": null,
"isActive": true
}
}
}
```
#### Step 4: Access Protected Resources
```bash
curl -X GET https://api.yourdomain.com/api/user/me \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
```
#### Step 5: Refresh Token When Expired
```bash
curl -X POST https://api.yourdomain.com/api/auth/refresh \
-H "Content-Type: application/json" \
-d '{
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}'
```
### JavaScript/TypeScript Examples
#### Using Axios
```typescript
import axios from 'axios';
const API_URL = 'https://api.yourdomain.com';
// Create axios instance with auth
const api = axios.create({
baseURL: API_URL,
headers: {
'Content-Type': 'application/json',
},
});
// Add auth token to requests
api.interceptors.request.use((config) => {
const token = localStorage.getItem('accessToken');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
// Handle authentication
export const handleCallback = async (code: string) => {
const response = await api.post('/api/auth/callback', { code });
const { accessToken, refreshToken, user } = response.data.data;
// Store tokens
localStorage.setItem('accessToken', accessToken);
localStorage.setItem('refreshToken', refreshToken);
return user;
};
// Refresh token
export const refreshAccessToken = async () => {
const refreshToken = localStorage.getItem('refreshToken');
const response = await api.post('/api/auth/refresh', { refreshToken });
const { accessToken } = response.data.data;
localStorage.setItem('accessToken', accessToken);
return accessToken;
};
// Get current user
export const getCurrentUser = async () => {
const response = await api.get('/api/user/me');
return response.data.data;
};
// Logout
export const logout = async () => {
await api.post('/api/auth/logout');
localStorage.removeItem('accessToken');
localStorage.removeItem('refreshToken');
};
```
### cURL Examples
#### Health Check
```bash
curl -X GET https://api.yourdomain.com/health
```
#### Login Callback
```bash
curl -X POST https://api.yourdomain.com/api/auth/callback \
-H "Content-Type: application/json" \
-d '{"code":"your_auth_code"}'
```
#### Get Current User
```bash
curl -X GET https://api.yourdomain.com/api/user/me \
-H "Authorization: Bearer your_access_token"
```
#### Refresh Token
```bash
curl -X POST https://api.yourdomain.com/api/auth/refresh \
-H "Content-Type: application/json" \
-d '{"refreshToken":"your_refresh_token"}'
```
#### Logout
```bash
curl -X POST https://api.yourdomain.com/api/auth/logout \
-H "Authorization: Bearer your_access_token"
```
## Security Considerations
### HTTPS Required in Production
Always use HTTPS for API requests in production to protect tokens and sensitive data.
### Token Storage
- **Access Token**: Store in memory or sessionStorage (more secure)
- **Refresh Token**: Can be stored in httpOnly cookie or localStorage
- **Never**: Store tokens in unencrypted cookies or URL parameters
### CORS Configuration
The API is configured to only accept requests from allowed origins:
```javascript
// Backend CORS configuration
cors({
origin: process.env.CORS_ORIGIN, // e.g., https://dashboard.yourdomain.com
credentials: true
})
```
Ensure `CORS_ORIGIN` environment variable matches your frontend URL exactly.
### Rate Limiting
Respect rate limits to avoid being temporarily blocked. Implement exponential backoff for failed requests.
## Additional Notes
### Timestamps
All timestamps are in ISO 8601 format with UTC timezone:
```
2026-02-23T10:30:00.000Z
```
### JSON Format
All request bodies and responses use JSON format with `Content-Type: application/json`.
### Versioning
API versioning is currently not implemented. All endpoints are under `/api/` prefix.
Future versions may use:
- `/api/v1/` for version 1
- `/api/v2/` for version 2
## Support
For API support:
1. Check this documentation
2. Review [DEVELOPMENT.md](DEVELOPMENT.md) for debugging tips
3. Check backend logs for detailed error messages
4. Consult [ARCHITECTURE.md](ARCHITECTURE.md) for system design
5. Create an issue with detailed request/response information
## Changelog
### Version 1.0.0 (2026-02-23)
Initial API release:
- OAuth 2.0 authentication with Authentik
- JWT-based session management
- User profile endpoints
- Health check endpoint
- Rate limiting
- Security headers