Taskbase — Authentication¶
Overview¶
Taskbase uses Keycloak OIDC for authentication. The UI handles the browser login flow; the API validates Bearer JWTs on every request. Any user who is a member of the Skatzi Keycloak group gets full access — there is no role-based access control in v1 (see ADR 0003).
Authentication is optional at runtime: when the relevant environment variables are not set the entire auth layer is skipped. This is the intended behaviour for local development, where running a full Keycloak instance would be unnecessary overhead.
How it works¶
Browser Keycloak platform-tasks-ui platform-tasks-api
| | | |
|-- OIDC login ------->| | |
|<- JWT --------------| | |
|-- load app ---------------------------------->| |
| |-- /api/* + Bearer JWT ->|
| | |-- verify JWT vs JWKS
| | |-- 401 if invalid
| |<- response -------------|
- The UI initialises
keycloak-jswithonLoad: 'login-required'. If the user has no active session, the browser is redirected to the Keycloak login page before the React app renders. - After login,
keycloak-jsholds the access token and refreshes it automatically (when less than 30 seconds remain). - Every API call from the UI includes
Authorization: Bearer <token>. - The API middleware (
api/internal/auth) fetches Keycloak's JWKS via OIDC discovery and verifies the token signature and expiry. Requests without a valid token receive401. /api/healthis excluded from auth — it must remain reachable for Kubernetes liveness probes.
Environment variables¶
API (platform-tasks-api)¶
| Variable | Required in prod | Purpose |
|---|---|---|
KEYCLOAK_ISSUER |
Yes | Full issuer URL, e.g. https://keycloak.prod.skatzi.com/realms/skatzi. When unset, the auth middleware is a no-op. |
UI (platform-tasks-ui)¶
| Variable | Required in prod | Purpose |
|---|---|---|
VITE_KEYCLOAK_URL |
Yes | Keycloak base URL, e.g. https://keycloak.prod.skatzi.com |
VITE_KEYCLOAK_REALM |
No | Realm name — defaults to skatzi |
VITE_KEYCLOAK_CLIENT_ID |
Yes | The Keycloak client ID registered for this app, e.g. taskbase |
VITE_* variables are baked in at build time by Vite. They must be set in the build
environment (CI/CD), not injected at runtime.
When both VITE_KEYCLOAK_URL and VITE_KEYCLOAK_CLIENT_ID are absent, the UI skips the OIDC
flow entirely — no login page, no token, and the "Sign out" button is hidden.
Local development¶
No Keycloak setup is needed. Leave all auth variables unset (they are commented out in
.env.example). The API will log:
and accept all requests without a token.
Keycloak client registration¶
A dedicated taskbase public client must exist in the skatzi realm. Add the following entry to
components/keycloak/base/skatzi-realm.yaml under clients::
- clientId: taskbase
name: "Taskbase"
description: "Task management application"
enabled: true
publicClient: true
standardFlowEnabled: true
directAccessGrantsEnabled: false
webOrigins:
- "https://taskbase.prod.skatzi.com"
redirectUris:
- "https://taskbase.prod.skatzi.com/*"
This follows the same pattern as the existing skatzi-web client. PKCE (S256) is enforced by
keycloak-js on the client side — no additional Keycloak configuration is required for it.
Testing auth locally (optional)¶
If you need to test the full OIDC flow locally, port-forward Keycloak and set the variables:
kubectl port-forward -n keycloak svc/keycloak-service 8081:8080
# .env
KEYCLOAK_ISSUER=http://localhost:8081/realms/skatzi
# ui/.env.local (Vite picks this up automatically)
VITE_KEYCLOAK_URL=http://localhost:8081
VITE_KEYCLOAK_CLIENT_ID=taskbase
The taskbase client in Keycloak will also need http://localhost:5173/* added to its
redirectUris for the Vite dev server to work.