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¶
- Find user by username (case-sensitive)
- Verify password using Bcrypt
- Check email confirmed — if not, auto-resends confirmation email and returns error
- Check registration status:
Pending→ "Registration pending admin approval"Declined→ "Registration has been declined"Deactivated→ "Account has been deactivated"- Check group assigned — user must be in a group
- Generate tokens and return
Token Refresh¶
Refresh tokens use a one-time use pattern with instant renewal:
- Client sends refresh request with cookie
- API validates token hash exists and not expired
- Old token is immediately deleted
- New token created with same expiry date
- 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¶
- User requests email change
- New email stored as
PendingEmail EmailStatusset toChangePending- Confirmation email sent to new address
- User confirms via same endpoint as initial confirmation
PendingEmailbecomesEmail
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.
Related¶
- Authorization — Registration status and roles
- Security — Security architecture
- Background Jobs — Email sending
- Middleware Pattern — Request pipeline
- Environment Variables — Token configuration