docs: OIDC PKCE login, LT admin screens, backend-served Flutter build
Plan + both READMEs updated for the Authentik Authorization-Code + PKCE flow, the Leitungsteam admin screen, the widened LT-admin guards, the issuer trailing-slash normalisation, and the backend now serving the Flutter web build (SPA fallback for /v1/auth/callback). Verification section records the local-Postgres E2E for LT admin + onboarding approval, and notes the OIDC browser round-trip still needs a test account. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+34
-8
@@ -7,25 +7,46 @@ added later with `flutter create --platforms=...` in this directory — the
|
||||
|
||||
## Run
|
||||
|
||||
The Authentik redirect URI is `http://localhost:3000/v1/auth/callback`, so
|
||||
the app must be reached on `:3000` — i.e. served by the backend, not `flutter
|
||||
run`'s own dev server. Build it and let NestJS serve it:
|
||||
|
||||
```bash
|
||||
flutter pub get
|
||||
flutter run -d chrome --dart-define=API_BASE=http://localhost:3000/api
|
||||
flutter build web # backend serves client/app/build/web at /
|
||||
# then run the backend (npm run start:dev in ../../backend) and open :3000
|
||||
```
|
||||
|
||||
`API_BASE` defaults to `http://localhost:3000/api` (the local NestJS
|
||||
backend, which also serves the interim plain-HTML client at `/`).
|
||||
For pure UI work without the OIDC flow, `flutter run -d chrome
|
||||
--dart-define=API_BASE=http://localhost:3000/api` still works (guest / local
|
||||
Teamer login only).
|
||||
|
||||
### Dart-defines
|
||||
|
||||
| define | default |
|
||||
|---|---|
|
||||
| `API_BASE` | `http://localhost:3000/api` |
|
||||
| `OIDC_ISSUER` | `https://sso.konfi-castle.com/application/o/konfi-castle-app/` |
|
||||
| `OIDC_CLIENT_ID` | the konfi-castle public client id |
|
||||
| `OIDC_REDIRECT_URI` | `http://localhost:3000/v1/auth/callback` |
|
||||
|
||||
## What's implemented
|
||||
|
||||
- **Login** (`lib/screens/login_screen.dart`) — three tabs:
|
||||
- *Konfi / Gast*: KC invite code + first/last name → `POST /auth/guest`.
|
||||
- *Team-Login*: email + password for local Gemeinde Teamer →
|
||||
`POST /auth/team-login`. (Leitungsteam / Verantwortliche use the
|
||||
Authentik Authorization-Code flow, not yet wired into this client.)
|
||||
- *Leitungsteam / Verantwortliche*: "Mit Konfi-Castle-ID anmelden" starts
|
||||
the Authentik **Authorization Code + PKCE** flow (`lib/oidc.dart`);
|
||||
below it, the local Gemeinde-Teamer password form
|
||||
(`POST /auth/team-login`).
|
||||
- *Einladung*: redeem a Teamer invite token → `POST /auth/teamer/register`.
|
||||
- The token is stored via `shared_preferences` (localStorage on web) and
|
||||
restored on start; `GET /auth/me` resolves the role for a role-aware home.
|
||||
- OIDC: discovery + S256 challenge, `?code=` handled on bootstrap, access +
|
||||
refresh token persisted (`shared_preferences` / localStorage), expired
|
||||
access token refreshed on restart. `GET /auth/me` resolves the role.
|
||||
- **Home** (`lib/screens/home_screen.dart`) — identity card + navigation.
|
||||
- **Verwaltung** (`lib/screens/admin_screen.dart`, Leitungsteam only) —
|
||||
list/create KCs; per KC the Gemeinden (list/create) and pending
|
||||
Verantwortlichen self-registrations (`GET /onboarding/requests`,
|
||||
approve / reject).
|
||||
- **Workshop-Wahl** (`lib/screens/wahl_screen.dart`, guests) — two tabs:
|
||||
*Wünsche* (`GET /wahl/guest/overview`, tap workshops in order, max 3,
|
||||
`POST /wahl/:id/teilnehmer`) and *Ergebnis* (`GET /wahl/guest/results` —
|
||||
@@ -40,6 +61,11 @@ backend, which also serves the interim plain-HTML client at `/`).
|
||||
|
||||
- `lib/api.dart` — `Api` (thin REST wrapper + models) and `AppState`
|
||||
(`ChangeNotifier`: session, login/logout, token persistence).
|
||||
- `lib/oidc.dart` — Authentik PKCE flow. Browser-only bits (sessionStorage,
|
||||
redirect, `window.location`) sit behind a conditional import
|
||||
(`browser.dart` → `browser_web.dart` / `browser_stub.dart`) so
|
||||
`flutter test` compiles on the Dart VM.
|
||||
- `lib/chat_socket.dart` — `/chat` WebSocket wrapper.
|
||||
- `lib/main.dart` — `AppScope` (an `InheritedNotifier<AppState>`) exposes
|
||||
`AppScope.of(context)`; `_AuthGate` switches Login/Home. No third-party
|
||||
state-management package.
|
||||
|
||||
Reference in New Issue
Block a user