Jeder Identity-Provider,
eine Form.

Clerk, WorkOS, Auth0, Kinde, Supabase oder jeder OIDC-konforme Provider, den dein Unternehmen ohnehin betreibt — jeder davon ist ein Adapter über einer gemeinsamen Strategie, und alle lösen sich zu demselben typisierten Subjekt auf, das deine Handler lesen. Social-Login, SAML-SSO und SCIM-Provisionierung stehen daneben. Den Provider zu wechseln ist eine Konfigurationsänderung, kein Umbau: kein Guard, keine Query und kein Screen erwähnt, wer das Token geprüft hat.

auth.ts
TypeScript
// app.config.ts — API keys are first-class
export default { apiKeys: true /* … */ }

// auth wiring — passwords, sessions, passkeys, MFA
import { authRoutesPlugin } from '@voltro/plugin-auth'
import { permission } from '@voltro/plugin-rbac'

plugins: [authRoutesPlugin()]   // mounts /auth/*

// guard a mutation — the Effect-native RBAC scope check
import { EffectStore } from '@voltro/runtime'
import { Effect } from 'effect'

export default (input, ctx) => Effect.gen(function* () {
  yield* permission(ctx, 'posts:write')   // fails typed Forbidden
  const store = yield* EffectStore
  return yield* store.insert('posts', input)
})

Jeder Provider, durch eine Naht.

Adapter für die Provider, die Leute wirklich nutzen

Clerk, WorkOS, Auth0, Kinde, Supabase und generisches OIDC kommen je als Plugin über einer gemeinsamen JWT-Bearer-Strategie: Tokens gegen die JWKS des Providers geprüft, Claims auf Subjekt und Mandant abgebildet. Der Adapter ist der einzige Teil deiner App, der weiß, welcher Provider es ist.

Mit Google anmelden, ohne ein Identity-Produkt einzukaufen

Social-Login ist ein eigenes Plugin und bewusst kein Token-Verifier: Google, GitHub und Apple melden den Nutzer an, und das Framework stellt seine EIGENE Session aus — dieselbe wie bei der Passwort-Anmeldung, mit derselben Geräteliste, demselben Widerruf und derselben Rotation. Kein Anbieter sitzt dazwischen, der das bepreist.

Enterprise-SSO und Verzeichnis-Provisionierung

SAML 2.0 und SCIM 2.0 sind Plugins — die Anforderungen, die mit dem ersten großen Kunden kommen, sind also ein Schalter und kein Umbau. Vom Verzeichnis provisionierte Nutzer und Gruppen landen als Mitgliedschaften, die der Rest des Frameworks bereits versteht.

Ein typisiertes Subjekt, egal wer das Token ausstellte

Handler lesen ein aufgelöstes Subjekt mit Scopes und Mandant, nie ein Provider-SDK. Das macht die Wahl umkehrbar — und lässt zwei Eingangstüren nebeneinander laufen, während du migrierst, weil beide sich zum selben auflösen.

Rollen kompilieren zu Scopes; API-Keys sind erstklassig

RBAC macht aus Rollen Scopes über das Scope-System des Protokolls — der Guard an einer Prozedur ist derselbe Ausdruck, ob der Aufrufer sich über Clerk, über SAML oder mit einem API-Key angemeldet hat. Keys werden über admin-geschützte Routen ausgegeben, gelistet und widerrufen und als Hashes gespeichert.

Eine vollständige Suite, wenn du keinen Anbieter dazunehmen willst

Passwörter mit modernem Hashing, signierte Sessions mit Schlüsselrotation, Passkeys, TOTP mit Recovery-Codes, Magic Links, Einladungen, Kontosperre und Multi-Tenant-Mitgliedschaften — First-Party, ohne dritten Dienst. Fang hier an und wechsle später zu einem Provider; die Guards ändern sich nicht.

Eine server-gerenderte Seite kommt nie abgemeldet an

Ein Token, das älter ist als die Lebensdauer des Providers — praktisch jeder erste Seitenaufruf des Tages — rendert eine SSR-Seite als anonymen Besucher, weil die Daten des Loaders aus einem Cookie gebunden werden, bevor irgendein Loader läuft. `middleware.ts` erneuert die Credential vor dieser Bindung und schreibt das rotierte Cookie zurück, sodass der Render, seine Queries und der Browser dieselbe frische Sitzung sehen. Wo er läuft, sagt er in deinen Routen — eine Marketing-Seite zahlt so nie für einen Identity-Umlauf.

Zwei Eingangstüren, ein Identitätsmodell.

Strategien komponieren — eine Migration ist kein Stichtag

Ein IdP und Passwort-Anmeldung können gleichzeitig laufen und lösen sich beide zu demselben typisierten Subjekt auf. Das ist der Unterschied zwischen einer Provider-Migration über ein Quartal und einer an einem Samstagabend: bestehende Nutzer behalten ihre Tür, neue nutzen die neue, und kein Handler kann die beiden unterscheiden. Eine gemeinsame JWT-Bearer-Strategie prüft JWKS und bildet Claims auf einen Mandanten ab; der React-Kleber (SubjectProvider, useSubject, RequireAuth) ist in beiden Fällen derselbe.

Wie viele Eingangstüren bereits verdrahtet sind.

6

IdP-Adapter über einer gemeinsamen JWT-Bearer-Strategie — Social-Login, SAML-SSO und SCIM sind eigene Plugins daneben

45

Plugins insgesamt: Identität steht also neben Billing, Storage und dem Rest statt in einer eigenen Kategorie

Derselbe App-Code, drei verschiedene Provider.

app.config.ts
ts
// app.config.ts
import { clerkAuth } from '@voltro/plugin-auth-clerk'

plugins: [clerkAuth({ issuer: process.env.CLERK_ISSUER })]

// ─── your application code ───────────────────────────────
guards: [requireScope('invoices:void')]
const subject = ctx.subject          // id, scopes, tenantId

Schau, was sich zwischen den Tabs NICHT ändert: der Guard, das Subjekt, der Mandant. Nur die Plugin-Zeile bewegt sich. Das ist die ganze Behauptung dieser Seite — und zugleich der Grund, warum mit der eingebauten Suite zu starten keine Entscheidung ist, die du am ersten Tag richtig treffen musst.

Authentifizierung im Detail.

Welchen Identity-Provider kann ich nutzen?

Namentlich sind es Clerk, WorkOS, Auth0, Kinde und Supabase, jeder als eigenes Plugin. Es sind keine getrennten Integrationen: alle sitzen über einer gemeinsamen JWT-Bearer-Strategie, die ein Token gegen die JWKS des Providers prüft und seine Claims auf Subjekt und Mandant abbildet.

Alles andere läuft über den generischen OIDC-Adapter — dieselbe Strategie, auf einen anderen Issuer gerichtet: Okta, Entra, Keycloak oder ein IdP, den ein Plattformteam intern betreibt. Social-Login ist mit Absicht ein eigenes Plugin, weil Google und GitHub weniger zu verifizieren als anzumelden sind: es stellt die eigene Session des Frameworks aus, statt dem Token eines Anbieters zu vertrauen. Enterprise-SAML-SSO und SCIM-Provisionierung sind ebenfalls eigene Plugins, weil die Kunden, die sie brauchen, sie namentlich verlangen.

Und wenn die Antwort keiner davon ist, ist die eigene Suite des Frameworks eine vollständige statt eines Platzhalters: Passwörter, Sessions, Passkeys, MFA, Einladungen und Mitgliedschaften. Sie existiert, damit noch keinen Provider gewählt zu haben ein Zustand ist, in dem man ausliefern kann — nicht, damit man darin bleiben muss.

Was ändert sich tatsächlich, wenn wir den Provider wechseln?

Die Plugin-Zeile in deiner App-Config und die Zugangsdaten. Sonst verweist nichts in der Anwendung auf den Provider: ein Handler liest ein aufgelöstes Subjekt, ein Guard nennt einen Scope, eine Query ist auf einen Mandanten begrenzt. Keines davon wird anders geschrieben, weil ein anderer Dienst das Token geprüft hat.

Das ist kein Zufall des Designs, sondern überhaupt der Grund, Identität über einen Adapter zu führen. Ein Provider-SDK, das direkt aus Anwendungscode gegriffen wird, macht aus einer Migration ein Suchen-und-Ersetzen über jede Datei, die je gefragt hat, wer der Aufrufer ist — so wird aus einem Wechsel von sechs Wochen einer von sechs Monaten.

Weil Strategien komponieren, muss der Wechsel nicht einmal atomar sein. Zwei Eingangstüren können gleichzeitig offen stehen, bestehende Nutzer auf der alten, neue Anmeldungen auf der neuen — und kein Handler kann sie auseinanderhalten.

Was hindert jemanden daran, Passwörter zu raten?

Eine Versuchssperre pro Konto. Nach einer konfigurierbaren Zahl fehlgeschlagener Anmeldeversuche innerhalb eines Fensters sperrt das Konto für eine Abkühlzeit und entsperrt sich danach selbst; eine korrekte Anmeldung setzt den Zähler zurück. Sie ist standardmäßig an — denn ein Sicherheits-Default, den man erst einschalten muss, ist einer, den die meisten Projekte nicht haben werden.

Das entscheidende Designdetail ist, was sie NICHT verrät. Der Zähler hängt an der eingegebenen E-Mail-Adresse, nicht an einer Nutzerzeile, und eine gesperrte Adresse liefert dieselbe Ablehnung, ob es das Konto gibt oder nicht — sonst würde die Sperre selbst zum Orakel für die Existenz eines Kontos, ein schlimmeres Leck als das, gegen das sie eingeführt wurde.

Der Anmeldepfad führt die Passwortprüfung außerdem auch für eine Adresse ohne Konto aus, damit die Antwortzeit nicht verrät, ob die Adresse registriert ist. Rate-Limiting obendrauf ist ein Plugin, nach IP oder Subject geschlüsselt, für die verkehrsförmige Hälfte des Problems.

Wie hängt Authentifizierung mit Autorisierung zusammen?

Authentifizierung erzeugt ein Subject: eine Identität, einen Mandanten und eine Menge Scopes. Autorisierung ist das, was jede Procedure darüber deklariert, welche Scopes sie aufrufen dürfen — und der Boot verweigert den Start, wenn eine über die Leitung erreichbare Procedure oder ein Event weder einen Guard noch einen ausgeschriebenen Grund für offenen Zugriff hat.

Rollen sind ein eigenes Thema und kommen vom RBAC-Plugin, das Rollen in dieselben Scopes übersetzt. Diese Indirektion ist Absicht: ein Scope ist eine Fähigkeit, die der Code prüft, eine Rolle ist ein organisatorisches Etikett, über das Menschen streiten — sie getrennt zu halten heißt, dass das Umbenennen einer Rolle nie still ändert, was durchgesetzt wird.

API-Keys sind erstklassig statt ein Plugin: aktivier sie, und du bekommst Bearer-Key-Authentifizierung mit admin-gegatetem Ausstellen, Auflisten und Widerrufen, als Hashes gespeichert. Keys tragen eigene Scopes, ein Key kann also strikt schwächer sein als der Mensch, der ihn erzeugt hat.

Was in der Auth-Suite steckt.

Im Framework enthaltene Authentifizierungs-Fähigkeiten
FähigkeitWas du bekommst
PasswörterModernes Hashing mit Rehash beim Verifizieren — Parameter lassen sich anheben, ohne alle zurückzusetzen.
SessionsServerseitige Datensätze, Mehr-Schlüssel-Rotation, gleitender Ablauf, Auflistung und Widerruf pro Session.
MFATOTP mit Durchsetzung beim Login und Recovery-Codes; Passkeys mit atomarer Klon-Erkennung.
Konto-WiederherstellungMagic Links, Passwort-Reset und E-Mail-Verifikation mit Off-/Soft-/Strict-Policy.
Brute-Force-AbwehrKontosperre mit Abkühlzeit, so geschlüsselt, dass sie die Existenz eines Kontos nicht verrät.
Externe IdentitätAdapter für die gängigen Provider plus generisches OIDC, alle über einer Strategie; SAML-SSO und SCIM als eigene Plugins.

Häufige Fragen

Kann ich von einem bestehenden Auth-Anbieter migrieren?

Ja. Weil Handler immer nur ein aufgelöstes Subject sehen, kann der erzeugende Mechanismus wechseln, ohne Autorisierungscode anzufassen. Während einer Migration einen IdP-Adapter und First-Party-Passwörter parallel zu fahren ist eine unterstützte Konfiguration, kein Workaround.

Wir sind heute auf Auth0 und wechseln vielleicht zu WorkOS. Was kostet das?

Einen Plugin-Tausch und die neuen Zugangsdaten. Beide sind Adapter über derselben Strategie — Subjekt, Scopes und Mandant, die dein Code liest, bleiben unverändert. Und weil Strategien komponieren, kannst du beide parallel laufen lassen, statt in einer Nacht umzuschalten.

Verrät die Kontosperre, ob eine E-Mail registriert ist?

Nein, und das hat das Design geprägt. Der Zähler hängt an der eingegebenen Adresse statt an einer Nutzerzeile, und eine gesperrte Adresse liefert dieselbe Ablehnung, ob das Konto existiert oder nicht. Der Anmeldepfad führt die Passwortprüfung auch für unbekannte Adressen aus, damit auch das Timing die Frage nicht beantwortet.

Wie unterstütze ich mehrere Organisationen pro Nutzer?

Mitgliedschaften und ein Switch-Tenant-Flow gehören zur Suite. Ein Nutzer kann mehreren Mandanten angehören, und ein Wechsel erzeugt ein neues, auf den gewählten Mandanten gescoptes Subject — dieselbe Subject-Form, die jeder andere Teil des Frameworks ohnehin liest.

Wo werden Session-Daten gespeichert?

In deiner Datenbank, in framework-eigenen Tabellen, die mit allem anderen migrieren. Es gibt keinen externen Session-Dienst, und nichts wird an fremde Infrastruktur weitergereicht; der Connection-String ist deiner.

Auth komponiert mit dem Rest der Runtime.

Öffne das Framework. Schau es dir selbst an.

Jede Primitive auf dieser Seite ist heute im Framework. Klone den Starter, lass `voltro dev` laufen, in zwei Minuten ist es auf dem Bildschirm.