Files
vue-zitadel/README.md
T

113 lines
4.0 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.