Authentication & Security
The API uses a combination of session-based authentication and CSRF protection for all database write operations to prevent unauthorized access and potential denial-of-service attacks. External APIs (used by Duplicati) remain unauthenticated for compatibility.
Session-Based Authentication
Protected endpoints require a valid session cookie and CSRF token. The session system provides secure authentication for all protected operations.
Session Management
- Create Session: POST to
/api/sessionto create a new session - Get CSRF Token: GET
/api/csrfto obtain a CSRF token for the session - Include in Requests: Send session cookie and CSRF token with protected requests
- Validate Session: GET
/api/sessionto check if session is still valid - Delete Session: DELETE
/api/sessionto logout and clear session
CSRF Protection
All state-changing operations require a valid CSRF token that matches the current session. The CSRF token must be included in the X-CSRF-Token header for protected endpoints.
Protected Endpoints
All endpoints that modify database data require session authentication and CSRF token:
- Server Management:
/api/servers/:id(PATCH, DELETE),/api/servers/:id/server-url(PATCH),/api/servers/:id/password(PATCH, GET) - Configuration Management:
/api/configuration/email(GET, POST, DELETE),/api/configuration/unified(GET),/api/configuration/ntfy(GET),/api/configuration/notifications(GET, POST),/api/configuration/backup-settings(POST),/api/configuration/templates(POST),/api/configuration/overdue-tolerance(GET, POST) - Notification System:
/api/notifications/test(POST) - Cron Configuration:
/api/cron-config(GET, POST) - Cron Proxy:
/api/cron/*(GET, POST) - proxies requests to the cron service - Session Management:
/api/session(POST, GET, DELETE),/api/csrf(GET) - Chart Data:
/api/chart-data/*(GET) - Dashboard:
/api/dashboard(GET) - Server Details:
/api/servers(GET),/api/servers/:id(GET),/api/detail/:serverId(GET) - Audit Log:
/api/audit-log(GET),/api/audit-log/download(GET),/api/audit-log/filters(GET),/api/audit-log/retention(PATCH),/api/audit-log/cleanup(POST) - admin required for write operations - User Management:
/api/users(GET, POST, PATCH, DELETE) - admin required - Database Management:
/api/database/backup(GET),/api/database/restore(POST) - admin required - Application Logs:
/api/application-logs(GET),/api/application-logs/export(GET) - admin required - Backup Collection:
/api/backups/collect(POST) - requires session and CSRF token - Backup Schedule Sync:
/api/backups/sync-schedule(POST) - requires session and CSRF token - Overdue Check:
/api/notifications/check-overdue(POST) - requires session and CSRF token - Clear Overdue Timestamps:
/api/notifications/clear-overdue-timestamps(POST) - requires session and CSRF token
Unprotected Endpoints
External APIs remain unauthenticated for Duplicati integration:
/api/upload- Backup data uploads from Duplicati/api/lastbackup/:serverId- Latest backup status/api/lastbackups/:serverId- Latest backups status/api/summary- Overall summary data/api/health- Health check endpoint
Usage Example (Session + CSRF)
// 1. Create session
const sessionResponse = await fetch('/api/session', { method: 'POST' });
const { sessionId } = await sessionResponse.json();
// 2. Get CSRF token
const csrfResponse = await fetch('/api/csrf', {
headers: { 'Cookie': `session=${sessionId}` }
});
const { csrfToken } = await csrfResponse.json();
// 3. Make protected request
const response = await fetch('/api/servers/server-id', {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': csrfToken,
'Cookie': `session=${sessionId}`
},
body: JSON.stringify({
alias: 'Updated Server Name',
note: 'Updated notes'
})
});
Authentication Endpoints
Login - /api/auth/login
- Endpoint:
/api/auth/login - Method: POST
- Description: Authenticates a user and creates a session. Supports account locking after failed attempts and password change requirements.
- Authentication: Requires valid session and CSRF token (but no logged-in user)
- Request Body:
{
"username": "admin",
"password": "password123"
} - Response (success):
{
"success": true,
"user": {
"id": "user-id",
"username": "admin",
"isAdmin": true,
"mustChangePassword": false
},
"keyChanged": false
} - Error Responses: All error responses include
error(English message) anderrorCode(stable code for client-side translation).400: Missing username or password —errorCode: "REQUIRED_CREDENTIALS"401: Invalid username or password —errorCode: "INVALID_CREDENTIALS"403: Account locked due to too many failed login attempts —errorCode: "ACCOUNT_LOCKED"(includeslockedUntil,minutesRemaining)500: Internal server error —errorCode: "INTERNAL_ERROR"503: Database not ready —errorCode: "DATABASE_NOT_READY"
- Notes:
- Account is locked after 5 failed login attempts for 15 minutes
- Failed login attempts are tracked and logged
- Session cookie is automatically set in the response
- If user has
mustChangePasswordflag set, they should be redirected to change password page - All login attempts (successful and failed) are logged to audit log
Logout - /api/auth/logout
- Endpoint:
/api/auth/logout - Method: POST
- Description: Logs out the current user and destroys their session.
- Authentication: Requires valid session and CSRF token
- Response (success):
{
"success": true,
"message": "Logged out successfully",
"successCode": "LOGGED_OUT"
} - Error Responses: Include
erroranderrorCodefor client-side translation.400: No active session —errorCode: "NO_ACTIVE_SESSION"500: Internal server error —errorCode: "INTERNAL_ERROR"
- Notes:
- Session cookie is cleared in the response
- Logout is logged to audit log
- Session is immediately invalidated
Get Current User - /api/auth/me
- Endpoint:
/api/auth/me - Method: GET
- Description: Returns the current authenticated user information, or indicates if no user is logged in.
- Authentication: Requires valid session (but no logged-in user required)
- Response (authenticated):
{
"authenticated": true,
"user": {
"id": "user-id",
"username": "admin",
"isAdmin": true,
"mustChangePassword": false
}
} - Response (not authenticated):
{
"authenticated": false,
"user": null
} - Error Responses: Include
erroranderrorCodefor client-side translation.500: Internal server error —errorCode: "INTERNAL_ERROR"
- Notes:
- Can be called without a logged-in user (returns
authenticated: false) - Useful for checking authentication status on page load
- Can be called without a logged-in user (returns
Change Password - /api/auth/change-password
- Endpoint:
/api/auth/change-password - Method: POST
- Description: Changes the password for the current authenticated user. If
mustChangePasswordis set, current password verification is skipped. - Authentication: Requires valid session and CSRF token (logged-in user required)
- Request Body:
{
"currentPassword": "old-password",
"newPassword": "new-secure-password"
}currentPassword: Optional ifmustChangePasswordis true, required otherwisenewPassword: Required, must meet password policy requirements
- Response (success):
{
"success": true,
"message": "Password changed successfully",
"successCode": "PASSWORD_CHANGED"
} - Error Responses: Include
erroranderrorCodefor client-side translation. Policy violation may includevalidationErrors(array of strings).400: Missing new password —errorCode: "NEW_PASSWORD_REQUIRED"400: Password policy violation —errorCode: "POLICY_NOT_MET"(may includevalidationErrors)400: New password same as current —errorCode: "NEW_PASSWORD_SAME_AS_CURRENT"401: Current password is incorrect —errorCode: "CURRENT_PASSWORD_INCORRECT"404: User not found —errorCode: "USER_NOT_FOUND"500: Internal server error —errorCode: "INTERNAL_ERROR"
- Notes:
- New password must meet password policy requirements (length, complexity, etc.)
- If
mustChangePasswordflag is set, current password verification is skipped - After successful password change,
mustChangePasswordflag is cleared - Password changes are logged to audit log
- New password must be different from current password
Check Admin Must Change Password - /api/auth/admin-must-change-password
- Endpoint:
/api/auth/admin-must-change-password - Method: GET
- Description: Checks if the admin user must change their password. This endpoint is public (no authentication required) as it only returns a boolean flag.
- Response:
{
"mustChangePassword": false
} - Error Responses:
500: Internal server error (returnsmustChangePassword: falseon error to avoid showing tip if there's a database issue)
- Notes:
- Public endpoint, no authentication required
- Returns
falseif admin user doesn't exist - Used to determine if password change tip should be shown
- On error, returns
falseto avoid showing tip if there's a database issue
Get Password Policy - /api/auth/password-policy
- Endpoint:
/api/auth/password-policy - Method: GET
- Description: Returns the current password policy configuration. This endpoint is public (no authentication required) as it's needed for frontend validation.
- Response:
{
"minLength": 8,
"requireUppercase": true,
"requireLowercase": true,
"requireNumbers": true,
"requireSpecialChars": false
} - Error Responses: Include
erroranderrorCodefor client-side translation.500: Failed to retrieve password policy —errorCode: "POLICY_RETRIEVE_FAILED"
- Notes:
- Public endpoint, no authentication required
- Used by frontend components to display password requirements and validate passwords before submission
- Policy is configured via environment variables (
PWD_ENFORCE,PWD_MIN_LEN) - Default password check (preventing use of default admin password) is always enforced regardless of policy settings
Auth API error and success codes (i18n)
Auth endpoints return a stable errorCode (and, on success, successCode) in addition to the human-readable error or message field. The error and message values are in English. Clients should use the codes to look up localized strings so that the UI displays messages in the user's selected language.
| Endpoint | Success code | Error codes |
|---|---|---|
/api/auth/login | — | REQUIRED_CREDENTIALS, INVALID_CREDENTIALS, ACCOUNT_LOCKED, DATABASE_NOT_READY, INTERNAL_ERROR |
/api/auth/logout | LOGGED_OUT | NO_ACTIVE_SESSION, INTERNAL_ERROR |
/api/auth/me | — | INTERNAL_ERROR |
/api/auth/change-password | PASSWORD_CHANGED | NEW_PASSWORD_REQUIRED, POLICY_NOT_MET, USER_NOT_FOUND, CURRENT_PASSWORD_INCORRECT, NEW_PASSWORD_SAME_AS_CURRENT, INTERNAL_ERROR |
/api/auth/password-policy | — | POLICY_RETRIEVE_FAILED |
Error Responses
401 Unauthorized: Invalid or missing session, expired session, or CSRF token validation failed403 Forbidden: CSRF token validation failed or operation not allowed
Don't expose the duplistatus server to the public internet. Use it in a secure network (e.g., local LAN protected by a firewall).
Exposing the duplistatus interface to the public internet without proper security measures could lead to unauthorized access.