From 723248dde5bc222d9b1741d47700c2da55e8553c Mon Sep 17 00:00:00 2001 From: RubenRWU Date: Wed, 6 May 2026 15:17:00 +0200 Subject: [PATCH] restructure: plugin files in /plugin subdir for correct sparse-checkout --- marketplace.json | 2 +- plugin/.claude-plugin/plugin.json | 7 ++ plugin/README.md | 41 +++++++ plugin/skills/onyva-login/SKILL.md | 170 +++++++++++++++++++++++++++++ 4 files changed, 219 insertions(+), 1 deletion(-) create mode 100644 plugin/.claude-plugin/plugin.json create mode 100644 plugin/README.md create mode 100644 plugin/skills/onyva-login/SKILL.md diff --git a/marketplace.json b/marketplace.json index bb110fa..11a6ff3 100644 --- a/marketplace.json +++ b/marketplace.json @@ -15,7 +15,7 @@ "source": { "source": "git-subdir", "url": "https://Ruben:67ba7a5b4c18311b4e16f28360f09fa934664af4@gitea.onyva.dev/Onyva/onyva-login-skill.git", - "path": ".", + "path": "plugin", "ref": "main" } } diff --git a/plugin/.claude-plugin/plugin.json b/plugin/.claude-plugin/plugin.json new file mode 100644 index 0000000..a424156 --- /dev/null +++ b/plugin/.claude-plugin/plugin.json @@ -0,0 +1,7 @@ +{ + "name": "onyva-login", + "description": "Integriert den zentralen Onyva Dev Login (OIDC/SSO) in ein bestehendes Projekt. Analysiert das Projekt und implementiert den vollständigen Authorization Code Flow mit PKCE, zentralem Logout und Rollenzugriff.", + "author": { + "name": "Onyva" + } +} diff --git a/plugin/README.md b/plugin/README.md new file mode 100644 index 0000000..f0b1907 --- /dev/null +++ b/plugin/README.md @@ -0,0 +1,41 @@ +# onyva-login — Claude Code Skill + +Integriert den zentralen **Onyva Dev Login** (OIDC/SSO unter `https://login.onyva.dev`) in ein bestehendes Projekt. + +## Was der Skill macht + +Claude analysiert das aktuelle Projekt, fragt nach den nötigen Credentials und implementiert dann: + +- Authorization Code Flow mit PKCE +- Session-Management mit JWT-Claims +- Auth Guard / Middleware für geschützte Routen +- Zentrales Logout (Single Sign-Out) +- `AUTH_ENABLED=false` Bypass für lokale Entwicklung +- Env-Variablen und `.env.example` + +## Installation + +``` +/plugin install onyva-login@onyva-marketplace +``` + +oder direkt per URL: + +``` +/plugin install https://gitea.onyva.dev/Onyva/onyva-login-skill +``` + +## Nutzung + +In Claude Code: + +``` +/onyva-login +``` + +oder einfach schreiben: *„Integriere den Onyva Login in dieses Projekt"* + +## Voraussetzungen + +- App muss im Admin-Panel von [login.onyva.dev](https://login.onyva.dev) angelegt sein +- `client_id`, `client_secret` und Redirect URI aus dem Admin-Panel bereithalten diff --git a/plugin/skills/onyva-login/SKILL.md b/plugin/skills/onyva-login/SKILL.md new file mode 100644 index 0000000..bb12e82 --- /dev/null +++ b/plugin/skills/onyva-login/SKILL.md @@ -0,0 +1,170 @@ +--- +name: onyva-login +description: Integriert den zentralen Onyva Dev Login (OIDC/SSO) in ein bestehendes Projekt. Nutze diesen Skill wenn jemand "onyva login integrieren", "auth integrieren" oder "/onyva-login" schreibt. +--- + +Deine Aufgabe ist es, den zentralen Onyva Dev Login (OIDC/SSO-Server unter `https://login.onyva.dev`) in das aktuelle Projekt zu integrieren. + +## Vorgehen + +1. **Analysiere das Projekt zuerst** — Lies relevante Dateien (Framework, Routing, vorhandene Auth-Logik, Env-Konfiguration), bevor du Code schreibst. + +2. **Frage nach fehlenden Infos** — Du brauchst: + - `OIDC_CLIENT_ID` und `OIDC_CLIENT_SECRET` (aus dem Admin-Panel von login.onyva.dev) + - `OIDC_REDIRECT_URI` (wohin der Auth-Server nach dem Login zurückleitet — muss im Admin-Panel eingetragen sein) + - `OIDC_LOGOUT_REDIRECT_URI` (wohin nach dem zentralen Logout weitergeleitet wird) + - Falls noch nicht bekannt: welche Routen geschützt sein sollen und welche öffentlich bleiben + +3. **Implementiere die Integration** nach den technischen Vorgaben unten. + +4. **Passe `.env.example`** (oder äquivalente Konfiguration) mit allen nötigen Variablen an. + +--- + +## Technische Vorgaben + +### Auth-Server + +- Issuer / Basis-URL: `https://login.onyva.dev` +- Discovery: `https://login.onyva.dev/.well-known/openid-configuration` + +| Endpunkt | Methode | Zweck | +|---|---|---| +| `/oauth/authorize` | GET | Login starten | +| `/oauth/token` | POST | Code gegen Tokens tauschen | +| `/oauth/userinfo` | GET | Nutzerinfos per Bearer Token | +| `/oauth/jwks.json` | GET | Public Keys (RS256) | +| `/logout` | GET | Zentrales Logout | + +### Login starten + +Generiere `state` (≥16 Zeichen, zufällig) und `code_verifier` (PKCE), speichere beide serverseitig in der Session. Dann Weiterleitung zu: + +``` +GET https://login.onyva.dev/oauth/authorize + ?response_type=code + &client_id={OIDC_CLIENT_ID} + &redirect_uri={OIDC_REDIRECT_URI} + &scope=openid profile email + &state={state} + &code_challenge={BASE64URL(SHA256(code_verifier))} + &code_challenge_method=S256 +``` + +**Wichtig:** `state` muss nachweislich in der URL stehen — ohne `state` kein `state` im Callback. + +### Callback verarbeiten + +```python +# Pseudocode — an das Framework anpassen +def callback(request): + # 1. State validieren + if request.params.get("state") != session.pop("oauth_state", None): + return error("State mismatch") + + # 2. Sonderfall: Auth deaktiviert + if "auth_disabled" in request.params: + session["user"] = {"auth_disabled": True} + return redirect("/") + + # 3. Token-Tausch (application/x-www-form-urlencoded, kein JSON) + resp = http_post("https://login.onyva.dev/oauth/token", data={ + "grant_type": "authorization_code", + "code": request.params["code"], + "redirect_uri": OIDC_REDIRECT_URI, + "client_id": OIDC_CLIENT_ID, + "client_secret": OIDC_CLIENT_SECRET, + "code_verifier": session.pop("pkce_verifier"), + }) + tokens = resp.json() + + # 4. Claims aus id_token lesen + claims = decode_jwt(tokens["id_token"]) # Verifikation optional für interne Nutzung + session["user"] = { + "sub": claims["sub"], # "user:42" + "email": claims["email"], + "name": claims["name"], + "user_type": claims["user_type"], # "employee" | "customer" + "app_roles": claims["app_roles"], # ["admin"] | ["editor"] | ["viewer"] | [] + } + return redirect("/") +``` + +Nach erfolgreichem Callback niemals auf `/auth/*` zurückleiten — `return_to` immer auf `/` oder eine echte App-Seite normalisieren. + +### JWT Claims + +```json +{ + "sub": "user:42", + "email": "nutzer@onyva.de", + "name": "Max Mustermann", + "user_type": "employee", + "app_roles": ["admin"], + "iss": "https://login.onyva.dev", + "aud": "mein-client-id", + "exp": 1700000000 +} +``` + +### Logout + +```python +def logout(request): + session.clear() + return redirect( + f"https://login.onyva.dev/logout" + f"?post_logout_redirect_uri={urllib.parse.quote(OIDC_LOGOUT_REDIRECT_URI)}" + ) +``` + +`post_logout_redirect_uri` muss absolut sein (`https://...`). Der Auth-Server löscht die zentrale Session — der Nutzer ist danach auf allen Onyva-Apps ausgeloggt. + +### Auth-Mode-Verhalten + +| auth_mode | Auth-Server-Verhalten | Client-Verhalten | +|---|---|---| +| `required` | 403 wenn keine Mitgliedschaft | Fehlermeldung anzeigen | +| `optional` | Code für jeden angemeldeten Nutzer; `app_roles` ggf. leer | App entscheidet selbst | +| `disabled` | Sofort zurück mit `?auth_disabled=1` | Kein Login-Dialog, alle Funktionen offen | + +### AUTH_ENABLED=false (Entwicklungsmodus) + +```python +def require_auth(request): + if not AUTH_ENABLED: + return {"sub": "user:0", "email": "dev@local", "name": "Dev", "user_type": "employee", "app_roles": ["admin"]} + # ... normaler Flow +``` + +### Env-Variablen + +```env +OIDC_ISSUER=https://login.onyva.dev +OIDC_CLIENT_ID= # aus Admin-Panel +OIDC_CLIENT_SECRET= # aus Admin-Panel +OIDC_REDIRECT_URI= # muss exakt im Admin-Panel stehen; bei /api-Prefix: https://app.example.com/api/auth/callback +OIDC_LOGOUT_REDIRECT_URI= # z.B. https://app.example.com/ +AUTH_ENABLED=true +``` + +## Wichtige Fallstricke + +- **State ist Pflicht** — wird er nicht mitgeschickt, kommt er nicht zurück und der Callback schlägt fehl +- **Redirect URI exakt** — `/api/auth/callback` ≠ `/auth/callback`; im Admin-Panel muss genau dieselbe URL stehen +- **Token-Endpunkt: Form, kein JSON** — `Content-Type: application/x-www-form-urlencoded` +- **Session-Middleware** — bei Starlette/FastAPI: `itsdangerous` als Dependency erforderlich +- **Öffentliche Routen ausdrücklich ausnehmen** — Upload-Links, Webhooks, Health-Checks müssen vom Auth Guard ausgeschlossen bleiben +- **Kein Redirect auf Auth-Routen** — `return_to` niemals auf `/auth/*` setzen + +## Schnellcheckliste + +- [ ] Env-Variablen konfiguriert und `.env.example` aktualisiert +- [ ] Redirect URI im Admin-Panel von login.onyva.dev eingetragen +- [ ] Login-Route: `state` + PKCE generiert, in Session gespeichert, in Authorize-URL enthalten +- [ ] Callback-Route: `state` validiert, Token-Tausch, Claims in Session +- [ ] Logout-Route: lokale Session gelöscht, Weiterleitung zu zentralem Logout +- [ ] Auth Guard für geschützte Routen +- [ ] Öffentliche Routen explizit ausgenommen +- [ ] `AUTH_ENABLED=false` Bypass für Entwicklung +- [ ] `auth_disabled=1` Callback-Fall abgedeckt