Skip to content

Authentication

[!abstract] Summary Unicorn Trails uses JWT access tokens with HTTP-only refresh token cookies. Login requires email confirmation and admin approval.

Token System

Token Storage Lifetime Purpose
Access Token localStorage Short-lived (configurable) API authorization via Bearer header
Refresh Token HTTP-only cookie Days (extended with "remember me") Obtain new access tokens

Token Claims

The access token JWT contains:

Claim Purpose
UserId User identifier
Nickname Display name
GroupId Current group
Role User role (User/Admin)
Currency Group's currency
Modules Enabled feature modules (JSON array)
PermsVersion Permissions version for cache invalidation

Login Flow

sequenceDiagram
    participant C as Client
    participant A as API
    participant DB as Database

    C->>A: POST /api/auth/login
    A->>DB: Find user by username
    A->>A: Verify password (Bcrypt)
    A->>A: Check email confirmed
    A->>A: Check registration status
    A->>A: Check group assigned
    A-->>C: Access token (body) + Refresh cookie

Login Validation Steps

  1. Find user by username (case-sensitive)
  2. Verify password using Bcrypt
  3. Check email confirmed — if not, auto-resends confirmation email and returns error
  4. Check registration status:
  5. Pending → "Registration pending admin approval"
  6. Declined → "Registration has been declined"
  7. Deactivated → "Account has been deactivated"
  8. Check group assigned — user must be in a group
  9. Generate tokens and return

Token Refresh

Refresh tokens use a one-time use pattern with instant renewal:

  1. Client sends refresh request with cookie
  2. API validates token hash exists and not expired
  3. Old token is immediately deleted
  4. New token created with same expiry date
  5. Session metadata (UserAgent, IP) updated

Password Reset

sequenceDiagram
    participant C as Client
    participant A as API

    C->>A: POST /api/auth/password/forgot
    Note over A: Validates CAPTCHA
    A->>A: Generate reset token (hashed)
    A-->>C: 204 (always, prevents enumeration)

    Note over C: User clicks email link

    C->>A: POST /api/auth/password/reset
    A->>A: Validate token hash + expiry
    A->>A: Update password
    A->>A: Revoke ALL refresh tokens
    A-->>C: 200 OK

Email Change

  1. User requests email change
  2. New email stored as PendingEmail
  3. EmailStatus set to ChangePending
  4. Confirmation email sent to new address
  5. User confirms via same endpoint as initial confirmation
  6. PendingEmail becomes Email

Session Management

Each session is tracked with metadata:

Field Purpose
UserAgent Browser/client identifier
IpAddress Client IP address
DeviceName Device identifier
LastUsedDate Last token refresh
CreatedDate Session start

Sessions can be:

  • Viewed in account settings
  • Individually revoked
  • All revoked on password change or account deactivation

Security Features

Feature Implementation
Token hashing Refresh tokens stored as SHA256 hashes
One-time use Refresh token replaced on each use
HttpOnly cookies Refresh token inaccessible to JavaScript
Secure flag Cookies only sent over HTTPS (production)
SameSite Lax mode prevents CSRF
Rate limiting All auth endpoints rate-limited
CAPTCHA Register and forgot password require Turnstile
Honeypot Registration form has hidden field to catch bots
Cooldowns Email confirmation and password reset throttled

Error Codes

See Authentication Errors for the complete list of auth error codes.