ServicesAuth

Auth

The platform's front door: the service that creates accounts, issues the credentials every other service consumes, and decides which tenant a request belongs to. It ships as always-compiled core, not a feature-gated module - email/password and magic-link sign-in, OAuth and passkeys, sessions with refresh rotation, TOTP two-factor, API keys, organizations with roles and invitations, enterprise SSO with DNS-verified domains, and admin user operations.

When to reach for it: signing users up, exchanging a password for a session, minting API keys for machines, grouping users into organizations, or letting an enterprise customer bring their own identity provider.

When not to: authorizing a third-party application against a user's account is the OAuth 2.1 server at OAuth; pushing users in from an external directory is SCIM; per-endpoint request budgets are rate limits. For local development you usually want no service call at all - snug jwt generate signs a token offline, as long as its tenant is already registered.

Concepts

  • Three route prefixes, three access levels. /auth/* is public and needs no credential (signup, signin, refresh, magic links, callbacks). /api/v1/auth/* needs a session or key. /admin/auth/* needs platform_role: admin, and returns 403 Admin access required. Current platform role: user otherwise.
  • A session is not a token. Signing in creates a server-side session (30 days) and hands back a short-lived access token (15 minutes) plus a refresh token. Session state is checked on every request, so revoking a session kills its access token immediately rather than at expiry.
  • Refresh tokens rotate. Each refresh returns a new refresh token and invalidates the one you presented; replaying the old one is 401 invalid_refresh_token.
  • Tenancy travels in azp. Signup registers your user ID as your personal tenant; creating an organization registers the org ID as another. Switching the active organization re-issues your token with a different azp, which changes which tenant's data you can see at all. See Authentication for the wider model.
  • Two roles, two claims. platform_role unlocks /admin/*; org_role (owner, admin, member) governs one organization. An organization owner is not a platform admin.
  • Email verification is checked against the account, not the token. A locally minted JWT carries email_verified: true and clears the shared middleware gate, but auth's own endpoints re-read the stored user, so organization calls still fail until the account itself is verified.

Create an account and use it

snug auth signup --email you@example.com --password '...'
snug auth login --email you@example.com --password '...'
snug --output json auth me
snug auth export            # print the access token for scripting
snug auth logout            # revokes the session, not just local state

The same over raw HTTP. Public routes take no credential; /api/v1/auth/me takes the access token you were issued:

curl -H "Authorization: Bearer $SNUG_API_TOKEN" \
  http://localhost:4000/api/v1/auth/me
{
  "status": 200,
  "msg": "OK",
  "data": {
    "id": "xhMLHYzgKQPSnTSsfcmz",
    "email": "docs4-auth@example.com",
    "email_verified": true,
    "two_factor_enabled": false,
    "role": "user",
    "created_at": "2026-08-28T22:54:13.888920Z",
    "updated_at": "2026-08-28T23:00:31.353348Z"
  }
}

Signup fails closed on the obvious cases: a second signup for the same address is 409 email_already_exists, a password under eight characters is 400 weak_password, and a wrong password at sign-in is 401 invalid_credentials.

API keys

API keys are opaque snug_key_... strings for machines. They carry explicit scopes and never expire unless you say so. They outlive session revocation - verified: after every session of the owning user was revoked, the key kept working - but a password change or reset revokes them along with the sessions.

snug auth api-key create --name "CI" --scopes "kv:write,leaderboard:read" \
  --expires-in-days 30
snug auth api-key list
snug auth api-key get --id <api_key_id>
snug auth api-key revoke --id <api_key_id>

The full secret is returned exactly once, at creation:

{
  "api_key_id": "dzDHgWACXWjGqMxzNpuX",
  "key": "snug_key_a63dddf7...",
  "key_prefix": "snug_key_a63dddf7",
  "name": "docs4-ci",
  "scopes": ["kv:write", "leaderboard:read"],
  "expires_at": "2026-09-27T22:56:40.716433Z"
}

Afterwards list and get return only key_prefix, alongside a last_used_at that updates as the key is used. Four behaviors are worth knowing, all reproduced:

  • Scopes are enforced per service path. The key above writes to KV, but a timeline call is 403 insufficient scope: requires 'timeline:read'.
  • A key with an empty scope list is accepted at creation and then refused on service paths with 403 credential carries no scopes. It can still read /api/v1/auth/me, so an unscoped key is not inert - it is a key that can do nothing useful.
  • Rescoping takes effect immediately on the live key. PATCH /api/v1/auth/api-keys/{id} narrows a leaked key without rotating it; there is no CLI equivalent, so this one is HTTP-only.
  • Revocation is instant, and the key then reports 401 api_key_revoked everywhere. A password change revokes every key the user holds the same way, so rotate credentials into your deployment before changing a service account's password.

Where to go next

  • Sign-in and sessions - every way in (password, magic link, OAuth, passkeys, the CLI browser flow), two-factor, refresh rotation, session revocation, and password and email management.
  • Organizations - creating orgs, inviting and demoting members, and what switching the active organization does to your data.
  • Enterprise SSO - registering a customer's identity provider, claiming and verifying email domains, and enforcing SSO without locking yourself out.
  • Administration - finding users, banning, impersonating, and bulk onboarding. Requires a platform admin token.

Limits and configuration

Access tokens live 15 minutes and sessions 30 days; a user gets 10 concurrent sessions, 20 API keys, and 10 organizations, an organization holds 100 members, and bulk onboarding caps at 100 users per request. Argon2 parameters, TTLs for every kind of emailed token, the per-address email budget, TOTP and WebAuthn settings, and the email provider itself are all tunable through the AUTH_SERVICE_* variables in the auth CONFIG reference.

Reference

  • Auth API - every endpoint, callable
  • Authentication - credentials, scopes, tenants, and roles across the whole platform
  • Related: OAuth for authorizing third-party applications, SCIM for directory-driven provisioning

On this page