Skip to content
Early preview. authbase is in active development. The source code and packages are not public yet; these docs describe how it works ahead of the first public release.

Concepts

authbase is an authentication service you run yourself. Your products keep their own databases and business logic; authbase owns their users’ credentials and issues the tokens they use.

end-user ──▶ your backend ──(API key)──▶ authbase ──▶ PostgreSQL
│ │
└── verifies tokens ◀── JWKS (public keys, cached)
What Authenticates with
Instance One authbase deployment: one binary, one PostgreSQL, one master key. Run by an operator. —
Account A developer who uses the dashboard. Owns apps. email + password → dashboard session cookie
App One of your products (e.g. Cashmate). Has its own users, keys and settings; nothing is shared between apps. —
API key Your backend’s credential for one app: sk_live_…, server-side only. Authorization: Bearer sk_live_…
End-user A user of your product, stored in an app. email or username + password, through your backend

An app is identified by an immutable slug; its issuer is {PUBLIC_URL}/apps/{slug}, and its app id (a UUID) is the audience of its tokens. Both are on the app’s overview page in the dashboard.

A successful login returns two tokens:

Access token Refresh token
Form JWT (RFC 9068), signed with Ed25519 (alg: EdDSA, typ: at+jwt) opaque rt_…, stored only as a hash
Lifetime 15 minutes by default (5–60, per app) 30 days by default (1 hour–1 year, per app), renewed on every refresh
Who checks it your backend, locally, with the app’s public keys authbase, when you call /v1/auth/refresh
Revocable no: it expires. Keep it short. yes: logout, “sign out everywhere”, password change

The access token carries sub (the user id), email, email_verified, username, roles, sid (the login it came from) and client_id/aud (the app id). User metadata is not in it: fetch the user when you need it.

Every refresh returns a new refresh token and makes the old one unusable. Presenting a used one again is treated as theft: the whole chain from that login (the family) is revoked and every holder must log in again (refresh_token_reused).

Consequences worth knowing:

  • Disabling or deleting a user, or signing them out everywhere, stops new tokens at once; access tokens already issued stay valid until they expire (at most the access-token lifetime).
  • Roles changed in authbase appear in the next token (login or refresh).

Each app has one active Ed25519 signing key. Its public half is published, with any retiring key, at {issuer}/.well-known/jwks.json (cacheable for 5 minutes); the private half is stored encrypted under the instance’s master key and never leaves authbase.

Rotating a signing key (dashboard or API) makes a new active key; the old one keeps being published until every token it signed has expired, then disappears. Verifiers that refetch the JWKS on an unknown kid (the SDKs do, at most once a second) need no restart.

An app can have several API keys at once. Rotate creates a new key and gives every other key a grace period (24 hours by default) in which both work, so you can deploy the new one without downtime; revoke stops a key immediately. Keys are shown once and stored only as hashes.

With SMTP configured, authbase sends password-reset and verification links (single-use, 30 minutes and 24 hours) and security notices when a password or email changes. Links go to authbase’s hosted pages, which work without JavaScript, or to your own pages (password_reset_url, email_verification_url in the app’s settings). Per app, email_verification is off, optional (sent, never blocks) or required (login waits for confirmation).

  • Rate limits per client IP on login, sign-up and email requests, and per API key; lockout of an identifier after repeated failed logins (1 minute, doubling to 1 hour). Answers are 429 with Retry-After.
  • Passwords: 12–128 characters, checked against the 100,000 most common leaked passwords, hashed with Argon2id.
  • Audit log per app: logins, failures, token reuse, user and key changes, with actor, IP and time; filterable in the dashboard.

Every error is application/problem+json (RFC 9457) with a stable code to branch on (invalid_credentials, account_locked, token_expired, …), a detail for people and a request_id to quote when reporting a problem. The SDKs raise them as AuthbaseError / AuthbaseException. The full list is in the API reference.