Skip to main content

Authentication & Authorization System

Overview

Heimdall uses a comprehensive authentication and authorization system supporting:

  • OAuth Users - Multiple OAuth providers via NextAuth (Twitch, Discord, YouTube, GitHub)
  • Email/Password - Traditional authentication with email verification
  • Two-Factor Authentication - TOTP-based 2FA with backup codes
  • API Keys - Programmatic access with scopes and permissions
  • RBAC - Role-Based Access Control with granular permissions
  • GDPR Compliance - Data export, account deletion, privacy controls

Architecture

Entities

User
├── Sessions (NextAuth managed)
├── API Keys (user-owned)
├── User Roles (many-to-many)
├── Two-Factor Auth (TOTP secret, backup codes)
├── Linked Accounts (OAuth providers)
├── Connected Apps (OAuth consents)
└── Data Export Logs

API Key
├── Owner (User, optional for system keys)
├── Scopes (array of scope strings)
└── API Key Roles (many-to-many)

Role
├── Name (e.g., "Admin", "Viewer", "GPS Manager")
├── Description
├── Requires 2FA (boolean)
└── Permissions (many-to-many)

Permission
├── Resource (e.g., "gps", "users", "settings")
├── Action (e.g., "read", "write", "delete")
├── Description

Authentication Flow

1. User Authentication (OAuth)

Frontend (Next.js) → NextAuth → OAuth Provider (Twitch/Discord/YouTube/GitHub)

Session Created

JWT Token (NextAuth)

API Request → Bearer Token → Validate Session → Check 2FA → User Context

2. User Authentication (Email/Password)

Frontend (Next.js) → Login Form → API Validate Credentials

Check 2FA Required

Verify TOTP/Backup Code

Session Created

3. API Key Authentication

API Request → Bearer Token → Lookup API Key → Check Active

User Context (if owned)

API Key Context

4. Two-Factor Authentication Flow

Login Success → Check 2FA Enabled

┌──────┴──────┐
│ │
2FA Enabled 2FA Disabled
│ │
▼ ▼
Show 2FA Form Grant Access


Verify Code
(TOTP or Backup)


Grant Access

Authorization Flow

Request → Auth Context (User/API Key)

Get Roles

Get Permissions

Check Permission (resource:action)

Allow/Deny

Database Schema

Users

CREATE TABLE "User" (
id TEXT PRIMARY KEY,
username TEXT NOT NULL,
display_name TEXT,
email TEXT,
email_verified BOOLEAN DEFAULT FALSE,
password_hash TEXT,
avatar_url TEXT,
two_factor_enabled BOOLEAN DEFAULT FALSE,
two_factor_secret TEXT,
backup_codes TEXT[],
privacy_mode BOOLEAN DEFAULT FALSE,
is_scheduled_for_deletion BOOLEAN DEFAULT FALSE,
scheduled_deletion_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
last_login_at TIMESTAMPTZ
);

Linked Accounts (OAuth Providers)

CREATE TABLE "LinkedAccount" (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL REFERENCES "User"(id) ON DELETE CASCADE,
platform_id TEXT NOT NULL,
platform_slug TEXT NOT NULL,
platform_name TEXT,
provider_account_id TEXT NOT NULL,
username TEXT,
email TEXT,
is_primary BOOLEAN DEFAULT FALSE,
is_oauth BOOLEAN DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE(platform_id, provider_account_id)
);

Sessions (next-auth managed)

CREATE TABLE "Session" (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL REFERENCES "User"(id) ON DELETE CASCADE,
session_token TEXT UNIQUE NOT NULL,
expires TIMESTAMPTZ NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

API Keys

CREATE TABLE "ApiKey" (
id TEXT PRIMARY KEY,
key TEXT UNIQUE NOT NULL,
name TEXT NOT NULL,
description TEXT,
user_id TEXT REFERENCES "User"(id) ON DELETE CASCADE,
scopes TEXT[] NOT NULL DEFAULT '{}',
is_active BOOLEAN NOT NULL DEFAULT true,
expires_at TIMESTAMPTZ,
last_used_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

Roles

CREATE TABLE "Role" (
id TEXT PRIMARY KEY,
name TEXT UNIQUE NOT NULL,
description TEXT,
is_system BOOLEAN NOT NULL DEFAULT false,
requires_2fa BOOLEAN NOT NULL DEFAULT false,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

Permissions

CREATE TABLE "Permission" (
id TEXT PRIMARY KEY,
resource TEXT NOT NULL,
action TEXT NOT NULL,
description TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE(resource, action)
);

Junction Tables

CREATE TABLE "UserRole" (
user_id TEXT NOT NULL REFERENCES "User"(id) ON DELETE CASCADE,
role_id TEXT NOT NULL REFERENCES "Role"(id) ON DELETE CASCADE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
PRIMARY KEY (user_id, role_id)
);

CREATE TABLE "ApiKeyRole" (
api_key_id TEXT NOT NULL REFERENCES "ApiKey"(id) ON DELETE CASCADE,
role_id TEXT NOT NULL REFERENCES "Role"(id) ON DELETE CASCADE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
PRIMARY KEY (api_key_id, role_id)
);

CREATE TABLE "RolePermission" (
role_id TEXT NOT NULL REFERENCES "Role"(id) ON DELETE CASCADE,
permission_id TEXT NOT NULL REFERENCES "Permission"(id) ON DELETE CASCADE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
PRIMARY KEY (role_id, permission_id)
);

OAuth Consents (Connected Apps)

CREATE TABLE "OAuthConsent" (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL REFERENCES "User"(id) ON DELETE CASCADE,
client_id TEXT NOT NULL REFERENCES "OAuthClient"(id) ON DELETE CASCADE,
scopes TEXT[] NOT NULL DEFAULT '{}',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE(user_id, client_id)
);

Data Export Logs

CREATE TABLE "DataExportLog" (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL REFERENCES "User"(id) ON DELETE CASCADE,
exported_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
file_size_bytes BIGINT
);

Default Roles

Case-Sensitive Role Names

Role names are case-sensitive. Always use the exact capitalization shown below when checking roles programmatically (e.g., "Admin" not "admin").

Use Role IDs for Programmatic Checks

Best Practice: Use role_ids instead of roles (names) for programmatic role checks. Role IDs are:

  • Immutable - They never change, even if role names are renamed
  • Case-insensitive - No capitalization issues
  • Stable - Safe for long-term use in code

The API returns both roles (names for display) and role_ids (IDs for programmatic use).

System Roles

Role NameIDDescriptionPermissionsRequires 2FA
Super Adminrole_super_adminFull system access, bypasses all permission checks*:* (wildcard)Yes
Adminrole_adminFull administrative access*:* (wildcard)Yes
Moderatorrole_moderatorContent moderation and GPS managementgps:*, users:read, users:ban, settings:read, stats:read, two_factor:*Yes
Userrole_userStandard user accesstwo_factor:read, two_factor:writeNo
Developerrole_developerOAuth and API key managementoauth_clients:*, api_keys:*, two_factor:*Yes

API Roles

Role NameIDDescriptionPermissions
API Full Accessrole_api_full_accessFull API access*:* (wildcard)
API Read Onlyrole_api_read_onlyRead-only API accessgps:read, users:read, roles:read, permissions:read, api_keys:read, settings:read, stats:read, oauth_clients:read

Super Admin

A user becomes a super admin if any of these conditions are true:

  1. Has the role_super_admin role - The Super Admin role with ID role_super_admin
  2. Has a role named super_admin (case-sensitive) - Legacy support
  3. Has the *:* permission - The wildcard permission granting access to all resources

Super admin users:

  • Have is_super_admin: true in their permissions response
  • Bypass all permission checks
  • Can access all resources and actions
  • Can modify system roles

Default Permissions

GPS Resource

  • gps:read - View GPS data
  • gps:write - Create/update GPS data
  • gps:delete - Delete GPS data

Users Resource

  • users:read - View users
  • users:write - Create/update users
  • users:delete - Delete users

Roles Resource

  • roles:read - View roles
  • roles:write - Create/update roles
  • roles:delete - Delete roles

Permissions Resource

  • permissions:read - View permissions
  • permissions:write - Create/update permissions
  • permissions:delete - Delete permissions

API Keys Resource

  • api_keys:read - View API keys
  • api_keys:write - Create/update API keys
  • api_keys:delete - Delete API keys

Settings Resource

  • settings:read - View settings
  • settings:write - Create/update settings
  • settings:delete - Delete settings

Stats Resource

  • stats:read - View statistics

Two-Factor Auth Resource

  • two_factor:read - View 2FA status
  • two_factor:write - Enable/disable 2FA
  • two_factor:admin - Manage 2FA for other users

OAuth Clients Resource

  • oauth_clients:read - View OAuth clients
  • oauth_clients:write - Create/update OAuth clients
  • oauth_clients:delete - Delete OAuth clients

API Key Scopes

Scopes provide additional restrictions beyond permissions:

  • gps:read - Read GPS data
  • gps:write - Write GPS data
  • users:read - Read users
  • users:write - Write users
  • settings:read - Read settings
  • settings:write - Write settings
  • * - All scopes (admin keys)

Scopes act as an additional layer: an API key needs both the scope AND the permission through roles.

Auth Context

pub struct AuthContext {
pub user: Option<User>,
pub api_key: Option<ApiKey>,
pub permissions: Vec<String>, // ["gps:read", "gps:write"]
pub scopes: Vec<String>, // API key scopes
}

impl AuthContext {
pub fn has_permission(&self, permission: &str) -> bool;
pub fn has_scope(&self, scope: &str) -> bool;
pub fn can(&self, resource: &str, action: &str) -> bool;
}

Endpoints

Authentication

  • POST /v1/auth/session - Validate next-auth session
  • POST /v1/auth/refresh - Refresh session

Users

  • GET /v1/users - List users (users:read)
  • GET /v1/users/:id - Get user (users:read)
  • PATCH /v1/users/:id - Update user (users:write)
  • DELETE /v1/users/:id - Delete user (users:delete)
  • GET /v1/users/:id/roles - Get user roles (users:read)
  • POST /v1/users/:id/roles - Add role to user (users:write)
  • DELETE /v1/users/:id/roles/:roleId - Remove role (users:write)

API Keys

  • GET /v1/api-keys - List API keys (api_keys:read)
  • POST /v1/api-keys - Create API key (api_keys:write)
  • GET /v1/api-keys/:id - Get API key (api_keys:read)
  • PATCH /v1/api-keys/:id - Update API key (api_keys:write)
  • DELETE /v1/api-keys/:id - Delete API key (api_keys:delete)
  • POST /v1/api-keys/:id/rotate - Rotate key (api_keys:write)

Roles

  • GET /v1/roles - List roles (roles:read)
  • POST /v1/roles - Create role (roles:write)
  • GET /v1/roles/:id - Get role (roles:read)
  • PATCH /v1/roles/:id - Update role (roles:write)
  • DELETE /v1/roles/:id - Delete role (roles:delete)
  • GET /v1/roles/:id/permissions - Get role permissions (roles:read)
  • POST /v1/roles/:id/permissions - Add permission (roles:write)
  • DELETE /v1/roles/:id/permissions/:permId - Remove permission (roles:write)

Permissions

  • GET /v1/permissions - List permissions (permissions:read)
  • POST /v1/permissions - Create permission (permissions:write)
  • GET /v1/permissions/:id - Get permission (permissions:read)
  • DELETE /v1/permissions/:id - Delete permission (permissions:delete)

Two-Factor Authentication

  • GET /v1/2fa/status - Get 2FA status
  • POST /v1/2fa/setup - Generate TOTP secret and QR code
  • POST /v1/2fa/enable - Enable 2FA with verification code
  • POST /v1/2fa/disable - Disable 2FA
  • POST /v1/2fa/verify - Verify TOTP code
  • GET /v1/2fa/backup-codes - Get backup codes
  • POST /v1/2fa/backup-codes/regenerate - Regenerate backup codes

GDPR / Data Management

  • GET /v1/user/export - Export user data (GDPR)
  • GET /v1/user/export/logs - Get export history
  • POST /v1/user/delete - Request account deletion
  • DELETE /v1/user/delete - Cancel account deletion
  • GET /v1/user/deletion-status - Get deletion status

Connected Apps (OAuth Consents)

  • GET /v1/user/connected-apps - List connected apps
  • DELETE /v1/user/connected-apps/:id - Revoke app access
  • DELETE /v1/user/connected-apps - Revoke all app access

Security Considerations

  1. API Keys

    • Generate using cryptographically secure random
    • Store hashed version (SHA-256)
    • Prefix with hmdl_ for identification
    • Support expiration dates
    • Track last used timestamp
  2. Sessions

    • Managed by NextAuth
    • JWT tokens with short expiration
    • Secure httpOnly cookies
    • App-specific cookie names to prevent session sharing:
      • ID App: id.session-token
      • Backend: backend.session-token
      • Policies: policies.session-token
  3. Two-Factor Authentication

    • TOTP-based (RFC 6238)
    • 30-second time window
    • Compatible with Google Authenticator, Authy, 1Password
    • 10 single-use backup codes
    • Role-based 2FA requirements
  4. Permissions

    • Cached per request to avoid N+1 queries
    • Wildcard support (*:*, gps:*)
    • Hierarchical checking (most specific first)
  5. Rate Limiting

    • Per user/API key
    • Different limits for different roles
    • Stricter limits for write operations
  6. GDPR Compliance

    • Data export in structured format (JSON)
    • Account deletion with 30-day grace period
    • User anonymization on deletion
    • Privacy mode to hide sensitive data
    • Audit logging of data exports