Authentication
Discord OAuth, JWT tokens, FiveM API keys, and session management for the CDE CAD platform.
https://cdecad.com/api — All authentication endpoints are prefixed with /api/auth.
CDE CAD supports multiple authentication strategies depending on the client type. Browser-based dashboards use Discord OAuth with session cookies, API consumers use JWT bearer tokens, and FiveM game servers authenticate with API keys.
Discord OAuth Flow
All user authentication begins with Discord. CDE CAD uses Discord's OAuth2 authorization code flow to verify identity and retrieve guild membership information. There is no traditional email/password registration — users must first link their Discord account before setting up CAD credentials.
Step-by-Step Flow
- Initiate: The client redirects the user (or opens a browser window) to
GET /api/auth/discord. The server redirects to Discord's authorization page. - Authorize: The user grants CDE CAD permission to read their Discord profile and guild list on the Discord consent screen.
- Callback: Discord redirects back to
GET /api/auth/discord/callbackwith an authorization code. The server exchanges this code for a Discord access token. - Account Resolution: The server looks up the Discord ID in the database. If the user already has a CAD account, they are logged in immediately. If not, the server returns a registration prompt.
- Registration (new users only): New users call
POST /api/auth/complete-registrationwith their desired CAD username, password, and security questions. - Token Issued: Upon successful login or registration, the server issues a JWT token and sets a session cookie. The client stores the JWT for subsequent API calls.
Flow Diagram
Client CDE CAD Server Discord
| | |
| GET /api/auth/discord | |
| ---------------------------> | |
| | |
| 302 Redirect | |
| <--------------------------- | |
| | |
| User visits Discord OAuth consent page |
| --------------------------------------------------------->|
| | |
| User grants permission | |
| 302 Redirect with ?code=xxx | |
| <---------------------------------------------------------|
| | |
| GET /api/auth/discord/callback?code=xxx |
| ---------------------------> | |
| | Exchange code for token |
| | -------------------------->|
| | |
| | Discord user profile |
| | <--------------------------|
| | |
| (Existing user) | |
| Set-Cookie + JWT token | |
| <--------------------------- | |
| | |
| (New user) | |
| Registration required | |
| <--------------------------- | |
| | |
| POST /api/auth/complete-registration |
| ---------------------------> | |
| | |
| Set-Cookie + JWT token | |
| <--------------------------- | |
JWT Bearer Tokens
After authenticating via Discord OAuth or via the /api/auth/login endpoint, the server issues a signed JWT (JSON Web Token). This token must be included in the Authorization header for all authenticated API requests.
Token Format
JWTs are signed with a server-side secret and contain the user's ID, Discord ID, and expiration timestamp. Tokens are not encrypted — they are base64-encoded and can be decoded by anyone. Never share your token or commit it to source control.
| Property | Value |
|---|---|
| Algorithm | HS256 |
| Expiration | 7 days from issuance |
| Header | Authorization: Bearer <token> |
Using the Token
Include the JWT in the Authorization header with the Bearer scheme:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Example: Fetch Current User
# Login and obtain a token
curl -X POST https://cdecad.com/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username": "johndoe", "password": "s3cur3p@ss"}'
# Response:
# {
# "token": "eyJhbGciOiJIUzI1NiIs...",
# "user": { "id": "abc123", "username": "johndoe", ... }
# }
# Use the token for authenticated requests
curl https://cdecad.com/api/auth/me \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."
401 Unauthorized. Your client should detect this and redirect the user to re-authenticate via Discord OAuth or the login endpoint.
FiveM API Keys
FiveM game servers communicate with CDE CAD using dedicated API keys. These keys authenticate server-to-CAD requests such as 911 calls, panic button alerts, livemap updates, and character synchronization. API keys are not tied to individual users — they represent the FiveM server itself.
Key Format
All FiveM API keys are prefixed with fvm_ followed by a randomly generated string:
fvm_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
The fvm_ prefix allows the server to quickly identify the authentication method being used and route the request through the correct middleware. If a key does not have this prefix, it will be rejected.
Generating an API Key
API keys are created through the CDE CAD admin panel:
- Navigate to your community's Admin Settings panel.
- Open the FiveM Integration section.
- Click Generate API Key to create a new key.
- Copy the key immediately — it is only shown once at creation time.
Using the API Key
Include the key in the x-api-key header on every request from your FiveM server:
x-api-key: fvm_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
Example: Send a 911 Call from FiveM
curl -X POST https://cdecad.com/api/fivem/911 \
-H "Content-Type: application/json" \
-H "x-api-key: fvm_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6" \
-d '{
"callerName": "John Smith",
"location": "Legion Square",
"description": "Vehicle accident with injuries",
"callerNumber": "555-0123"
}'
API Key vs JWT
| Feature | JWT Bearer Token | FiveM API Key |
|---|---|---|
| Represents | An individual user | A FiveM server |
| Header | Authorization: Bearer ... | x-api-key: fvm_... |
| Expiration | 7 days | Does not expire (revoke manually) |
| Scope | All user-facing API endpoints | FiveM integration endpoints only |
| Creation | Issued on login/OAuth | Generated via admin panel |
Session Authentication
For browser-based clients (the CDE CAD dashboard and admin panels), the server also maintains cookie-based sessions. When a user completes the Discord OAuth flow or logs in via the login endpoint, the server sets an HTTP-only session cookie alongside the JWT response.
How Sessions Work
| Property | Value |
|---|---|
| Cookie Name | Server-managed (HTTP-only) |
| Max Age | 24 hours |
| Scope | Same-origin browser requests |
| Security | HTTP-only, Secure, SameSite |
Sessions are automatically sent by the browser with every request to the CDE CAD domain. You do not need to manually attach any headers — the browser handles this via cookies. This is the primary authentication method used by the CDE CAD web dashboard.
Session Lifecycle
- Creation: A session is created when the user logs in via OAuth or the login endpoint.
- Validation: On each request, the server validates the session cookie and hydrates the user context.
- Expiration: Sessions expire after 24 hours of inactivity. The user must re-authenticate after expiration.
- Logout: Calling
POST /api/auth/logoutdestroys the session and clears the cookie.
Auth Endpoints
Complete reference for all authentication and account management endpoints.
Initiate the Discord OAuth2 authorization flow. Redirects the user to Discord's consent page. After the user grants permission, Discord redirects back to the callback URL.
No Auth RequiredResponse
HTTP 302 redirect to https://discord.com/api/oauth2/authorize?...
# Open in a browser or redirect the user
curl -v https://cdecad.com/api/auth/discord
# < HTTP/1.1 302 Found
# < Location: https://discord.com/api/oauth2/authorize?client_id=...&redirect_uri=...&response_type=code&scope=identify+guilds
Handles the OAuth2 callback from Discord. Exchanges the authorization code for user profile data, resolves the account, and issues authentication credentials. This endpoint is called automatically by Discord's redirect — do not call it directly.
No Auth RequiredQuery Parameters
| Parameter | Type | Description |
|---|---|---|
code | string | Authorization code provided by Discord |
Response
Sets a session cookie and returns user data with a JWT token (existing users), or returns a registration prompt (new users).
Complete the registration process for a new user after Discord OAuth. Sets up the CAD username, password, and security questions for account recovery.
Requires Discord OAuth SessionRequest Body
| Field | Type | Required | Description |
|---|---|---|---|
discordId | string | Yes | The user's Discord ID from the OAuth flow |
username | string | Yes | Desired CAD username (must be unique) |
password | string | Yes | Account password (minimum 8 characters) |
securityQuestion1 | string | Yes | First security question for account recovery |
securityAnswer1 | string | Yes | Answer to the first security question |
securityQuestion2 | string | Yes | Second security question for account recovery |
securityAnswer2 | string | Yes | Answer to the second security question |
curl -X POST https://cdecad.com/api/auth/complete-registration \
-H "Content-Type: application/json" \
-d '{
"discordId": "123456789012345678",
"username": "johndoe",
"password": "s3cur3p@ssw0rd",
"securityQuestion1": "What is your pet'\''s name?",
"securityAnswer1": "Buddy",
"securityQuestion2": "What city were you born in?",
"securityAnswer2": "Austin"
}'
Response 201 Created
{
"token": "eyJhbGciOiJIUzI1NiIs...",
"user": {
"id": "64f1a2b3c4d5e6f7a8b9c0d1",
"username": "johndoe",
"discordId": "123456789012345678",
"discordUsername": "johndoe#1234"
}
}
Authenticate with a CAD username and password. Returns a JWT token and sets a session cookie. This is the standard login endpoint for users who have already completed registration.
No Auth RequiredRequest Body
| Field | Type | Required | Description |
|---|---|---|---|
username | string | Yes | CAD username |
password | string | Yes | Account password |
curl -X POST https://cdecad.com/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username": "johndoe", "password": "s3cur3p@ss"}'
Response 200 OK
{
"token": "eyJhbGciOiJIUzI1NiIs...",
"user": {
"id": "64f1a2b3c4d5e6f7a8b9c0d1",
"username": "johndoe",
"discordId": "123456789012345678",
"discordUsername": "johndoe#1234"
}
}
Authenticate from an in-game FiveM tablet or MDT interface. Functions identically to the standard login endpoint but is optimized for in-game UI contexts where Discord OAuth is not available.
No Auth RequiredRequest Body
| Field | Type | Required | Description |
|---|---|---|---|
username | string | Yes | CAD username |
password | string | Yes | Account password |
curl -X POST https://cdecad.com/api/auth/tablet-login \
-H "Content-Type: application/json" \
-d '{"username": "johndoe", "password": "s3cur3p@ss"}'
Response 200 OK
{
"token": "eyJhbGciOiJIUzI1NiIs...",
"user": {
"id": "64f1a2b3c4d5e6f7a8b9c0d1",
"username": "johndoe"
}
}
Retrieve the full profile of the currently authenticated user. Optionally scope the response to a specific community by passing a guildId query parameter, which includes the user's role and permissions within that community.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
guildId | string | No | Discord guild ID to scope community-specific data (roles, permissions) |
# Get basic user info
curl https://cdecad.com/api/auth/me \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."
# Get user info scoped to a specific community
curl "https://cdecad.com/api/auth/me?guildId=987654321098765432" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."
Response 200 OK
{
"id": "64f1a2b3c4d5e6f7a8b9c0d1",
"username": "johndoe",
"discordId": "123456789012345678",
"discordUsername": "johndoe#1234",
"communities": [
{
"communityId": "64f1a2b3c4d5e6f7a8b9c0d2",
"guildId": "987654321098765432",
"role": "member",
"permissions": ["civilian", "dispatch"]
}
]
}
Quick authentication status check. Returns the current user if authenticated, or a 401 if the session or token is invalid or expired. Useful for client-side auth guards.
curl https://cdecad.com/api/auth/user \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."
Response 200 OK (authenticated)
{
"authenticated": true,
"user": {
"id": "64f1a2b3c4d5e6f7a8b9c0d1",
"username": "johndoe"
}
}
Response 401 Unauthorized (not authenticated)
{
"success": false,
"msg": "Not authenticated"
}
Destroy the current session and clear the session cookie. The JWT token itself cannot be revoked server-side, but the session will be invalidated. Clients should discard the stored token after calling this endpoint.
Requires JWT or Sessioncurl -X POST https://cdecad.com/api/auth/logout \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."
Response 200 OK
{
"success": true,
"msg": "Logged out successfully"
}
Change the password for the currently authenticated user. Requires the current password for verification.
Requires JWT or SessionRequest Body
| Field | Type | Required | Description |
|---|---|---|---|
currentPassword | string | Yes | The user's current password |
newPassword | string | Yes | The desired new password (minimum 8 characters) |
curl -X POST https://cdecad.com/api/auth/change-password \
-H "Content-Type: application/json" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
-d '{
"currentPassword": "oldP@ssw0rd",
"newPassword": "n3wS3cur3P@ss"
}'
Response 200 OK
{
"success": true,
"msg": "Password changed successfully"
}
Retrieve the security questions for a given username. Used as the first step of the password reset flow. Only the questions are returned, never the answers.
No Auth RequiredQuery Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
username | string | Yes | The CAD username to look up |
curl "https://cdecad.com/api/auth/security-questions?username=johndoe"
Response 200 OK
{
"securityQuestion1": "What is your pet's name?",
"securityQuestion2": "What city were you born in?"
}
Verify the user's answers to their security questions. If the answers are correct, a time-limited password reset token is returned. This is the second step of the password reset flow.
No Auth RequiredRequest Body
| Field | Type | Required | Description |
|---|---|---|---|
username | string | Yes | The CAD username |
securityAnswer1 | string | Yes | Answer to the first security question |
securityAnswer2 | string | Yes | Answer to the second security question |
curl -X POST https://cdecad.com/api/auth/verify-security-answers \
-H "Content-Type: application/json" \
-d '{
"username": "johndoe",
"securityAnswer1": "Buddy",
"securityAnswer2": "Austin"
}'
Response 200 OK
{
"success": true,
"resetToken": "rst_a1b2c3d4e5f6g7h8i9j0..."
}
Reset the user's password using a valid reset token obtained from the security answer verification step. This is the final step of the password reset flow.
No Auth Required (requires resetToken)Request Body
| Field | Type | Required | Description |
|---|---|---|---|
resetToken | string | Yes | The time-limited reset token from /verify-security-answers |
newPassword | string | Yes | The desired new password (minimum 8 characters) |
curl -X POST https://cdecad.com/api/auth/reset-password \
-H "Content-Type: application/json" \
-d '{
"resetToken": "rst_a1b2c3d4e5f6g7h8i9j0...",
"newPassword": "myN3wP@ssword!"
}'
Response 200 OK
{
"success": true,
"msg": "Password has been reset successfully"
}
Endpoints Summary
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET | /api/auth/discord | None | Initiate Discord OAuth flow |
GET | /api/auth/discord/callback | None | Handle Discord OAuth callback |
POST | /api/auth/complete-registration | OAuth session | Complete new user registration |
POST | /api/auth/login | None | Username/password login |
POST | /api/auth/tablet-login | None | In-game tablet/MDT login |
GET | /api/auth/me | JWT / Session | Get current user profile |
GET | /api/auth/user | JWT / Session | Check authentication status |
POST | /api/auth/logout | JWT / Session | Destroy session and log out |
POST | /api/auth/change-password | JWT / Session | Change current password |
GET | /api/auth/security-questions | None | Get security questions for a user |
POST | /api/auth/verify-security-answers | None | Verify security answers, get reset token |
POST | /api/auth/reset-password | None | Reset password with reset token |
Permission Levels
CDE CAD uses a hierarchical role-based permission system. Every user has a role within each community they belong to. Roles determine what actions the user can perform and which API endpoints they can access.
| Role | Level | Description |
|---|---|---|
| Super Admin | Highest | Platform-level administrator. Has unrestricted access to all communities, settings, and management functions across the entire CDE CAD instance. This role is reserved for platform operators. |
| Community Admin | High | Full administrative control over a specific community. Can manage members, departments, roles, FiveM integration, community settings, and custom domains. Cannot access other communities or platform-level settings. |
| Supervisor | Medium | Elevated permissions within a community. Can manage dispatch operations, approve reports, manage BOLOs, and perform supervisory overrides. Cannot modify community settings or manage member roles. |
| Member | Standard | Standard community member. Can access the CAD as a civilian (characters, vehicles, 911 calls), and if assigned to a department, can access dispatch, LEO, or other departmental functions. |
Permission Checks in the API
When a request is made, the server checks the user's role within the relevant community. If the user does not have sufficient permissions, the server responds with 403 Forbidden:
{
"success": false,
"msg": "Insufficient permissions. Required role: Community Admin"
}
Some endpoints also check department-level permissions. For example, only users assigned to a law enforcement department can access LEO-specific endpoints like warrant management.
Community Scoping
CDE CAD is a multi-tenant platform where each Discord server maps to a separate community. All data (civilians, vehicles, calls, reports, departments, etc.) is isolated per community. When making API requests, you must specify which community you are operating within.
The x-community-id Header
The primary method for specifying the target community is the x-community-id request header. This header should contain the MongoDB ObjectId of the community:
x-community-id: 64f1a2b3c4d5e6f7a8b9c0d2
Example: Scoped Request
# Fetch all civilians in a specific community
curl https://cdecad.com/api/civilian \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
-H "x-community-id: 64f1a2b3c4d5e6f7a8b9c0d2"
The communityId Parameter
Some endpoints accept communityId as a query parameter or in the request body instead of (or in addition to) the header. This is particularly common in endpoints that create or list resources:
# Create a civilian with communityId in the request body
curl -X POST https://cdecad.com/api/civilian \
-H "Content-Type: application/json" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
-d '{
"communityId": "64f1a2b3c4d5e6f7a8b9c0d2",
"firstName": "John",
"lastName": "Smith",
"dateOfBirth": "1990-05-15"
}'
x-community-id header and the communityId parameter on endpoints that require community scoping, the server will respond with 400 Bad Request and a message indicating the community context is required.
How Community Scoping Works
| Method | Format | When to Use |
|---|---|---|
x-community-id header |
MongoDB ObjectId string | Most API requests. Set once in your HTTP client and include on every call. |
communityId body param |
MongoDB ObjectId string | POST/PUT requests where the community is part of the resource being created or updated. |
guildId query param |
Discord guild ID string | Some endpoints (like /api/auth/me) accept the Discord guild ID as an alternative identifier. |
GET /api/auth/me to see all communities your account belongs to. Each community object includes its communityId and associated guildId.