- Symfony 7.4 LTS, PHP 8.4, FrankenPHP, SQLite (3-Layer src/UI|Logic|Data) - Auth: manueller OidcClient (Authorization-Code) im UI-Layer, kein Security-Bundle/league; Session-Gate via SessionAuthSubscriber - API-Guard via statischem X-Api-Token (ApiTokenSubscriber); ExceptionSubscriber - UserEntity (Auto-Provision) + Init-Migration - Docker (php:8.4-frankenphp) + compose (essen.ljh31.de, traefik-net external) - SmokeTests (7) grün: Redirect-Fälle, API-Token, Auth-Render, Logout Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
36 lines
1.9 KiB
Markdown
36 lines
1.9 KiB
Markdown
# 003 – Auth: Keycloak-OIDC (Web) + statischer API-Token (HA)
|
||
|
||
**Status:** accepted (überarbeitet 2026-09-13; ersetzt „kein Login")
|
||
|
||
**Kontext.** Das Werkzeug wird im privaten Homelab von **einer** Person (bzw.
|
||
Familie via LAN) genutzt. Es gibt bereits eine Keycloak-Instanz
|
||
(`https://iam.ljh31.de`, Realm `family`), die von `open-webui`/`webuix` genutzt
|
||
wird – eine Wiederverwendung ist kostenlos. Home-Assistant soll zusätzlich die
|
||
API (`GET /api/today`) konsumieren; ein zweiter, stateless Auth-Mechanismus ist
|
||
für Consumer wie HA robuster als eine Browser-Session.
|
||
|
||
**Entscheidung.**
|
||
1. **Web-UI:** Keycloak-OIDC (Authorization-Code) gegen Realm `family`,
|
||
eigener Client `essens` (Redirect `https://essen.ljh31.de/auth/callback`).
|
||
Verifiziert wird das Id-Token (RS256 + JWKS); pro Login wird eine lokale
|
||
User-Zeile auto-provisioniert (`essens_user`, gekoppelt an `sub`).
|
||
Die App selbst ist ein einfaches **Session-Auth-Gate**
|
||
(`SessionAuthSubscriber`): Session-Key `auth_sub` fehlt → Redirect `/login`.
|
||
Kein Symfony-Security-Authenticator, keine Rollen.
|
||
2. **API:** optionaler statischer Token per Header `X-Api-Token`
|
||
(env `API_TOKEN`), geprüft durch `ApiTokenSubscriber` (nur `/api/*`).
|
||
Token leer/leer-String → API blockiert; HA bekommt den Token direkt
|
||
(kein Keycloak-Refresh-Flow im Consumer).
|
||
|
||
**Konsequenzen.**
|
||
- + Wiederverwendung vorhandener IdP, keine zweite Auth-Welt.
|
||
- + HA-Integration bleibt trivial (ein Header statt OIDC im Sensor).
|
||
- − Manueller OIDC-Client (JWKS/Id-Token) statt Framework-Lib – bewusst:
|
||
kein `league/oauth2-client`/Security-Bundle im Stack, Code bleibt klein
|
||
und auditierbar.
|
||
- − Zwei Auth-Pfade (Session + Token) – klar getrennt nach Pfad-/Header-Gate.
|
||
|
||
**Abgelöst.** Fassung „Single-User, keine Authentifizierung" (M0-Start) –
|
||
Login via eigener Domain ohne Identität war mit HA-Kopplung und
|
||
Familien-Nutzung nicht mehr akzeptabel.
|