# 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. A unique `handle` is auto-generated from `username` (collision-safe with `-2`, `-3` suffixes). **Response (201):** Same token shape as Login. If the user requires admin approval (`is_active` = false), returns `201` with: ```json { "message": "Account created but requires admin approval", "user_id": "uuid" } ``` ### Login ``` POST /auth/login ``` ```json { "username": "jdoe", "password": "..." } ``` **Response (200):** ```json { "access_token": "eyJ...", "refresh_token": "opaque-string", "token_type": "Bearer", "expires_in": 900, "user": { "id": "uuid", "username": "jdoe", "email": "jdoe@example.com", "display_name": "Jane Doe", "handle": "jdoe", "role": "user", "auth_source": "builtin" } } ``` ### 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).