Authentication

Discord OAuth, JWT tokens, FiveM API keys, and session management for the CDE CAD platform.

Base URL: 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

  1. Initiate: The client redirects the user (or opens a browser window) to GET /api/auth/discord. The server redirects to Discord's authorization page.
  2. Authorize: The user grants CDE CAD permission to read their Discord profile and guild list on the Discord consent screen.
  3. Callback: Discord redirects back to GET /api/auth/discord/callback with an authorization code. The server exchanges this code for a Discord access token.
  4. 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.
  5. Registration (new users only): New users call POST /api/auth/complete-registration with their desired CAD username, password, and security questions.
  6. 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      |                            |
    | <--------------------------- |                            |
Important: The Discord OAuth flow must be initiated from a browser. It cannot be performed from a headless API client because the user must interactively grant permission on Discord's consent page.

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.

PropertyValue
AlgorithmHS256
Expiration7 days from issuance
HeaderAuthorization: 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..."
Token Expiration: Tokens expire after 7 days. When a token expires, the server responds with 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:

  1. Navigate to your community's Admin Settings panel.
  2. Open the FiveM Integration section.
  3. Click Generate API Key to create a new key.
  4. Copy the key immediately — it is only shown once at creation time.
Security: Treat your API key like a password. Store it in your FiveM server's environment variables or a secure configuration file. Never commit API keys to version control or share them publicly.

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

FeatureJWT Bearer TokenFiveM API Key
RepresentsAn individual userA FiveM server
HeaderAuthorization: Bearer ...x-api-key: fvm_...
Expiration7 daysDoes not expire (revoke manually)
ScopeAll user-facing API endpointsFiveM integration endpoints only
CreationIssued on login/OAuthGenerated 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

PropertyValue
Cookie NameServer-managed (HTTP-only)
Max Age24 hours
ScopeSame-origin browser requests
SecurityHTTP-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 vs JWT: Browser clients typically rely on sessions for convenience (automatic cookie handling). External API consumers, scripts, and mobile clients should use JWT bearer tokens instead. Both methods authenticate the same user and grant the same permissions.

Session Lifecycle

  1. Creation: A session is created when the user logs in via OAuth or the login endpoint.
  2. Validation: On each request, the server validates the session cookie and hydrates the user context.
  3. Expiration: Sessions expire after 24 hours of inactivity. The user must re-authenticate after expiration.
  4. Logout: Calling POST /api/auth/logout destroys the session and clears the cookie.

Auth Endpoints

Complete reference for all authentication and account management endpoints.

GET /api/auth/discord

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 Required

Response

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
GET /api/auth/discord/callback

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 Required

Query Parameters

ParameterTypeDescription
codestringAuthorization 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).

POST /api/auth/complete-registration

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 Session

Request Body

FieldTypeRequiredDescription
discordIdstringYesThe user's Discord ID from the OAuth flow
usernamestringYesDesired CAD username (must be unique)
passwordstringYesAccount password (minimum 8 characters)
securityQuestion1stringYesFirst security question for account recovery
securityAnswer1stringYesAnswer to the first security question
securityQuestion2stringYesSecond security question for account recovery
securityAnswer2stringYesAnswer 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"
  }
}
POST /api/auth/login

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 Required

Request Body

FieldTypeRequiredDescription
usernamestringYesCAD username
passwordstringYesAccount 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"
  }
}
POST /api/auth/tablet-login

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 Required

Request Body

FieldTypeRequiredDescription
usernamestringYesCAD username
passwordstringYesAccount 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"
  }
}
GET /api/auth/me

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.

Requires JWT or Session

Query Parameters

ParameterTypeRequiredDescription
guildIdstringNoDiscord 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"]
    }
  ]
}
GET /api/auth/user

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.

Requires JWT or Session
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"
}
POST /api/auth/logout

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 Session
curl -X POST https://cdecad.com/api/auth/logout \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

Response 200 OK

{
  "success": true,
  "msg": "Logged out successfully"
}
POST /api/auth/change-password

Change the password for the currently authenticated user. Requires the current password for verification.

Requires JWT or Session

Request Body

FieldTypeRequiredDescription
currentPasswordstringYesThe user's current password
newPasswordstringYesThe 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"
}
GET /api/auth/security-questions

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 Required

Query Parameters

ParameterTypeRequiredDescription
usernamestringYesThe 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?"
}
POST /api/auth/verify-security-answers

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 Required

Request Body

FieldTypeRequiredDescription
usernamestringYesThe CAD username
securityAnswer1stringYesAnswer to the first security question
securityAnswer2stringYesAnswer 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..."
}
POST /api/auth/reset-password

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

FieldTypeRequiredDescription
resetTokenstringYesThe time-limited reset token from /verify-security-answers
newPasswordstringYesThe 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

MethodEndpointAuthDescription
GET/api/auth/discordNoneInitiate Discord OAuth flow
GET/api/auth/discord/callbackNoneHandle Discord OAuth callback
POST/api/auth/complete-registrationOAuth sessionComplete new user registration
POST/api/auth/loginNoneUsername/password login
POST/api/auth/tablet-loginNoneIn-game tablet/MDT login
GET/api/auth/meJWT / SessionGet current user profile
GET/api/auth/userJWT / SessionCheck authentication status
POST/api/auth/logoutJWT / SessionDestroy session and log out
POST/api/auth/change-passwordJWT / SessionChange current password
GET/api/auth/security-questionsNoneGet security questions for a user
POST/api/auth/verify-security-answersNoneVerify security answers, get reset token
POST/api/auth/reset-passwordNoneReset 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.

RoleLevelDescription
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 Inheritance: Higher roles inherit all permissions from lower roles. A Community Admin can do everything a Supervisor and Member can do, plus community management tasks.

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"
  }'
Missing Community Context: If you omit both the 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

MethodFormatWhen 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.
Finding Your Community ID: Call GET /api/auth/me to see all communities your account belongs to. Each community object includes its communityId and associated guildId.