2.9 KiB
2.9 KiB
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) UNIQUEtouserstable - Make
hashed_passwordnullable (always NULL for SSO users; kept for schema stability)
Configuration
ConfigMap (non-secret):
KEYCLOAK_URL:https://sso.baumann.grKEYCLOAK_REALM:homelabKEYCLOAK_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+httpxtorequirements.txt - Add
keycloak_url,keycloak_realm,keycloak_client_id,keycloak_client_secrettoSettings - Add
keycloak_subcolumn toUsermodel - New migration: add
keycloak_sub, makehashed_passwordnullable - Replace
backend/app/api/auth.pywith OIDC endpoints:GET /auth/oidc/login— generate state, redirect to KeycloakGET /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_passwordfromauth.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)
- Create client
shorefront, access type: confidential - Set Valid Redirect URIs:
https://shorefront.baumann.gr/api/auth/oidc/callback - Set Web Origins:
https://shorefront.baumann.gr - Add Group Membership mapper on client: include groups in ID token, claim name
groups - Create group
firewall admins, add users to it