Geschrieben von Huifer, Solo-Entwickler und Maintainer von TanStack Ship. Ich habe seit 2023 die Authentifizierung in neun SaaS-Produktionsanwendungen implementiert — Better Auth auf Cloudflare Workers mit Sessions in KV, OAuth-Integrationen mit GitHub und Google, MFA per TOTP und WebAuthn sowie Autorisierungsmodelle von einfachem RBAC bis ReBAC für eine B2B-Plattform mit Workspace-Vererbung. Dieser Leitfaden fasst die Muster zusammen, auf die ich immer wieder zurückkomme: wie man authentifiziert, wie man autorisiert, wie die Edge-Runtime das Kalkül verändert, die Fehlerquellen, die mich nachts um 2 Uhr wecken, und die Produktions-Checkliste, die ich vor dem Ausrollen einer neuen Authentifizierungsfläche abarbeite. TanStack Ship ist mein kostenpflichtiges Produkt; ich lege diese Befangenheit vorab offen.
Verifizierte Quellen: OWASP Password Storage Cheat Sheet · RFC 6749 — OAuth 2.0 Authorization Framework · OpenID Connect Core 1.0 · Cloudflare Workers KV · Cloudflare Durable Objects · WebAuthn Guide — MDN · Better Auth documentation · TanStack Ship auth reference repo
Zuletzt aktualisiert: 15. Juli 2026 · Changelog
TL;DR: Die Authentifizierung beantwortet: „Wer stellt diese Anfrage?“; die Autorisierung beantwortet: „Was darf diese Person tun?“. In der Edge-Runtime rücken beide in einen Hot Path — jede authentifizierte Anfrage liest eine Session, sodass der Session Store und die Autorisierungsprüfung gemeinsam über Ihre p99-Latenz entscheiden. Die Muster, die ich 2026 einsetze: Argon2id für das Passwort-Hashing mit den von OWASP empfohlenen Parametern, serverseitige Sessions in KV mit versionierten Session-IDs, um Multi-Device-Race-Conditions zu vermeiden, OAuth 2.0 / OIDC für Drittanbieter-Logins, TOTP plus WebAuthn für MFA, RBAC für einfache Apps, ABAC für attributgesteuerte Richtlinien und ReBAC für B2B-Workspaces, bei denen die Mitgliedschaft Berechtigungen vererbt. Die gravierendsten Fehlerursachen sind Session-Races über mehrere Geräte hinweg, CSRF an Cross-Origin POST-Endpunkten und die XSS-Exfiltration von JWTs im localStorage — für jedes dieser Probleme bietet dieser Artikel eine konkrete Lösung.
Authentication: Die Identitätsebene
Die Authentifizierung ist der Teil Ihrer SaaS, in dem Fehler katastrophale, aber stille Auswirkungen haben — eine Session-Race-Condition fällt vielleicht monatelang nicht auf, aber wenn sie eintritt, müssen Sie Ihren größten Kunden rechtfertigen, warum sie sich zweimal einloggen mussten. Die folgenden Muster sind jene, die ich ausgerollt, aus Fehlern gelernt und erneut veröffentlicht habe.
Passwort-Hashing mit Argon2id
Die Wahl des Passwort-Hashings ist seit 2023 unverändert: Argon2id, niemals bcrypt, niemals scrypt, niemals etwas selbst Geschriebenes. Das OWASP Password Storage Cheat Sheet veröffentlicht die Parameter, die ich in allen neun Deployments verwende: 19 MiB Speicherkosten, Zeitkosten 2, Parallelität 1. Auf Cloudflare Workers werden die Argon2-Bindings über better-auth/crypto bereitgestellt und laufen als WebAssembly-Modul — die p95-Hash-Zeit liegt bei rund 90 ms mit den konservativen Parametern. Dies liegt deutlich unter dem Workers CPU budget für einen Login-Handler, ist jedoch zu ressourcenintensiv, um bei jeder Anfrage ausgeführt zu werden.
Serverseitige Sessions in der Edge-Runtime
Zustandslose Authentifizierung (JWT im localStorage) ist in Tutorials modern und stellt ein tickendes Sicherheitsrisiko dar. Das Muster, das ich verwende, sind serverseitige Sessions: eine zufällige Session-ID in einem HTTP-only-Cookie, die Session-Payload in einem Session Store und ein versioniertes Session-ID-Schema, das die Multi-Device-Race-Condition verhindert, die im Better Auth postmortem dokumentiert wurde. Auf Cloudflare Workers dient KV als Session Store für leseintensive Zugriffe: ein Session-ID-zu-Payload-Mapping, TTL-gesteuerter Ablauf sowie eine aktive Invalidierung beim Logout und über den Session-Versionszähler des Nutzers. Lesezugriffe benötigen weltweit ein p99 von ~5 ms, was schnell genug ist, sodass man Sessions nicht prozessintern zwischenspeichern muss.
Das Versionsschema zur Lösung der Multi-Device-Race-Condition ist kurz: Jede Session-ID enthält einen monotonen Zähler ({userId}.{version}) und jede Änderung der Session erhöht den Versionszähler des Nutzers. Eine Session, deren eingebettete Version älter ist als die aktuelle Version des Nutzers, gilt als überholt und wird bei der nächsten Anfrage ungültig. Der Postmortem-Artikel enthält die Anleitung zur Reproduktion; der Produktionswert liegt bei einer medianen globalen Propagierungsverzögerung von 10 Sekunden, verglichen mit dem Worst-Case von 60 Sekunden bei einer rein TTL-basierten Expiration.
OAuth 2.0 und OpenID Connect für Drittanbieter-Logins
Für „Sign in with Google / GitHub / Microsoft“ implementieren Sie OAuth 2.0 kombiniert mit OpenID Connect als Aufsatz. Die Komponenten, die Sie tatsächlich benötigen: den Autorisierungsendpunkt, den Token-Endpunkt, den JWKS-Endpunkt zur Überprüfung von ID-Token-Signaturen und den Userinfo-Endpunkt als Fallback, falls die ID-Token-Claims nicht ausreichen. Die Scopes, die ich anfrage, sind openid profile email für eine grundlegende Identitätsprüfung sowie anbieterspezifische Scopes, wenn zusätzliche Daten benötigt werden — read:user bei GitHub, https://www.googleapis.com/auth/userinfo.email bei Google.
Die zwei Fehlerszenarien in der Produktion sind State-Mismatch beim Callback (der OAuth-state-Parameter stimmt nicht mit dem anfangs gespeicherten Wert überein) sowie das Überspringen der ID-Token-Signaturprüfung, weil der Entwickler dem kid-Claim vertraute. Die Lösung ist mechanisch: Speichern Sie den OAuth-state serverseitig in KV mit einer TTL von 10 Minuten, vergleichen Sie ihn beim Callback und nutzen Sie eine JWKS-Bibliothek — keinen eigens entwickelten Verifizierer —, um die ID-Token-Signatur zu überprüfen. Der Abschnitt zum Authorization Code Grant im RFC 6749 dient hier als Referenz; programmieren Sie hierbei keine eigenen Lösungen.
MFA, TOTP und WebAuthn
E-Mail-Passwort gepaart mit einem Social-Login-Button ist die Grundvoraussetzung für 2026. Für alles, was Zahlungsdaten, Kundendaten oder Admin-Aktionen betrifft, müssen Sie zusätzlich MFA bereitstellen. Die beiden Mechanismen, die sich in der Praxis bewährt haben, sind TOTP (RFC 6238, wobei Google Authenticator und 1Password die Standard-Clients bilden) und WebAuthn-Plattform-Authentifikatoren (MDN WebAuthn guide). Ich bevorzuge dabei folgende Reihenfolge: TOTP als erster MFA-Faktor, da er auf jedem Gerät funktioniert, und WebAuthn als Option für Nutzer moderner Browser, da es gegen Phishing immun ist und eine schnellere Handhabung ermöglicht.
Authorization: Wer darf was tun?
Die Authentifizierung stellt die Identität sicher. Die Autorisierung ist die Richtlinienebene, welche festlegt, welche Handlungen diese Identität durchführen darf. Die drei Modelle, die ich 2026 anwende, sind RBAC, ABAC und ReBAC — wobei die Wahl zwischen ihnen keine reine Funktionspräferenz darstellt, sondern maßgeblich von der Struktur der Daten abhängt.
RBAC für einfache Anwendungsfälle
Role-Based Access Control ordnet Nutzer zu Rollen und Rollen zu Berechtigungen zu. Ein Nutzer hat eine Rolle, eine Rolle umfasst eine festgelegte Auswahl an Berechtigungen und die Autorisierungsprüfung ist ein Berechtigungs-Lookup. Für eine Single-Tenant-SaaS mit internen Nutzern sowie Admin-/Editor-/Viewer-Stufen ist dies das passende Modell, welches ich als Standard wähle. Die Implementierung besteht aus einer role-Spalte im Benutzerdatensatz und einer Serverfunktion, die beim Systemstart die Berechtigungen der Rolle aus einer Konfigurationsdatei lädt.
// src/lib/auth/rbac.ts
import type { Role, Permission } from "../types";
const ROLE_PERMISSIONS: Record<Role, Permission[]> = {
admin: ["billing:read", "billing:write", "users:read", "users:write",
"workspace:delete", "content:publish"],
editor: ["content:publish", "users:read"],
viewer: ["users:read"],
};
export function can(role: Role, permission: Permission): boolean {
return ROLE_PERMISSIONS[role]?.includes(permission) ?? false;
}
Die Überprüfung am Edge jeder Serverfunktion lautet if (!can(ctx.user.role, "billing:write")) throw new ForbiddenError(). RBAC ist das richtige Modell, wenn die Berechtigungsmenge überschaubar ist (unter ~30 distinct permissions), wenn Nutzer jeweils nur eine Rolle besitzen und sich die Rollen nicht häufig ändern. Wenn Ihre Berechtigungsmenge in die Hunderte geht oder die Rollenvergabe ressourcenabhängig und dynamisch erfolgt, sind Sie den Möglichkeiten von RBAC entwachsen.
ABAC für attributgesteuerte Richtlinien
Attribute-Based Access Control bewertet eine Richtlinie auf Basis eines Attribut-Tupels — Subjekt, Ressource, Aktion, Umgebung. Das Standardbeispiel lautet: „Der Nutzer darf dieses Dokument bearbeiten, wenn er der Eigentümer des Dokuments und das Dokument im Entwurfsstadium ist.“ ABAC ist die richtige Wahl, wenn die rollenbasierte Abstraktion von RBAC zu grob ausfällt. Das von mir ausgerollte Implementierungsmuster nutzt eine Richtliniendatei mit strukturierten Regeln und einer zentralen authorize()-Funktion, die das Regelwerk gegen den Request-Kontext prüft.
// src/lib/auth/abac.ts
import type { Policy, Subject, Resource, Environment } from "../types";
const POLICIES: Policy[] = [
{
id: "doc.edit.owner-draft",
effect: "allow",
actions: ["doc:edit"],
condition: (s: Subject, r: Resource) =>
s.id === r.ownerId && r.status === "draft",
},
{
id: "doc.publish.editor",
effect: "allow",
actions: ["doc:publish"],
condition: (s: Subject) => s.role === "editor" || s.role === "admin",
},
];
export function authorize(
subject: Subject,
action: string,
resource: Resource,
env: Environment
): boolean {
return POLICIES.some(p =>
p.actions.includes(action) &&
p.condition(subject, resource, env) &&
p.effect === "allow"
);
}
ABAC ist das ideale Modell, wenn die Richtlinie auf Attributen aufbaut, die Sie nicht im Vorfeld detailliert aufzählen können. Ich habe noch keinen Performance-Engpass hierbei beobachtet, solange die Richtliniendatei nicht den Umfang von ~500 Regeln übersteigt — ab diesem Punkt sollten Sie eine Rule-Engine in Erwägung ziehen.
ReBAC für B2B-Workspaces
Relationship-Based Access Control wertet Berechtigungen in Abhängigkeit von Beziehungen innerhalb eines Graphen aus. Das Standardbeispiel ist Google Docs: Alice darf bearbeiten, da Alice Editor des Dokuments ist, sich das Dokument im Workspace W befindet und Alice als Mitglied im Workspace W die Editor-Rolle geerbt hat. Für B2B-SaaS mit Workspaces, Projekten sowie vererbten Berechtigungen ist ReBAC genau das Modell, welches nicht an der eigenen Komplexität zugrunde geht.
Das zugehörige Implementierungsmuster ist ein Beziehungsgraph in der Datenbank — bestehend aus (subject, relation, object)-Tupeln — und eine Traversierung, die prüft, ob ein Pfad mit der passenden Relation zwischen Nutzer und Ressource besteht. Auf Cloudflare Workers speichere ich die Beziehungs-Tupel in Durable Objects , wobei pro Workspace ein DO als Koordinationsknoten fungiert; der Lookup benötigt dabei nur einstellige Millisekunden. Dieses Muster wird im multi-tenant architecture guide beschrieben — ReBAC und Multi-Tenant-Scoping stellen letztlich die gleiche Herausforderung auf unterschiedlichen Ebenen dar.
Details zur Edge-Runtime: Warum sich das Authentifizierungs-Szenario auf Workers verändert
Die Edge-Runtime ist nicht nur ein weiteres Deployment-Ziel — sie verändert das Kostenmodell, das Konsistenzmodell sowie die Fehlertoleranz Ihrer Authentifizierungsschicht.
KV als Session Cache, D1 als Single Source of Truth
Der Session Store besteht aus zwei Hälften. Der Hot Path — bei dem jede authentifizierte Anfrage liest — ist KV: Ein Mapping von Session-ID zu Payload mit TTL-gesteuerter Dauer. Der Cold Path — Login, Logout, Passwort zurücksetzen, MFA-Enrollment — ist D1: Persistente Nutzerdatensätze, Audit-Logs und der kanonische Session-Versionszähler. Lesezugriffe aus KV erreichen weltweit p99 ~5 ms; Lesezugriffe aus D1 benötigen p99 ~30 ms. Die grundlegende Disziplin lautet, niemals im Hot Path von Requests aus D1 zu lesen; die Session-Payload in KV beinhaltet alles, was die Anfrage benötigt.
Durable Objects für Rate Limiting und Account-Sperrungen
Login-Endpunkte sind das klassische Ziel für Brute-Force-Angriffe, und einfaches IP-basiertes Rate Limiting wird mithilfe von Residential Proxies rasch umgangen. Das von mir bevorzugte Muster ist ein Durable Object pro Account als Sperr-Koordinator: Fehlgeschlagene Login-Versuche erhöhen einen Zähler im DO, bei fünf Fehlschlägen innerhalb von 10 Minuten wird der Account für 15 Minuten gesperrt, und die Single-Writer-Konsistenz des DO macht diesen Zähler über mehrere Regionen hinweg atomar. Dasselbe Muster deckt das Throttling von Passwort-Resets sowie die OAuth-Statusvalidierung ab.
Regionsübergreifende Konsistenz und das 10-Sekunden-Fenster
KV nutzt Eventual Consistency, was im Worst-Case eine globale Propagierungszeit von bis zu 60 Sekunden bedeutet. Bei Session-Lesevorgängen wird dies toleriert, da die Session-Payload während der Laufzeit unveränderlich bleibt. Was hingegen keine Eventual Consistency duldet, ist der Session-Versionszähler für die Multi-Device-Invalidierung. Die mediane Latenz bei der globalen KV-Propagierung liegt bei meinen Messungen bei etwa 10 Sekunden — das ist das Worst-Case-Szenario für die Zeit, die zwischen dem Login eines Nutzers auf Gerät B und der Erkennung durch Gerät A vergeht, dass die Session überschrieben wurde.
Fehlerquellen, die Sie nachts um 2 Uhr wecken
Die vier nachfolgenden Fehlerquellen sind nicht theoretischer Natur. Es handelt sich jeweils um einen echten Vorfall, den ich per Debugging gelöst und hier aufgelistet habe, damit Sie beim eigenen Bearbeiten ein Muster erkennen können.
Multi-Device Session Races
Das Symptom: Ein Nutzer loggt sich auf Gerät B ein, aktualisiert danach Gerät A und bei Gerät A wird bis zu 60 Sekunden lang weiterhin der alte Session-Status angezeigt. Die Ursache: TTL-basierter Session-Ablauf ohne aktive Invalidierung bei der Erstellung einer neueren Session. Die Lösung: Das oben beschriebene versionierte Session-ID-Schema in Kombination mit aktiver Invalidierung bei jeder Session-Änderung. Die detaillierte Anleitung zur Reproduktion und die Lösung wurden im Better Auth postmortem dokumentiert.
CSRF an Cross-Origin POST-Endpunkten
Das Symptom: Der Browser eines eingeloggten Nutzers überträgt eine zustandsverändernde Anfrage von einer abweichenden Origin (Domain) an Ihre API und Ihr Server bearbeitet diese, da das Cookie gesendet wird. Die Lösung: SameSite=Lax im Session-Cookie als Baseline, SameSite=Strict für die restriktivste Position sowie eine explizite CSRF-Token-Prüfung an jedem zustandsverändernden Endpunkt, der formularbasierte Cross-Origin-Anfragen akzeptiert. Die Faustregel lautet, dass alles, was über fetch() mit credentials: "include" aufgerufen werden kann, ein CSRF-Token erfordert.
XSS-Exfiltration von JWT-im-localStorage
Das Symptom: Ein einziger XSS-Angriff in einer Abhängigkeit extrahiert das JWT aus dem localStorage und sendet es an den Server des Angreifers. Die Lösung: Nutzen Sie HTTP-only-Cookies für Session-Token und niemals localStorage, sessionStorage oder jeglichen proprietären Client-Speicher, der durch JavaScript erreichbar ist. Wenn Sie zwingend Bearer-Tokens für eine API verwenden müssen, versehen Sie diese mit einer Expiration von 5 Minuten und erfordern Sie eine Aktualisierung über einen serverseitigen Endpunkt.
OAuth State-Mismatch beim Callback
Das Symptom: Ein Nutzer klickt auf „Sign in with Google“, wird zum Zustimmungs-Bildschirm weitergeleitet, stimmt zu, kehrt zum Callback zurück und erhält die Fehlermeldung „Session abgelaufen, bitte versuchen Sie es erneut“. Die Lösung: Speichern Sie den OAuth-State mit einer TTL von 10 Minuten in KV und validieren Sie ihn serverseitig beim Callback. Das State-Mismatch lässt sich beheben; die weitaus gefährlichere Variante — die fehlende Überprüfung der ID-Token-Signatur — lässt sich jedoch überhaupt nicht ausgleichen.
Produktions-Checkliste
Die aus fünf Schritten bestehende Checkliste, die ich vor dem Ausrollen einer neuen Authentifizierungsfläche auf einem TanStack Ship-Deployment abarbeite:
- Passwort-Hashing: Argon2id mit den von OWASP empfohlenen Parametern (Speicher 19 MiB, Zeit 2, Parallelität 1). Bcrypt wird abgelehnt. Jede Form von SHA ist tabu.
- Session Storage: Serverseitige Sessions in KV mit einem versionierten Session-ID-Schema, HTTP-only / Secure / SameSite=Lax Cookies, 24 Stunden TTL inklusive aktivem Refresh bei jeder validierten Anfrage.
- Authorization at the edge: Jede Serverfunktion prüft die Autorisierung, bevor etwas gelesen oder geschrieben wird. Diese Überprüfung steht in der allerersten Zeile und ist kein bloßer Middleware-Nachtrag.
- OAuth State- und Signatur-Verifizierung: Der OAuth-
statewird mit einer TTL von 10 Minuten serverseitig gespeichert; während ID-Token-Signaturen mit einer JWKS-Bibliothek verifiziert werden. - Rate Limiting und Account-Sperrungen: Durable Object als Tracking-Instrument für fehlgeschlagene Logins pro Account, Sperrung nach 5 erfolglosen Versuchen innerhalb von 10 Minuten sowie Throttling des Passwort-Resets und OAuth-State-Validierung.
Sobald einer dieser fünf Punkte fehlt, ist das Deployment noch nicht bereit. Die Muster in diesem Artikel entsprechen exakt den Standards, die von TanStack Ship regulär bereitgestellt werden — auf der features page finden Sie eine Auflistung der Komponenten, die in neuen Projekten integriert sind.
Fazit
Authentifizierung und Autorisierung erfordern nicht bloß eine einzige Entscheidung — sie sind eine lange Reihe von Entscheidungen, bei der jede ihre eigenen Trade-offs und Ausfallrisiken birgt. Die Standardmuster, auf die ich 2026 setze, umfassen Argon2id-Passwort-Hashing, serverseitige Sessions in KV mit versionsgesteuerten Session-IDs, OAuth 2.0 / OIDC für Drittanbieter-Logins, TOTP in Verbindung mit WebAuthn für MFA, RBAC für einfach gestrickte Fälle, ABAC für attributgesteuerte Richtlinien sowie ReBAC für B2B-Workspaces.
Für den breiteren Kontext des Multi-Tenant-Scopings lesen Sie bitte den multi-tenant architecture guide. Bezüglich der Edge-Runtime, die das alles hostet, widmet sich der Cloudflare Workers 2026 guide deren Ausführungsmodell. Wenn Sie eine Authentifizierungsbibliothek auswählen müssen, finden Sie beim Better Auth comparison einen detaillierten Side-by-Side-Vergleich. TanStack Ship rüstet jedes der in diesem Artikel erwähnten Muster standardmäßig mit aus; die features page sowie die pricing page geben Aufschluss darüber, was genau integriert ist.