176 lines
4.3 KiB
Markdown
176 lines
4.3 KiB
Markdown
# Auth
|
|
|
|
Three auth modes, configured via `AUTH_MODE` env var. All three issue the
|
|
same JWT after authentication — downstream middleware is auth-mode-agnostic.
|
|
|
|
## Builtin Auth (default)
|
|
|
|
Username/password authentication with bcrypt hashing.
|
|
|
|
### Register
|
|
|
|
```
|
|
POST /auth/register
|
|
```
|
|
|
|
```json
|
|
{
|
|
"username": "jdoe",
|
|
"password": "...",
|
|
"email": "jdoe@example.com",
|
|
"display_name": "Jane Doe"
|
|
}
|
|
```
|
|
|
|
Gated by `allow_registration` policy. Returns same shape as Login.
|
|
A unique `handle` is auto-generated from `username` (collision-safe
|
|
with `-2`, `-3` suffixes).
|
|
|
|
### Login
|
|
|
|
```
|
|
POST /auth/login
|
|
```
|
|
|
|
```json
|
|
{ "username": "jdoe", "password": "..." }
|
|
```
|
|
|
|
**Response:**
|
|
|
|
```json
|
|
{
|
|
"access_token": "eyJ...",
|
|
"refresh_token": "opaque-string",
|
|
"user": {
|
|
"id": "uuid",
|
|
"username": "jdoe",
|
|
"email": "jdoe@example.com",
|
|
"display_name": "Jane Doe",
|
|
"handle": "jdoe",
|
|
"role": "user",
|
|
"auth_source": "builtin",
|
|
"avatar_url": "/api/v1/profile/avatar?v=1234",
|
|
"created_at": "...",
|
|
"last_login": "..."
|
|
}
|
|
}
|
|
```
|
|
|
|
### Refresh
|
|
|
|
```
|
|
POST /auth/refresh
|
|
```
|
|
|
|
```json
|
|
{ "refresh_token": "opaque-string" }
|
|
```
|
|
|
|
Returns new `access_token` and `refresh_token`. Old refresh token is
|
|
invalidated (rotation).
|
|
|
|
### Logout
|
|
|
|
```
|
|
POST /auth/logout
|
|
```
|
|
|
|
```json
|
|
{ "refresh_token": "opaque-string" }
|
|
```
|
|
|
|
Revokes the refresh token. Returns `{ "message": "logged out" }`.
|
|
|
|
## mTLS Auth
|
|
|
|
Mutual TLS via reverse proxy headers. The proxy terminates TLS and
|
|
forwards cert DN fields in headers. The backend auto-provisions
|
|
users on first connection.
|
|
|
|
**Configured by:** `AUTH_MODE=mtls` + `MTLS_HEADER_*` env vars.
|
|
|
|
No explicit login/register endpoints. User identity is extracted from
|
|
the TLS certificate on every request. `password_hash` is NULL for
|
|
mTLS users.
|
|
|
|
**Headers parsed:**
|
|
- `MTLS_HEADER_CN` (default: `X-SSL-Client-CN`) — common name → username
|
|
- `MTLS_HEADER_DN` (default: `X-SSL-Client-DN`) — distinguished name → metadata
|
|
- `MTLS_HEADER_VERIFY` (default: `X-SSL-Client-Verify`) — must be `SUCCESS`
|
|
- `MTLS_HEADER_FINGERPRINT` (default: `X-SSL-Client-Fingerprint`) — stable identity
|
|
|
|
On first request with a valid cert, the system creates a user with
|
|
`auth_source=mtls` and `external_id=fingerprint`.
|
|
|
|
## OIDC Auth
|
|
|
|
OpenID Connect authorization code flow. Tested with Keycloak but
|
|
compatible with any OIDC-compliant IdP.
|
|
|
|
**Configured by:** `AUTH_MODE=oidc` + `OIDC_*` env vars.
|
|
|
|
### OIDC Login (redirect)
|
|
|
|
```
|
|
GET /auth/oidc/login
|
|
```
|
|
|
|
Redirects to the IdP authorization endpoint. Stores `state` + `nonce`
|
|
in `oidc_auth_state` table (cleaned up after callback).
|
|
|
|
### OIDC Callback
|
|
|
|
```
|
|
GET /auth/oidc/callback?code=...&state=...
|
|
```
|
|
|
|
Exchanges authorization code for tokens, validates ID token signature
|
|
via JWKS, extracts claims. Auto-provisions user on first login with
|
|
`auth_source=oidc` and `external_id=sub`. Returns HTML fragment that
|
|
passes tokens to the frontend via URL fragment.
|
|
|
|
**Claim mapping:**
|
|
- `sub` → `external_id`
|
|
- `preferred_username` → `username` + `handle`
|
|
- `email` → `email`
|
|
- `name` → `display_name`
|
|
- `groups` → synced to internal groups with `source=oidc`
|
|
|
|
**Split-horizon issuer:** `OIDC_EXTERNAL_ISSUER_URL` can differ from
|
|
`OIDC_ISSUER_URL` when the IdP's internal address (Docker/K8s service)
|
|
differs from its external address.
|
|
|
|
**OIDC env vars:**
|
|
|
|
| Var | Required | Description |
|
|
|-----|----------|-------------|
|
|
| `OIDC_ISSUER_URL` | Yes | IdP issuer URL for discovery |
|
|
| `OIDC_EXTERNAL_ISSUER_URL` | No | External issuer (if different from internal) |
|
|
| `OIDC_CLIENT_ID` | Yes | Client ID |
|
|
| `OIDC_CLIENT_SECRET` | Yes | Client secret |
|
|
| `OIDC_REDIRECT_URL` | No | Callback URL (auto-derived if not set) |
|
|
|
|
## Login Page Adaptation
|
|
|
|
The `/login` page adapts by `AUTH_MODE`:
|
|
|
|
| Mode | UI |
|
|
|------|-----|
|
|
| `builtin` | Username/password form + register link |
|
|
| `mtls` | Certificate status display |
|
|
| `oidc` | "Sign in with SSO" button |
|
|
|
|
## User Identity Fields
|
|
|
|
All auth modes produce users with:
|
|
|
|
| Field | Description |
|
|
|-------|-------------|
|
|
| `auth_source` | `builtin`, `mtls`, or `oidc` |
|
|
| `external_id` | IdP subject (OIDC) or cert fingerprint (mTLS). NULL for builtin. |
|
|
| `handle` | Unique @mention handle. Auto-generated, collision-safe. |
|
|
|
|
`handle` is the canonical identifier for @mentions (replacing username
|
|
in the resolution chain since v0.24.0).
|