OAuth ohne Client-Secret

Schnellstart

Der kanonische Issuer ist https://moddingflow.com. Desktop-, Mobile-, CLI- und Mod-Manager-Integrationen verwenden eine öffentliche client_id mit token_endpoint_auth_method=none und erhalten oder senden niemals ein statisches client_secret.

Die App registriert den Client beim ersten Login automatisch einmal. Erzeuge für jede Autorisierung neue Werte für Verifier, S256 Challenge, state und nonce, öffne den Systembrowser, prüfe state und iss und tausche den Code mit client_id und dem ursprünglichen Verifier aus. Nutzer klicken nur auf Anmelden und kopieren weder client_id noch Token.

Sende POST https://moddingflow.com/oauth/register mit Content-Type: application/json und dem minimalen Body {"client_name":"My Mod Manager","redirect_uris":["http://127.0.0.1/callback"]}. Weder Entwicklerkonto, Kontaktmetadaten, manuelle Genehmigung noch Client-Secret sind erforderlich; Basis-Scopes werden automatisch zugewiesen.

Die Antwort enthält client_id, registration_client_uri und einmalig registration_access_token, aber kein client_secret. Die App speichert die öffentliche client_id automatisch. Verwirf den Management-Token, wenn die Registrierung nicht verwaltet wird, oder bewahre ihn nur in einem geschützten Produkt-Backend auf; zeige ihn niemals dem Nutzer.

Authorization Code + PKCE

Erzeuge 32 Zufallsbytes mit dem System-CSPRNG, kodiere den Verifier als base64url ohne Padding und setze code_challenge=BASE64URL(SHA256(verifier)) sowie code_challenge_method=S256. Erzeuge unabhängige Werte für state und OpenID-Connect nonce.

Öffne GET https://moddingflow.com/oauth/authorize?response_type=code&client_id=CLIENT_ID&redirect_uri=CALLBACK&scope=openid%20mods:read&code_challenge=CHALLENGE&code_challenge_method=S256&state=STATE&nonce=NONCE im Systembrowser. Embedded Webviews, Implicit Flow, Password Grant und prompt=none werden nicht unterstützt.

Verlange in Erfolgs- und Fehler-Callbacks exakte Werte für state und iss=https://moddingflow.com. Tausche den fünf Minuten gültigen Code über POST /oauth/token mit grant_type=authorization_code, client_id, code, redirect_uri und code_verifier aus. Ein Client-Secret wird nicht gesendet.

Prüfe OIDC-ID-Tokens über Discovery und JWKS: RS256-Signatur, Issuer, Audience, Ablauf und ursprüngliche nonce. Access Tokens gelten 15 Minuten.

Device Flow

Sende POST /oauth/device_authorization mit client_id und scope, zeige verification_uri_complete und user_code mit allen Vertrauens- und Berechtigungshinweisen. Frage POST /oauth/token mit dem Device Grant nicht schneller als im zurückgegebenen interval ab.

Behandle authorization_pending, slow_down, access_denied und expired_token exakt. Beende Polling nach Ablehnung, Ablauf oder Erfolg und protokolliere weder device_code noch Tokens.

Registrierung und Callbacks

registration_access_token trägt das Präfix mfrg_ und ist ein administrativer Registrierungs-Credential, kein OAuth Access- oder Refresh-Token. Verwirf ihn sofort, wenn der Registrierungs-Lifecycle nicht verwaltet wird. Andernfalls bewahre ihn nur in einem geschützten Produkt-Backend oder Secret Manager auf und übergib ihn als separates managementToken. Zeige ihn nie an und fordere Nutzer nie zum Kopieren auf.

GET registration_client_uri liest Metadaten ohne Token-Anzeige oder Rotation. PUT ersetzt Metadaten und rotiert den Token atomar. DELETE deaktiviert den Client und widerruft Grants. Claim macht den Management-Token dauerhaft ungültig.

Neue Clients dürfen exakte HTTPS-Callbacks, Loopback-IP-Callbacks auf 127.0.0.1 oder [::1] mit dynamischem Port oder Reverse-Domain-Schemata wie com.example.manager:/oauth/callback verwenden. localhost, Fragmente, Userinfo, Wildcard-Hosts und beliebige Schemata werden abgelehnt.

Scopes, Consent und Prüfung

Anonyme Registrierung darf eine Teilmenge von openid, mods:read, files:download, install_plans:resolve und profile:read anfordern. Ohne scope wird dieses Basisset gewählt. Der Consent-Bildschirm erscheint bei jeder Autorisierung, auch bei schon erteilten Rechten.

Für mods:write, Versions- oder Dateiverwaltung, Webhooks, private Daten oder Team-Administration beanspruche den dynamischen Client in den Developer Settings mit client_id und Management-Token. Claim erfordert Same-Origin-Sitzung und AAL2/2FA.

Sensible Anfragen durchlaufen pending, approved oder rejected; der Administrator muss einen Grund angeben und die Entscheidung wird auditiert. rejected ist fail-closed. agent:* und admin:* sind intern und für Drittanbieter immer gesperrt.

Tokens, Widerruf und Fehler

Refresh Tokens rotieren bei jeder Nutzung; Wiederverwendung eines alten Tokens widerruft die ganze Familie. POST /oauth/revoke ist idempotent. Widerruf unter Account Security schließt Consent, Refresh-Familien, ausstehende Codes und Device Grants und macht aktuelle Bearer Tokens sofort ungültig.

invalid_request, invalid_client, invalid_grant, invalid_scope, unauthorized_client, access_denied und interaction_required beenden den aktuellen Versuch. Wiederhole nur dokumentierte temporäre Fehler und beachte Retry-After mit Jitter.

Offene Registrierung ist auf 16 KiB, 10 Anfragen pro IP und Stunde und 50 pro IP und Tag begrenzt. Management, Claim, Device-Polling und Authentifizierungsfehler haben eigene gemeinsame fail-closed Buckets.

SDK-Helfer

TypeScript: const request = await createOAuthAuthorizationRequest({ clientId, redirectUri, scopes: ["openid", "mods:read"] }); speichere request.verifier, request.state und request.nonce und öffne request.authorizationUrl.

Python: request = create_oauth_authorization_request(client_id=client_id, redirect_uri=redirect_uri, scopes=["openid", "mods:read"]); speichere verifier, state und nonce und öffne authorization_url.

C#: var request = ModdingflowPublicApiClient.CreateOAuthAuthorizationRequest(clientId, redirectUri, new[] { "openid", "mods:read" }); speichere Verifier, State und Nonce und öffne AuthorizationUrl.