# Concepts

> Accounts, apps, API keys, access and refresh tokens, signing keys and email, in two pages.

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)
```

## Who is who

| | 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.

## Tokens

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).

## Signing keys and the JWKS

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.

## API keys

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.

## Email

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).

## Protection that is always on

- **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.

## Errors

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](https://authbase.burakmetehan.com.tr/api/).

## Where to go next

- [Quick start](https://authbase.burakmetehan.com.tr/docs/quickstart.md): run it and integrate an example.
- [Integration guide](https://authbase.burakmetehan.com.tr/docs/integration.md): the API calls and token checks in
  detail, with code for Node, Go and Python.
- [Self-hosting](https://authbase.burakmetehan.com.tr/docs/self-hosting.md): production deployment and runbooks.
- [Security model](https://authbase.burakmetehan.com.tr/docs/security-model.md): what is protected, how, and
  what is left to you.
