docs: add Keycloak SSO integration design

This commit is contained in:
2026-03-01 00:37:49 +01:00
parent 4c4cdf0a52
commit 40113bc634

View File

@@ -0,0 +1,75 @@
# Keycloak SSO Integration Design
**Goal:** Replace local username/password auth with Keycloak OIDC using a backend callback flow.
**Architecture:** Backend acts as OIDC confidential client. Browser is redirected to Keycloak, which redirects back to a backend callback endpoint. Backend validates the token, checks group membership, provisions the user, and issues its own httpOnly JWT cookie. Frontend is unchanged except the login page.
**Tech Stack:** authlib (OIDC), FastAPI, Alembic, React
---
## Auth Flow
```
Browser → GET /api/auth/oidc/login
→ backend generates state, stores in short-lived cookie, redirects to Keycloak
Keycloak → user authenticates → redirects to:
GET /api/auth/oidc/callback?code=...&state=...
Backend:
1. Validates state cookie (CSRF protection)
2. Exchanges code for tokens via Keycloak token endpoint
3. Validates ID token signature via Keycloak JWKS (authlib handles this)
4. Checks groups claim for "firewall admins" → 403 if absent
5. Looks up user by keycloak_sub → auto-provisions row if first login
6. Issues httpOnly JWT cookie (same mechanism as before)
7. Redirects browser to /
```
Removed endpoints: `POST /auth/login`, `POST /auth/register`
Kept endpoints: `GET /auth/me`, `POST /auth/logout`
## Data Model
New Alembic migration:
- Add `keycloak_sub VARCHAR(255) UNIQUE` to `users` table
- Make `hashed_password` nullable (always NULL for SSO users; kept for schema stability)
## Configuration
**ConfigMap** (non-secret):
- `KEYCLOAK_URL`: `https://sso.baumann.gr`
- `KEYCLOAK_REALM`: `homelab`
- `KEYCLOAK_CLIENT_ID`: `shorefront`
**Secret** (added to `scripts/create-secrets.sh`):
- `KEYCLOAK_CLIENT_SECRET`
**Redirect URI** (backend callback, registered in Keycloak):
- `https://shorefront.baumann.gr/api/auth/oidc/callback`
## Backend Changes
- Add `authlib` + `httpx` to `requirements.txt`
- Add `keycloak_url`, `keycloak_realm`, `keycloak_client_id`, `keycloak_client_secret` to `Settings`
- Add `keycloak_sub` column to `User` model
- New migration: add `keycloak_sub`, make `hashed_password` nullable
- Replace `backend/app/api/auth.py` with OIDC endpoints:
- `GET /auth/oidc/login` — generate state, redirect to Keycloak
- `GET /auth/oidc/callback` — exchange code, validate token, check group, provision user, set cookie, redirect
- Keep `POST /auth/logout`, `GET /auth/me`
- Remove `hash_password`, `verify_password` from `auth.py`
## Frontend Changes
- `Login.tsx`: replace username/password form with a single "Sign in with SSO" button (`window.location.href = '/api/auth/oidc/login'`)
- All other components unchanged
## Keycloak Manual Setup (pre-deploy)
1. Create client `shorefront`, access type: confidential
2. Set Valid Redirect URIs: `https://shorefront.baumann.gr/api/auth/oidc/callback`
3. Set Web Origins: `https://shorefront.baumann.gr`
4. Add Group Membership mapper on client: include groups in ID token, claim name `groups`
5. Create group `firewall admins`, add users to it