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/*needsplatform_role: admin, and returns403 Admin access required. Current platform role: userotherwise. - 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 differentazp, which changes which tenant's data you can see at all. See Authentication for the wider model. - Two roles, two claims.
platform_roleunlocks/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: trueand 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 stateThe 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_revokedeverywhere. 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