Authentifizierung
Übersicht
Abschnitt betitelt „Übersicht“Das Modul client.auth verwaltet die Benutzerauthentifizierung, Token-Verwaltung und Sitzungspersistenz. Sobald sich ein Benutzer anmeldet, enthalten alle nachfolgenden Datenanfragen automatisch das JWT.
Das SDK speichert Sitzungen standardmäßig im localStorage und aktualisiert Tokens automatisch, bevor sie ablaufen.
E-Mail / Passwort
Abschnitt betitelt „E-Mail / Passwort“Anmelden
Abschnitt betitelt „Anmelden“const { user, accessToken, refreshToken } = await client.auth.signInWithEmail( "user@example.com", "password");console.log(user.uid, user.email);Registrieren
Abschnitt betitelt „Registrieren“const { user } = await client.auth.signUp( "user@example.com", "password", "Jane Doe" // optional displayName);OAuth-Anbieter
Abschnitt betitelt „OAuth-Anbieter“Das SDK enthält dedizierte Methoden für beliebte OAuth-Anbieter sowie ein generisches signInWithOAuth() für jeden benutzerdefinierten Anbieter.
Unterstützt drei Aufrufarten:
// ID-token flow (One Tap / Sign In With Google button)await client.auth.signInWithGoogle({ idToken: googleIdToken });
// Access-token flow (popup)await client.auth.signInWithGoogle({ accessToken: googleAccessToken });
// Authorization code flow (most secure, server-side exchange)await client.auth.signInWithGoogle({ code: authCode, redirectUri: "https://..." });Weitere Anbieter
Abschnitt betitelt „Weitere Anbieter“Jeder Anbieter folgt dem Authorization-Code-Flow mit (code, redirectUri):
await client.auth.signInWithGitHub(code, redirectUri);await client.auth.signInWithMicrosoft(code, redirectUri);await client.auth.signInWithFacebook(code, redirectUri);await client.auth.signInWithLinkedin(code, redirectUri);await client.auth.signInWithDiscord(code, redirectUri);await client.auth.signInWithGitLab(code, redirectUri);await client.auth.signInWithBitbucket(code, redirectUri);await client.auth.signInWithSlack(code, redirectUri);await client.auth.signInWithSpotify(code, redirectUri);Apple und Twitter erfordern zusätzliche Parameter:
// Apple — optional user info from first sign-inawait client.auth.signInWithApple(code, redirectUri, { name: { firstName: "Jane", lastName: "Doe" }, email: "jane@example.com"});
// Twitter — requires PKCE code verifierawait client.auth.signInWithTwitter(code, redirectUri, codeVerifier);Generisches OAuth
Abschnitt betitelt „Generisches OAuth“Für jeden im Backend registrierten Anbieter:
await client.auth.signInWithOAuth("custom-provider", { code: authCode, redirectUri: "https://myapp.com/callback"});Magic Links
Abschnitt betitelt „Magic Links“Ein Ein-Klick-Anmeldelink per E-Mail. Der Link führt zu einer Seite Ihrer Anwendung und enthält ein Token; übergeben Sie das Token zurück, um es gegen eine Sitzung einzutauschen.
// 1. Ask for the link. `redirectTo` is where the link points.await client.auth.sendMagicLink("user@example.com");
// 2. On the landing page, trade the token for a session.const token = new URLSearchParams(location.search).get("token")!;const { user } = await client.auth.verifyMagicLink(token);sendMagicLink liefert die gleiche Antwort, unabhängig davon, ob für die Adresse ein Konto existiert oder nicht. Das ist Absicht: Ein Endpunkt, der „Benutzer existiert nicht“ meldet, wäre ein Orakel für die Konto-Enumeration (Account Enumeration). Nutzen Sie das Ergebnis daher nicht, um einer Person mitzuteilen, ob sie registriert ist — der Endpunkt weiß es nicht.
Beide erfordern einen im Backend konfigurierten E-Mail-Dienst, andernfalls antworten sie mit 503 EMAIL_NOT_CONFIGURED.
Einmalcodes
Abschnitt betitelt „Einmalcodes“Ein sechsstelliger Code per E-Mail für Fälle, in denen ein Link unpraktisch ist — eine native App, ein zweites Gerät, ein Browser, der Links beschädigt.
const { expiresInSeconds } = await client.auth.sendEmailOtp("user@example.com");
// The address goes back with the code, because the code is only valid for it.const { user } = await client.auth.verifyEmailOtp("user@example.com", "418293");Das Senden der Adresse zusammen mit dem Code stellt sicher, dass ein sechsstelliger Rateversuch ein Versuch gegen ein einzelnes Konto bleibt und nicht gegen alle Konten gleichzeitig gerichtet ist.
Anonyme Sitzungen
Abschnitt betitelt „Anonyme Sitzungen“Melden Sie einen Besucher ganz ohne Anmeldedaten an, sodass er die App nutzen kann, bevor er einen Grund zur Registrierung hat:
const { user } = await client.auth.signInAnonymously();user.isAnonymous; // trueDas Konto ist echt: Es besitzt eine ID, Rollen und eine Sitzung, sodass Row-Level Security seine Zeilen genauso eingrenzt wie bei einem registrierten Benutzer. Was es nicht hat, ist ein Weg zurück — niemand kann sich ein zweites Mal als dieses Konto anmelden, daher geht alles, was ihm gehört, mit der Sitzung verloren.
Mit linkAnonymous hört es auf, ein Wegwerfkonto zu sein. Der Benutzer behält seine ID, sodass alles, was er im anonymen Zustand erstellt hat, in seinem Besitz bleibt:
await client.auth.linkAnonymous("user@example.com", "correct-horse-battery");| Fehler | Bedeutung |
|---|---|
ANONYMOUS_AUTH_DISABLED (403) |
Das Backend hat die anonyme Authentifizierung nicht aktiviert |
NOT_ANONYMOUS (400) |
Die aktuelle Sitzung gehört zu einem regulären Konto |
EMAIL_EXISTS (409) |
Für die Adresse existiert bereits ein Konto — melden Sie sich stattdessen bei diesem an |
Einen Anbieter mit einem bestehenden Konto verknüpfen
Abschnitt betitelt „Einen Anbieter mit einem bestehenden Konto verknüpfen“signInWithGoogle und Co. melden einen Benutzer an. linkProvider verknüpft eine Anbieteridentität mit dem bereits angemeldeten Konto, sodass dieselbe Person über beide Wege zurückkehren kann:
await client.auth.linkProvider("google", { idToken });Die Sitzung beweist bereits den Kontobesitz. Im Gegensatz zur Anmeldung muss der Anbieter die E-Mail-Adresse daher nicht verifiziert haben, und die beiden Adressen müssen nicht übereinstimmen. Die Verknüpfung gelingt idempotent (alreadyLinked: true), wenn diese Identität dem Konto bereits zugeordnet ist, und verweigert die Verknüpfung mit IDENTITY_ALREADY_LINKED (409), wenn sie zu einem anderen Konto gehört.
Benutzer per E-Mail nachschlagen
Abschnitt betitelt „Benutzer per E-Mail nachschlagen“const profile = await client.auth.findUserByEmail("user@example.com");// { uid, displayName, photoURL } | nullDrei unkritische Felder und nichts weiter — genug, um „Sie laden Jane ein“ anzuzeigen, bevor eine Einladung gesendet wird.
Multi-Faktor-Authentifizierung
Abschnitt betitelt „Multi-Faktor-Authentifizierung“TOTP-Faktoren — eine Authentifikator-App — plus die Challenge, die eine Sitzung von aal1 auf aal2 hochstuft.
Faktor registrieren
Abschnitt betitelt „Faktor registrieren“const { factor, totp, recoveryCodes } = await client.auth.mfa.enroll({ friendlyName: "Phone"});
showQrCode(totp.uri); // otpauth://… — what the authenticator scansshowRecoveryCodes(recoveryCodes);Zeigen Sie die Wiederherstellungscodes einmalig und nie wieder an. Da nur deren Hashes gespeichert werden, können sie später nicht mehr angezeigt werden.
Der Faktor ist erst nutzbar, wenn der Benutzer nachweist, dass sein Authentifikator einen Code aus diesem Secret generiert hat:
await client.auth.mfa.verify(factor.id, "418293");Mit MFA anmelden
Abschnitt betitelt „Mit MFA anmelden“Eine Anmeldung an einem Konto mit eingerichteter MFA liefert eine Sitzung mit aal1 zurück. Starten Sie eine Challenge und beantworten Sie diese, um die eigentliche Sitzung zu erhalten:
const factors = await client.auth.mfa.listFactors();const { challengeId } = await client.auth.mfa.challenge(factors[0].id);
// A TOTP code, or one of the recovery codes.const { user } = await client.auth.mfa.verifyChallenge(challengeId, "418293");verifyChallenge erstellt die aal2-Sitzung, und dieser Client übernimmt sie, wodurch die Tokens aus der Anmeldung ersetzt werden. Eine Challenge läuft nach fünf Minuten ab, und eine Challenge, deren Rateversuche das Limit erreicht haben, bleibt für den Rest ihrer Lebensdauer ungültig — andernfalls würde eine offene Challenge unbegrenzte Versuche für sechs Ziffern ermöglichen.
Faktor entfernen
Abschnitt betitelt „Faktor entfernen“await client.auth.mfa.unenroll(factorId);Erfordert eine aal2-Sitzung — also eine, die bereits eine Challenge beantwortet hat —, damit ein gestohlenes aal1-Token MFA nicht deaktivieren kann. Durch das Entfernen des letzten verifizierten Faktors werden auch die Wiederherstellungscodes gelöscht.
Abmelden
Abschnitt betitelt „Abmelden“await client.auth.signOut();Dies widerruft das Refresh-Token auf dem Server, leert die lokale Sitzung und löst ein SIGNED_OUT-Event aus.
Sitzungsverwaltung
Abschnitt betitelt „Sitzungsverwaltung“Aktuelle Sitzung abrufen
Abschnitt betitelt „Aktuelle Sitzung abrufen“const session = client.auth.getSession();// { accessToken, refreshToken, expiresAt, user } | nullAktuellen Benutzer abrufen (serververifiziert)
Abschnitt betitelt „Aktuellen Benutzer abrufen (serververifiziert)“const user = await client.auth.getUser();// Fetches the user from the backend (GET /auth/me)Benutzerprofil aktualisieren
Abschnitt betitelt „Benutzerprofil aktualisieren“const updatedUser = await client.auth.updateUser({ displayName: "Jane Doe", photoURL: "https://example.com/avatar.jpg"});Token aktualisieren
Abschnitt betitelt „Token aktualisieren“Die Token-Aktualisierung erfolgt automatisch, Sie können sie jedoch auch manuell auslösen:
const session = await client.auth.refreshSession();Wo die Sitzung gespeichert wird: authFlowMode
Abschnitt betitelt „Wo die Sitzung gespeichert wird: authFlowMode“const client = createRebaseClient({ baseUrl: API_URL, auth: { authFlowMode: "cookie" }});| Modus | Wo sich das Refresh-Token befindet | Wann man ihn verwendet |
|---|---|---|
"json" (Standard) |
Im Response-Body zurückgegeben, im localStorage gehalten |
Eine native App, ein Skript, alles ohne den Cookie-Speicher eines Browsers |
"cookie" |
Ein HttpOnly-Cookie, das das Backend setzt | Eine Browser-App. Skripte auf Ihrer Seite können es nicht lesen, was es XSS-sicher macht |
Der Cookie-Modus erfordert auth.cookieAuth im Backend und wird von der generierten Frontend-Vorlage standardmäßig verwendet.
Auf die Wiederherstellung der Sitzung warten
Abschnitt betitelt „Auf die Wiederherstellung der Sitzung warten“Eine wiederhergestellte Sitzung ist beim ersten Rendern noch nicht verfügbar. getSession() ist synchron, weshalb es beim Laden der Seite null zurückgibt, während die Wiederherstellung noch läuft — und im Cookie-Modus läuft immer eine Wiederherstellung, da sich das Refresh-Token in einem Cookie befindet, das die Seite nicht lesen kann, sodass der Client beim Server ein neues Access-Token anfragen muss.
Das synchrone Auslesen führt bei jedem Neuladen zu einem kurzen Aufblitzen des abgemeldeten Zustands (Flash):
// Wrong: renders the signed-out view for one round trip, every reload.const session = client.auth.getSession();if (!session) return <SignIn />;isInitialized() löst auf, sobald der Client den Versuch abgeschlossen hat — unabhängig davon, ob eine Sitzung gefunden wurde oder nicht:
async function currentUser() { await client.auth.isInitialized(); return client.auth.getSession()?.user ?? null;}In React entspricht das einem einzigen Effect:
import { useEffect, useState } from "react";
function useCurrentUser() { const [user, setUser] = useState<User | null>(null); const [loading, setLoading] = useState(true);
useEffect(() => { let cancelled = false; client.auth.isInitialized().then(() => { if (cancelled) return; setUser(client.auth.getSession()?.user ?? null); setLoading(false); }); return () => { cancelled = true; }; }, []);
return { user, loading };}useRebaseAuthController in @rebasepro/app erledigt dies bereits, sodass eine auf der generierten Vorlage aufbauende App dies automatisch mitbringt.
Eine erfolgreiche Wiederherstellung erreicht onAuthStateChange auch als TOKEN_REFRESHED — es ist eine Aktualisierung —, aber ein Listener allein kann Ihnen nicht sagen, ob die Wiederherstellung abgeschlossen ist: Ein Start ohne Sitzung löst gar nichts aus, was von einem noch laufenden Vorgang nicht zu unterscheiden ist. Warten Sie für diese Prüfung isInitialized() ab und nutzen Sie den Listener für spätere Änderungen.
Auth-Status-Listener
Abschnitt betitelt „Auth-Status-Listener“Reagieren Sie auf Authentifizierungsänderungen in Ihrer gesamten Anwendung:
const unsubscribe = client.auth.onAuthStateChange((event, session) => { // event: "SIGNED_IN" | "SIGNED_OUT" | "TOKEN_REFRESHED" | "USER_UPDATED" console.log("Auth event:", event); console.log("Session:", session?.user?.email);});
// Stop listeningunsubscribe();| Event | Wann |
|---|---|
SIGNED_IN |
Eine Anmeldung oder Registrierung wurde abgeschlossen |
TOKEN_REFRESHED |
Das Access-Token wurde erneuert — einschließlich der stillen Erneuerung, die eine Sitzung beim Laden der Seite wiederherstellt |
USER_UPDATED |
updateUser() hat das Profil geändert |
SIGNED_OUT |
Eine Abmeldung oder eine Aktualisierung, die endgültig fehlgeschlagen ist |
Passwortverwaltung
Abschnitt betitelt „Passwortverwaltung“Passwort vergessen
Abschnitt betitelt „Passwort vergessen“const { success, message } = await client.auth.resetPasswordForEmail( "user@example.com");Passwort zurücksetzen (mit Token)
Abschnitt betitelt „Passwort zurücksetzen (mit Token)“const { success, message } = await client.auth.resetPassword( resetToken, "newSecurePassword");Passwort ändern (authentifiziert)
Abschnitt betitelt „Passwort ändern (authentifiziert)“const { success, message } = await client.auth.changePassword( "oldPassword", "newPassword");E-Mail-Verifizierung
Abschnitt betitelt „E-Mail-Verifizierung“// Send verification email to the current userawait client.auth.sendVerificationEmail();
// Verify with the token from the email linkawait client.auth.verifyEmail(token);Sitzungsverwaltung (Multi-Device)
Abschnitt betitelt „Sitzungsverwaltung (Multi-Device)“// List all active sessionsconst sessions = await client.auth.getSessions();
// Revoke a specific sessionawait client.auth.revokeSession(sessionId);
// Revoke ALL sessions (logs out everywhere)await client.auth.revokeAllSessions();Auth-Konfiguration
Abschnitt betitelt „Auth-Konfiguration“Fragen Sie die Authentifizierungskonfiguration des Backends ab:
const config = await client.auth.getAuthConfig();// {// hasBuiltInAuthRoutes: boolean,// emailPasswordLogin: boolean,// registrationEnabled: boolean, // open right now, bootstrap window included// passwordReset: boolean, // needs an email service// adminPasswordReset: boolean,// sessionManagement: boolean,// profileUpdate: boolean,// emailVerification: boolean,// magicLink: boolean,// anonymousLogin: boolean,// enabledProviders: string[],// needsSetup: boolean// }Benutzerdefinierter Sitzungsspeicher
Abschnitt betitelt „Benutzerdefinierter Sitzungsspeicher“Standardmäßig werden Sitzungen im localStorage gespeichert. Sie können dies über die Option auth anpassen:
import { createRebaseClient, createCookieStorage } from "@rebasepro/client";
// Use cookies instead of localStorageconst client = createRebaseClient({ baseUrl: import.meta.env.VITE_API_URL, auth: { storage: createCookieStorage({ path: "/", sameSite: "Lax", secure: true }), autoRefresh: true, // default: true persistSession: true // default: true }});Struktur des User-Objekts
Abschnitt betitelt „Struktur des User-Objekts“// Canonical type — import from @rebasepro/typesinterface User { uid: string; email: string | null; displayName: string | null; photoURL: string | null; providerId: string; isAnonymous: boolean; emailVerified?: boolean; roles?: string[]; // text[] from the users table metadata?: Record<string, unknown>;}Nächste Schritte
Abschnitt betitelt „Nächste Schritte“- Daten abfragen — CRUD-Operationen und Query-Builder
- Echtzeit-Abonnements — Live-Daten mit WebSockets
- Authentifizierungs-Backend — Serverseitige Auth-Konfiguration