Authentifizierung
Die REST-API authentifiziert jede Anfrage mit einem OAuth2-/OIDC-Access-Token (einem Bearer-JWT), das von unserem Identity-Provider (Keycloak) ausgestellt wird. Es gibt keinen separaten API-Schlüssel-Mechanismus — Server-zu-Server-Integrationen verwenden den OAuth2-Flow Client Credentials, um dieselbe Art von Token zu erhalten.
Ein Access-Token verwenden
Fügen Sie das Token bei jeder Anfrage im Authorization-Header ein:
curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
https://api.tourfold.com/api/v2/areas
Anfragen ohne gültiges Token erhalten 401 Unauthorized — Payload und Retry-Regeln finden Sie unter
Fehlerantworten.
Access-Token beziehen
Token werden von unserem Keycloak-Realm ausgestellt. Die Endpoints des Realms sind hier auffindbar:
https://auth.tourfold.com/auth/realms/tourfold/.well-known/openid-configuration
Authorization Code Flow (empfohlen für nutzerorientierte Apps)
- Leiten Sie den Benutzer an den Authorization-Endpoint weiter:
https://auth.tourfold.com/auth/realms/tourfold/protocol/openid-connect/auth
?client_id=YOUR_CLIENT_ID
&response_type=code
&redirect_uri=YOUR_REDIRECT_URI
&scope=openid profile email
&state=YOUR_STATE_VALUE
- Tauschen Sie den Authorization Code gegen Token ein:
curl -X POST https://auth.tourfold.com/auth/realms/tourfold/protocol/openid-connect/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET" \
-d "code=AUTHORIZATION_CODE" \
-d "redirect_uri=YOUR_REDIRECT_URI"
Antwort:
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 300,
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
}
Client Credentials Flow (Server-zu-Server)
Verwenden Sie für Hintergrund-Jobs und automatisierte Systeme einen vertraulichen Client und den Client-Credentials-Grant, um ein Token direkt zu beziehen — ohne Benutzerinteraktion:
curl -X POST https://auth.tourfold.com/auth/realms/tourfold/protocol/openid-connect/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET"
Scopes und Autorisierung
Fordern Sie bei der Authentifizierung die üblichen OIDC-Scopes an:
openid— für OIDC-Konformität erforderlichprofile— Zugriff auf grundlegende Profil-Claimsemail— Zugriff auf den E-Mail-Claim
Der Zugriff auf einzelne Ressourcen wird über die Rollen und Grants des Workspace des Aufrufers
geregelt, nicht über OAuth-Scopes. Das Token identifiziert, wer Sie sind und in welchem Workspace
Sie agieren; was Sie lesen oder schreiben dürfen, entscheiden die Rollenzuweisungen dieser Identität.
Ein Token, das gültig ist, aber den erforderlichen Grant nicht besitzt, erhält
403 Forbidden; der fehlende Grant steht in data.required_grants, sofern er statisch
bekannt ist.
Token-Verwaltung
Ein Access-Token erneuern
Access-Token sind kurzlebig. Verwenden Sie das Refresh-Token, um ein neues zu erhalten:
curl -X POST https://auth.tourfold.com/auth/realms/tourfold/protocol/openid-connect/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=refresh_token" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET" \
-d "refresh_token=YOUR_REFRESH_TOKEN"
Eine Session widerrufen
curl -X POST https://auth.tourfold.com/auth/realms/tourfold/protocol/openid-connect/logout \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET" \
-d "refresh_token=YOUR_REFRESH_TOKEN"
Sicherheits-Best-Practices
- Bewahren Sie Token und Client-Secrets sicher auf — in Umgebungsvariablen oder einem Secrets-Manager, niemals in der Versionsverwaltung.
- Behandeln Sie den Ablauf — implementieren Sie eine Refresh-Token-Logik für langlaufende Anwendungen.
- Nur HTTPS verwenden — senden Sie Token niemals über unverschlüsselte Verbindungen.
- Fordern Sie nur die Scopes an, die Sie benötigen.
- Verwenden Sie einen eigenen Client je Umgebung — getrennte Clients/Anmeldedaten für Entwicklung, Staging und Produktion.
Enterprise: dedizierter Realm
Standardmäßig authentifizieren sich alle Workspaces gegen den gemeinsamen tourfold-Realm.
Enterprise-Workspaces können mit einem eigenen dedizierten Keycloak-Realm bereitgestellt werden
— etwa um einen eigenen Identity-Provider / SSO, eigene Passwort- und Session-Richtlinien oder eine
isolierte User-Federation einzubringen. Wird ein dedizierter Realm verwendet, wird das Segment
tourfold in den obigen Endpoints durch den Realm-Namen des Workspace ersetzt. Kontaktieren Sie den
Support, um einen dedizierten Realm einzurichten.
Fehlerantworten
Fehler bei Authentifizierung und Autorisierung sind RFC-9457-Problemdokumente und werden — wie jeder
andere API-Fehler — als application/problem+json ausgeliefert. Zwei Statuscodes sind hier relevant:
401 Unauthorized— die Anfrage enthielt keine verwendbare Identität. Behoben wird das durch das Beziehen oder Erneuern von Anmeldedaten.403 Forbidden— die Anfrage ist authentifiziert, aber diese Identität darf die Aktion nicht ausführen. Andere Anmeldedaten helfen nicht; andere Grants schon.
Beide enthalten ein maschinenlesbares data.reason, damit ein Client das weitere Vorgehen ohne
Auswertung des menschenlesbaren detail-Texts entscheiden kann.
Das unten beschriebene Autorisierungs-Vokabular gilt für access-denied und spezifischere
Autorisierungsablehnungen. Ein typspezifischer 403-Fehler kann einen eigenen Ursachenwert
definieren; tenant-disabled ist das bestehende, unten erläuterte Beispiel.
Behandeln Sie data als optional — greifen Sie niemals ohne Existenzprüfung auf data.reason zu.
Bei 401 ist data.reason immer vorhanden. Bei 403 ist es vorhanden, sobald die Ursache bekannt ist,
und data.required_grants nur dann, wenn die Ablehnung auf Grants beruht und die erforderlichen
Grants statisch bekannt sind (eine als eigene Regel implementierte Prüfung kann keine benennen).
401 — nicht authentifiziert
Wird zurückgegeben, wenn der Authorization-Header fehlt oder das Token abgelaufen, fehlerhaft oder
von einem nicht vertrauenswürdigen Issuer ausgestellt ist:
{
"type": "https://problems.tourfold.com/authentication-required",
"title": "Authentication required",
"status": 401,
"detail": "Valid authentication credentials are required",
"data": {
"reason": "token_expired"
}
}
Der Problemtyp type bleibt für alle drei Ursachen authentication-required — die Ursache steht in
data.reason, nicht in einer separaten Typ-URI:
data.reason | Ursache | Was der Client tun soll |
|---|---|---|
token_missing | Es wurden überhaupt keine Anmeldedaten übermittelt. | Über einen der oben beschriebenen Flows authentifizieren. Eine unveränderte Wiederholung scheitert identisch. |
token_expired | Ein wohlgeformtes Token, dessen Gültigkeit abgelaufen ist. | Access-Token erneuern und die Anfrage einmal wiederholen. Nur diese Ursache rechtfertigt einen automatischen Retry. |
token_invalid | Fehlerhaftes Token, ungültige Signatur oder nicht vertrauenswürdiger Issuer. | Keinen blinden Retry ausführen — dasselbe Token scheitert weiterhin. Token verwerfen und neu authentifizieren. |
Jede 401-Antwort enthält zusätzlich einen WWW-Authenticate-Header:
- Ein Token wurde übermittelt, aber abgelehnt → der Challenge enthält die Fehlerparameter nach
RFC 6750, zum Beispiel
WWW-Authenticate: Bearer error="invalid_token", error_description="...". - Es wurde kein Token gesendet → ein einfaches
WWW-Authenticate: Bearer.
Ein weiterer Problemtyp führt zu 401: https://problems.tourfold.com/login-failed
(„Login failed“). Er deckt einen abgelehnten Login mit Anmeldedaten ab, nicht ein fehlendes oder
unbrauchbares Bearer-Token.
Token-Erneuerung implementieren
Das Ursachen-Vokabular lässt sich direkt auf eine Refresh-Logik abbilden:
401mitdata.reason=token_expired→ Refresh-Token gegen ein neues Access-Token tauschen und die ursprüngliche Anfrage einmal wiederholen.401mitdata.reason=token_missing→ es sind keine Anmeldedaten in der API angekommen. Client korrigieren und authentifizieren.401mitdata.reason=token_invalid→ abbrechen. Das zwischengespeicherte Token verwerfen und neu authentifizieren; eine Wiederholung scheitert garantiert und verbraucht nur Rate-Limit-Budget.- Der Refresh-Aufruf selbst scheitert → das Refresh-Token ist abgelaufen oder widerrufen; den vollständigen Authorization-Flow erneut durchlaufen.
403 — authentifiziert, aber nicht berechtigt
Wird zurückgegeben, wenn der Aufrufer bekannt ist, die Aktion aber nicht ausführen darf:
{
"type": "https://problems.tourfold.com/access-denied",
"title": "Access denied",
"status": 403,
"detail": "You don't have permission to perform this action",
"data": {
"reason": "missing_grant",
"required_grants": ["users:update"]
}
}
Bei access-denied und spezifischeren Autorisierungsablehnungen ist data.reason einer der
folgenden Werte:
data.reason | Bedeutung |
|---|---|
missing_grant | Dem Aufrufer fehlt einer der für die Operation erforderlichen Grants. |
not_owner | Der Aufrufer ist nicht der Eigentümer der Zielressource. |
not_author | Der Aufrufer hat die Zielressource nicht erstellt. |
not_member | Der Aufrufer ist kein Mitglied der Gruppe, zu der die Ressource gehört. |
plan_restricted | Der Plan bzw. die Feature-Flags des Workspace enthalten diese Funktion nicht. |
tenant_scope | Die Ressource gehört zu einem anderen Workspace als dem des Aufrufers. |
resource_locked | Die Ressource befindet sich in einem Zustand, der die Aktion für alle verbietet. |
Ein deaktivierter Workspace wird als typspezifisches Problem
https://problems.tourfold.com/tenant-disabled mit data.reason = TENANT_INACTIVE gemeldet. Prüfen
Sie zuerst type, bevor Sie typspezifische data-Felder auswerten; behandeln Sie
TENANT_INACTIVE nicht als zusätzlichen Wert der geschlossenen Liste für Autorisierungsablehnungen.
data.required_grants listet die Grants auf, die die Prüfung erfüllt hätten. Das Feld ist nur
vorhanden, wenn die Ablehnung grant-basiert ist und die erforderlichen Grants statisch bekannt sind —
Clients müssen es deshalb als optional behandeln. Grant-Werte sind durch Doppelpunkte getrennt, zum
Beispiel users:read, users:update, tenant:ownership:update,
custom-objects:definitions:update, webhooks:create.
Eine 403-Antwort zu wiederholen hilft für sich genommen nie: entweder benötigt der Aufrufer eine
Rolle mit dem fehlenden Grant, oder die Aktion ist für diese Ressource nicht verfügbar.
Die interne Admin-Control-Plane (admin-v1) ist bewusst schlanker gehalten. Sie authentifiziert
Aufrufer und liefert 401 in genau der oben beschriebenen Form, besitzt aber kein
Autorisierungsmodell auf Grant-Ebene und erzeugt daher keine grant-basierten 403-Antworten.
Die meisten Workspace-bezogenen Operationen durchlaufen zusätzlich Prüfungen des Tenant-Zustands.
Ein deaktivierter Workspace liefert 403 tenant-disabled; ein aufgrund der Abrechnung gesperrter
Workspace liefert 402 tenant-billing-locked. Billing-Portal-, Zahlungsmethoden- und
Billing-Recheck-Operationen bleiben erreichbar, damit der Workspace die Sperre beheben kann. Dabei
handelt es sich um Fehler des Workspace-Zustands, nicht um Fehler der Token-Authentifizierung.
Auf type und Status verzweigen, nicht auf detail
Ein Feature kann anstelle des generischen access-denied einen spezifischeren, namensraumbezogenen
Problemtyp zurückgeben — zum Beispiel https://problems.tourfold.com/comments/cannot-edit-others.
Der status bleibt 403 und data.reason enthält weiterhin die grobe maschinenlesbare Ursache;
Code, der auf Status plus data.reason prüft, funktioniert also weiter, während ein Client, der den
genauen Fall benötigt, auf die spezifischere type-URI matchen kann.
type-URIs und HTTP-Statuscodes sind stabiler Vertragsbestandteil. title und detail sind
menschenlesbar und können jederzeit umformuliert oder lokalisiert werden — verzweigen Sie niemals
darauf.
Das vollständige problem+json-Format finden Sie unter Fehler, den kompletten Typkatalog in der Fehlertypen-Referenz.
Rate-Limiting
Die Authentifizierung unterliegt dem Rate-Limiting. Einzelheiten finden Sie unter Rate-Limiting.
Nächste Schritte
- Lernen Sie die API-Grundlagen kennen
- Verstehen Sie das Erstellen & Aktualisieren von Ressourcen
- Erkunden Sie Paginierung, Sortierung & Filterung