Skip to content

authentication

github-actions[bot] edited this page May 28, 2026 · 1 revision

Authentication & Profiles

SlopSmith uses a PIN-based multi-profile system. Each profile is a separate user identity with its own settings, favorites, scores, and loops. There are no passwords — access is controlled by a numeric PIN and optional permission groups.


Profiles

A Profile is the top-level identity in SlopSmith. Multiple profiles can exist on a single instance (e.g., family members sharing a device).

Profile fields

Field Description
id Auto-incremented integer primary key
name Unique display name
avatarId Selected avatar index
pinCode SHA-256(salt + PIN) — never exposed in API
pinSalt Random salt used for PIN hashing
recoveryPhrase SHA-256(salt + phrase) — never exposed
recoveryPhraseSalt Random salt for recovery phrase
recoveryPhraseHint User-visible hint (e.g., "name of first pet")
locked Admin-set flag to prevent login
profileSettings JSON blob (theme, volume, renderer, etc.)

Authentication Flow

User selects profile → enters PIN
        │
        ▼
POST /api/auth/login
  { profileName, pin }
        │
        │ backend: SHA-256(salt + pin) == stored hash?
        ├── no  → increment fail counter
        │         5 failures → 30-min runtime lockout
        │         return 401
        └── yes → create Session (token UUID, 24h TTL)
                  return { token, expiresAt, profile }
        │
        ▼
Frontend stores token in localStorage
Includes on every request:
  Authorization: Bearer {token}

Session object

{
  token: string       // UUID
  profileId: number
  profileName: string
  createdAt: number   // Unix ms
  expiresAt: number   // now + 24 hours (Unix ms)
}

Sessions are stored in the database. On logout (POST /api/auth/logout) the token is immediately invalidated — existing sessions on other devices/tabs stop working.


PIN Security

  • PINs are never stored in plain text.
  • Storage: SHA-256(salt + pin) — a random salt per profile is generated at profile creation.
  • Lockout: 5 failed attempts lock the profile for 30 minutes (in-memory; resets on server restart).
  • Recovery: If a user forgets their PIN, they can verify their recovery phrase (POST /api/auth/recover) and set a new PIN.

WebSocket Authentication

Browsers cannot send custom headers during a WebSocket upgrade. To authenticate WebSocket connections, pass the token as a query parameter:

ws://host:8085/ws/some-endpoint?token={token}

The backend auth middleware accepts both Authorization: Bearer {token} (REST) and ?token= (WebSocket). The resulting req.session object is identical in both cases.


Permissions

Permissions are coarse-grained capabilities assigned to profiles via Permission Groups.

Built-in permissions

Permission What it gates
ADMIN Full access; implicitly grants all others
UPLOAD Import new songs
EDIT_TRACKS Edit song metadata
DELETE_TRACKS Delete songs from the library
MANAGE_PROFILES Create, update, delete profiles
MANAGE_PERMISSIONS Manage permission groups
MANAGE_SETTINGS Change app settings, switch active providers

Plugins can define additional permissions via ctx.permissions.define(name, description) — these appear alongside the built-in list in the admin panel.

Permission Groups

A PermissionGroup holds a list of profile IDs and a list of permissions. Profiles in the group receive all listed permissions.

  • A profile can belong to multiple groups.
  • Permissions are additive — there is no deny override.
  • The ADMIN permission bypasses all other checks.

Profile API

Method Endpoint Auth Description
GET /api/profiles None List all profiles (safe fields only)
GET /api/profiles/:id None Get single profile
POST /api/profiles ADMIN Create profile
PATCH /api/profiles/:id ADMIN Update profile (name, avatar, settings)
DELETE /api/profiles/:id ADMIN Delete profile + invalidate all sessions
POST /api/auth/login None Login, returns token
POST /api/auth/logout Required Invalidate current token
POST /api/auth/recover None Reset PIN via recovery phrase
GET /api/auth/session Required Get current session + profile

Safe fields — sensitive fields (pinCode, pinSalt, recoveryPhrase, recoveryPhraseSalt) are stripped from every API response by ProfileService.sanitize().


First-Run Setup

On a fresh install with no profiles, /api/setup/status returns { configured: false }. The frontend shows a setup wizard:

  1. GET /api/setup/status{ configured: false, profiles: 0 }
  2. User enters first profile name + PIN
  3. POST /api/setup/init → creates the first profile with ADMIN permission

After setup, the normal login flow applies.


Per-Profile Settings

Each profile stores a profileSettings JSON blob that persists personal preferences independently of the global settings file:

Key Description
theme UI colour theme
renderer Preferred highway renderer (highway2d, modernway, tabmaster)
lyricsVisible Show/hide lyrics overlay
volume Master volume (0–1)
language UI locale (en, de, etc.)

These are read by the frontend on login and written back via PATCH /api/profiles/:id.


See Also

Clone this wiki locally