OIDC Login
hzel uses OpenID Connect (OIDC) for browser-based authentication. The identity provider is https://i.hzel.org. All flow endpoints are served from https://api.hzel.org.
Browser → GET https://api.hzel.org/api/v1/auth/oidc/authorize ← 302 redirect to https://i.hzel.org/authorize?…
https://i.hzel.org authenticates the user → GET https://api.hzel.org/api/v1/auth/oidc/callback?code=…&state=…
Backend validates code and state, issues JWTs ← 302 redirect to dashboard callback URLGET /api/v1/auth/oidc/authorize
Section titled “GET /api/v1/auth/oidc/authorize”Redirects the browser to https://i.hzel.org to begin the OIDC flow. Sets a CSRF state cookie before redirecting.
| Header | Value | Required |
|---|---|---|
| — | — | — |
No headers or body required. Visited directly by the browser.
Response: 302 Found → https://i.hzel.org/authorize?client_id=…&state=…
GET /api/v1/auth/oidc/callback
Section titled “GET /api/v1/auth/oidc/callback”Receives the authorization code from https://i.hzel.org, validates the state against the session cookie, exchanges the code for JWTs, and redirects the browser to the dashboard.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
code | string | Yes | Authorization code from https://i.hzel.org |
state | string | Yes | CSRF state token set during the authorize redirect |
Response: 302 Found → dashboard callback URL.
Errors
| Status | error.code | Reason |
|---|---|---|
400 | BAD_REQUEST | Missing code or state parameter |
403 | FORBIDDEN | state does not match the session cookie |
POST /api/v1/auth/refresh
Section titled “POST /api/v1/auth/refresh”Exchanges a valid refresh JWT for a new access/refresh token pair. Tokens rotate on each use.
| Header | Value | Required |
|---|---|---|
Content-Type | application/json | Yes |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
refresh_token | string | Yes | The refresh JWT from the previous token response |
Success 200
{ "data": { "access_token": "eyJ…", "refresh_token": "eyJ…", "token_type": "Bearer", "expires_in": 900 }}Errors
| Status | error.code | Reason |
|---|---|---|
401 | UNAUTHORIZED | Token missing, expired, or revoked |
GET /api/v1/auth/session
Section titled “GET /api/v1/auth/session”Returns the authenticated user’s profile and CSRF token. Use the csrf_token in the x-csrf-token header for all browser-session mutating requests.
| Header | Value | Required |
|---|---|---|
Authorization | Bearer <access_token> | One of these two |
Cookie | session=<value> | One of these two |
Success 200
{ "data": { "user": { "user_id": "01942cf7-…", "email": "user@example.com", "role": "user" }, "session": { "session_id": "…", "csrf_token": "3f2a1b4c-…", "expires_at": "2026-03-28T10:00:00Z", "auth_method": "Oidc" } }}Errors
| Status | error.code | Reason |
|---|---|---|
401 | UNAUTHORIZED | No valid session or token |