feat: generated dummy with cursor

This commit is contained in:
2026-08-21 17:48:35 +02:00
commit 52852fb27f
15 changed files with 1332 additions and 0 deletions
+112
View File
@@ -0,0 +1,112 @@
# Vue + Zitadel — Lernprojekt
Kleine Vue-App, die Login/Logout über Zitadel (OIDC) zeigt — ohne eigenes Passwort-Formular.
**Stack:** Vue 3, TypeScript, Vite
**Zitadel-Instanz:** `https://auth.schild.work`
**Kernkomponente:** `src/components/auth-area.vue`
```bash
pnpm install
pnpm dev
```
---
## Wie funktioniert der Login?
Die App nutzt **OpenID Connect (OIDC)** mit **Authorization Code + PKCE** — der Standard für SPAs.
```
Vue App Zitadel
│ 1. Sign in/up │
│ ─────────────────────►│ Login- oder Register-Seite
│ 2. Redirect mit code │
│ ◄─────────────────────│ ?code=... auf localhost:5173
│ 3. POST /oauth/v2/token│
│ ─────────────────────►│ code + code_verifier (PKCE)
│ 4. access_token │
│ ◄─────────────────────│ (+ id_token, optional refresh_token)
```
**Sign in** → normale Login-Seite von Zitadel.
**Sign up** → gleicher Flow, aber mit `prompt=create` (direkt Registrierung).
Credentials (E-Mail/Passwort) werden **nur auf Zitadel** eingegeben, nie in der Vue-App.
**Logout** → Redirect zu Zitadel `end_session`, danach zurück auf die App.
---
## Wie ist das implementiert?
Alles in `auth-area.vue`:
| Schritt | Was passiert |
|---------|----------------|
| Sign in / Sign up | `redirectToAuthorize()` — PKCE-Verifier erzeugen, Redirect zu `/oauth/v2/authorize` |
| Zurück auf der App | `handleCallback()` liest `?code=` aus der URL |
| Token holen | `exchangeCodeForTokens()` — POST an `/oauth/v2/token` |
| Session speichern | `access_token` und `id_token` in `sessionStorage` |
| App-Start | `loadStoredSession()` — Token aus `sessionStorage` laden |
Konfiguration oben in der Datei:
```ts
const ZITADEL_ISSUER = "https://auth.schild.work";
const CLIENT_ID = "…"; // aus Zitadel Console
const REDIRECT_URI = "http://localhost:5173/";
const POST_LOGOUT_REDIRECT_URI = "http://localhost:5173/";
const SCOPES = "openid profile email offline_access";
```
In Zitadel: Application (User Agent / SPA, PKCE), Redirect URIs und Post Logout Redirect URI eintragen, **Register allowed** für Sign up aktivieren.
---
## Woran erkennt man, dass man eingeloggt ist?
**In der App:** `isAuthenticated` — ein `computed`, das prüft ob `accessToken` gesetzt ist.
```ts
const isAuthenticated = computed(() => accessToken.value !== null);
```
Im Template: eingeloggt → Name/E-Mail + Logout; nicht eingeloggt → Sign in / Sign up.
**User-Anzeige:** Der `id_token` ist ein JWT. Payload decodieren (Mitte zwischen den Punkten) liefert z. B. `email`, `name` — siehe `userLabel` in der Komponente.
**Technisch:** Ein gültiger `access_token` in `sessionStorage` (`zitadel_access_token`) bedeutet: Session vorhanden. Nach Tab-Schließen weg (`sessionStorage`), nach erneutem Öffnen neu einloggen.
---
## API-Aufrufe mit Login
Geschützte APIs erwarten den Token im Header:
```ts
const token = sessionStorage.getItem("zitadel_access_token");
const response = await fetch("https://api.example.com/me", {
headers: {
Authorization: `Bearer ${token}`,
},
});
```
Die **API** (nicht Vue) muss das JWT prüfen — Signatur gegen Zitadel JWKS (`/oauth/v2/keys`), Issuer `https://auth.schild.work`, Ablaufzeit (`exp`).
| Token | Zweck |
|-------|--------|
| `access_token` | API-Aufrufe (`Authorization: Bearer …`) |
| `id_token` | User-Infos in der UI (JWT decodieren) |
| `refresh_token` | Neues `access_token` ohne erneuten Login (Scope `offline_access`) |
**Wichtig:** Token nur an **eigene, vertrauenswürdige APIs** senden. Die API validiert das Token serverseitig — die Vue-App allein „macht“ keine Sicherheit.
---
## Was dieses Projekt nicht macht
- Kein eigenes Registrierungsformular (E-Mail/Passwort direkt an Zitadel) — dafür bräuchte man ein Backend mit Service-User-Token.
- Kein Refresh-Token-Handling — könnte später ergänzt werden.