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

14 KiB

API Documentation

Complete REST API documentation for the Feuerwehr Dashboard backend.

Table of Contents

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

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

RateLimit-Limit: 100
RateLimit-Remaining: 95
RateLimit-Reset: 1645564800

Rate Limit Exceeded Response

HTTP/1.1 429 Too Many Requests
{
  "success": false,
  "message": "Too many requests from this IP, please try again later."
}

Response Format

All API responses follow a consistent format:

Success Response

{
  "success": true,
  "message": "Operation completed successfully",
  "data": {
    // Response data here
  }
}

Error Response

{
  "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

// 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:

GET /health HTTP/1.1
Host: api.yourdomain.com

Response:

HTTP/1.1 200 OK
Content-Type: application/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:

{
  "code": "authorization_code_from_authentik"
}

Request Example:

POST /api/auth/callback HTTP/1.1
Host: api.yourdomain.com
Content-Type: application/json

{
  "code": "abc123def456ghi789"
}

Success Response:

HTTP/1.1 200 OK
Content-Type: application/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/1.1 400 Bad Request
{
  "success": false,
  "message": "Authorization code is required"
}
HTTP/1.1 403 Forbidden
{
  "success": false,
  "message": "User account is inactive"
}
HTTP/1.1 500 Internal Server Error
{
  "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:

{
  "refreshToken": "your_refresh_token"
}

Request Example:

POST /api/auth/refresh HTTP/1.1
Host: api.yourdomain.com
Content-Type: application/json

{
  "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Success Response:

HTTP/1.1 200 OK
Content-Type: application/json
{
  "success": true,
  "message": "Token refreshed successfully",
  "data": {
    "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
  }
}

Error Responses:

HTTP/1.1 400 Bad Request
{
  "success": false,
  "message": "Refresh token is required"
}
HTTP/1.1 401 Unauthorized
{
  "success": false,
  "message": "Invalid refresh token"
}
HTTP/1.1 403 Forbidden
{
  "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:

Authorization: Bearer <access-token>

Request Example:

POST /api/auth/logout HTTP/1.1
Host: api.yourdomain.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Success Response:

HTTP/1.1 200 OK
Content-Type: application/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:

Authorization: Bearer <access-token>

Request Example:

GET /api/user/me HTTP/1.1
Host: api.yourdomain.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Success Response:

HTTP/1.1 200 OK
Content-Type: application/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/1.1 401 Unauthorized
{
  "success": false,
  "message": "Not authenticated"
}
HTTP/1.1 404 Not Found
{
  "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:

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

curl -X POST https://api.yourdomain.com/api/auth/callback \
  -H "Content-Type: application/json" \
  -d '{
    "code": "abc123def456"
  }'

Response:

{
  "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

curl -X GET https://api.yourdomain.com/api/user/me \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Step 5: Refresh Token When Expired

curl -X POST https://api.yourdomain.com/api/auth/refresh \
  -H "Content-Type: application/json" \
  -d '{
    "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
  }'

JavaScript/TypeScript Examples

Using Axios

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

curl -X GET https://api.yourdomain.com/health

Login Callback

curl -X POST https://api.yourdomain.com/api/auth/callback \
  -H "Content-Type: application/json" \
  -d '{"code":"your_auth_code"}'

Get Current User

curl -X GET https://api.yourdomain.com/api/user/me \
  -H "Authorization: Bearer your_access_token"

Refresh Token

curl -X POST https://api.yourdomain.com/api/auth/refresh \
  -H "Content-Type: application/json" \
  -d '{"refreshToken":"your_refresh_token"}'

Logout

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:

// 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 for debugging tips
  3. Check backend logs for detailed error messages
  4. Consult 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