feat: initial commit
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
# Rust GraphQL Zitadel
|
||||
|
||||
Kleines Lernprojekt für eine geschützte GraphQL-API mit Rust, Actix-Web,
|
||||
async-graphql und Zitadel sowie einer Vue/Vite-SPA als Frontend.
|
||||
|
||||
## Architektur
|
||||
|
||||
```text
|
||||
Vue SPA (Vite, :5173)
|
||||
|
|
||||
| OIDC Authorization Code + PKCE
|
||||
v
|
||||
Zitadel
|
||||
|
|
||||
| Access Token (JWT)
|
||||
v
|
||||
Rust API (Actix-Web, :8080)
|
||||
|
|
||||
| JWT-Signatur, Issuer, Audience und Ablaufzeit prüfen
|
||||
v
|
||||
async-graphql
|
||||
```
|
||||
|
||||
Das Frontend führt den Login bei Zitadel durch. Die Rust-Anwendung führt
|
||||
keinen Login durch, sondern arbeitet als OAuth-Resource-Server: Jede
|
||||
GraphQL-Anfrage muss ein gültiges Access Token im `Authorization`-Header
|
||||
enthalten.
|
||||
|
||||
## Verzeichnisstruktur
|
||||
|
||||
```text
|
||||
src/
|
||||
├── main.rs # Konfiguration laden und Actix-Server starten
|
||||
├── config.rs # config.toml in typisierte Settings laden
|
||||
├── auth.rs # OIDC Discovery, JWKS und JWT-Validierung
|
||||
└── graphql.rs # GraphQL-Schema und geschützter Handler
|
||||
|
||||
web/src/
|
||||
├── App.vue # Seitenfluss und Zusammenspiel der Module
|
||||
├── auth/oidc.ts # OIDC-Client und Zitadel-Redirects
|
||||
├── auth/useAuth.ts # Authentifizierungszustand im Vue-Lifecycle
|
||||
├── graphql/client.ts # GraphQL-Request mit Bearer Token
|
||||
└── components/ # Login- und Ergebnisdarstellung
|
||||
```
|
||||
|
||||
## Konfiguration
|
||||
|
||||
Backend-Konfiguration:
|
||||
|
||||
```bash
|
||||
cp config.example.toml config.toml
|
||||
```
|
||||
|
||||
`config.toml` wird absichtlich nicht versioniert. Die wichtigsten Werte sind:
|
||||
|
||||
```toml
|
||||
[server]
|
||||
host = "127.0.0.1"
|
||||
port = 8080
|
||||
|
||||
[zitadel]
|
||||
issuer_url = "https://deine-instanz.zitadel.cloud"
|
||||
audience = "deine-api-audience"
|
||||
```
|
||||
|
||||
`issuer_url` ist die Zitadel-Instanz. `audience` muss einem Wert im `aud`-
|
||||
Claim des Access Tokens entsprechen, für dieses Projekt normalerweise der
|
||||
Zitadel-Project-ID.
|
||||
|
||||
Frontend-Konfiguration:
|
||||
|
||||
```bash
|
||||
cp web/.env.example web/.env.local
|
||||
```
|
||||
|
||||
```env
|
||||
VITE_ZITADEL_AUTHORITY=https://deine-instanz.zitadel.cloud
|
||||
VITE_ZITADEL_CLIENT_ID=deine-spa-client-id
|
||||
VITE_ZITADEL_PROJECT_ID=deine-projekt-id
|
||||
```
|
||||
|
||||
Die SPA muss in Zitadel als Single-Page Application angelegt sein. Für die
|
||||
lokale Entwicklung werden diese Redirect-URIs benötigt:
|
||||
|
||||
```text
|
||||
http://localhost:5173/callback
|
||||
http://localhost:5173/
|
||||
```
|
||||
|
||||
Die erste URI ist die Login-Callback-URI, die zweite die Post-Logout-URI.
|
||||
Wenn die Anwendung über `127.0.0.1` geöffnet wird, müssen die entsprechenden
|
||||
URIs mit `127.0.0.1` ebenfalls in Zitadel eingetragen werden.
|
||||
|
||||
Der Access-Token-Typ der Zitadel-Anwendung muss auf **JWT** stehen. Ein
|
||||
opaker beziehungsweise verschlüsselter Token kann nicht mit den öffentlichen
|
||||
JWKS-Schlüsseln lokal validiert werden; dafür wäre stattdessen Token
|
||||
Introspection erforderlich.
|
||||
|
||||
## Lokale Entwicklung
|
||||
|
||||
Mit Devenv werden Backend und Frontend gemeinsam gestartet:
|
||||
|
||||
```bash
|
||||
devenv up
|
||||
```
|
||||
|
||||
Alternativ einzeln:
|
||||
|
||||
```bash
|
||||
# Repository-Root
|
||||
cargo run
|
||||
|
||||
# web/
|
||||
```
|
||||
|
||||
Der Vite-Entwicklungsserver proxied `/graphql` an das Rust-Backend. Dadurch
|
||||
ruft der Browser `/graphql` auf Port `5173` auf, während die Anfrage intern an
|
||||
`http://127.0.0.1:8080/graphql` weitergeleitet wird.
|
||||
|
||||
## Request-Fluss
|
||||
|
||||
1. Die SPA startet den Authorization-Code-Flow mit PKCE.
|
||||
2. Zitadel authentifiziert den Benutzer und leitet zu `/callback` zurück.
|
||||
3. `oidc-client-ts` tauscht den Code gegen Tokens und speichert die Session
|
||||
in `sessionStorage`.
|
||||
4. Das Frontend sendet das Access Token als `Bearer`-Token an `/graphql`.
|
||||
5. Actix-Web validiert Signatur, Issuer, Audience und Ablaufzeit.
|
||||
6. Erst danach wird die GraphQL-Abfrage ausgeführt.
|
||||
|
||||
## Prüfungen
|
||||
|
||||
Backend aus dem Repository-Root:
|
||||
|
||||
```bash
|
||||
cargo fmt --check
|
||||
cargo check
|
||||
cargo test
|
||||
cargo clippy -- -D warnings
|
||||
```
|
||||
|
||||
Frontend aus `web/`:
|
||||
|
||||
```bash
|
||||
pnpm build
|
||||
```
|
||||
|
||||
## Häufige Fehler
|
||||
|
||||
- `redirect_uri is missing`: Die exakte Callback-URI fehlt in der Zitadel-SPA-Anwendung.
|
||||
- `401 Unauthorized`: Das Access Token fehlt, ist abgelaufen oder Issuer/Audience stimmen nicht.
|
||||
- `Base64 error` mit fünf Token-Teilen: Zitadel liefert keinen normalen signierten JWT; den Access-Token-Typ auf JWT stellen oder Introspection implementieren.
|
||||
Reference in New Issue
Block a user