JavaScript-Browser-SDK
@keenauth/js ist der Browser-Kern von KeenAuth. Es meldet Benutzer in Ihrer Single-Page-App an, speichert deren Tokens und ruft Ihre API auf. Es hat keine Laufzeitabhängigkeiten, und die React-Komponenten bauen darauf auf. Das Paket ist noch nicht auf npm veröffentlicht.
Anmeldung mit Weiterleitung
Das SDK ist ein Standard-Public-Client für OpenID Connect:
- Es verwendet PKCE (S256) und DPoP-gebundene Tokens.
- Es prüft
state,nonceundissim Callback. - Es validiert die Signatur und die Claims des ID-Tokens anhand der Schlüssel Ihrer Umgebung.
import { KeenAuth } from "@keenauth/js";
const auth = new KeenAuth({
issuer: "https://acme.eu.keenauth.app", // your environment's issuer, exactly
clientId: "app_01j9zzzzzzzzzzzzzzzzzzzz",
redirectUri: "https://app.acme.example/callback",
postLogoutRedirectUri: "https://app.acme.example/",
});
// On your sign-in button:
await auth.signInRedirect({ appState: "/orders" });
// On https://app.acme.example/callback:
const { appState } = await auth.handleRedirectCallback();
handleRedirectCallback akzeptiert eine Antwort nur einmal. Es schlägt mit einem KeenAuthError fehl, wenn:
stateoderissnicht übereinstimmt;- die ausstehende Anmeldung älter als 10 Minuten ist;
- das ID-Token nicht gültig ist.
Ein Fehler, den der Server zurückgibt, wird als KeenAuthOAuthError mit dem OAuth-Code error ausgelöst.
Ihre API aufrufen
Um ein Zugriffstoken für Ihre API zu erhalten, übergeben Sie deren Kennung als resource (RFC 8707). Sie wird bei der Autorisierungsanfrage, beim Code-Austausch und bei jeder Aktualisierung gesendet, und der aud des Tokens ist diese Kennung:
const auth = new KeenAuth({
issuer: "https://acme.eu.keenauth.app",
clientId: "app_01j9zzzzzzzzzzzzzzzzzzzz",
redirectUri: "https://app.acme.example/callback",
resource: "https://api.acme.example", // your API's identifier
scope: "openid profile email offline_access messages:read",
});
- Der Wert muss ein absoluter URI ohne Fragment (
#) sein. Er wird genau so gesendet, wie Sie ihn schreiben, verwenden Sie daher die Kennung der API so, wie sie registriert ist. Ein ungültiger Wert löst im Konstruktor einenKeenAuthErrormit dem Codeconfig_invalidaus. signInRedirect({ resource })überschreibt den Wert für eine einzelne Anmeldung.- Ohne
resourceverwendet der Server die Standard-API der Anwendung, sofern eine festgelegt ist; andernfalls werden keine API-Scopes gewährt. - Die Option akzeptiert auch ein Array, das wie RFC 8707 es erlaubt als wiederholte
resource-Parameter gesendet wird. Ein KeenAuth-Zugriffstoken hat genau eine Audience, daher lehnt der Server mehr als einen Wert mitinvalid_targetab (einKeenAuthOAuthError).
const res = await auth.authorizedFetch("https://api.acme.example/v1/orders");
// Or take the token yourself; it is refreshed when it is about to expire.
const token = await auth.getAccessToken();
authorizedFetch sendet Authorization: DPoP mit einem frischen, an das Token gebundenen Nachweis. Wenn der Server einen DPoP-Nonce verlangt, wiederholt es den Aufruf einmal mit diesem Nonce. Gleichzeitige Aufrufe teilen sich eine Aktualisierungsanfrage, und jede Aktualisierung rotiert das Refresh-Token. Informationen zur Prüfung dieser Tokens in Ihrer API finden Sie unter Tokens in Ihrer API prüfen.
Abmelden
await auth.signOut();
signOut verwirft die Tokens und widerruft das Refresh-Token. Anschließend leitet es den Browser zum Logout-Endpunkt Ihrer Umgebung weiter, der ihn zu postLogoutRedirectUri zurückbringt.
Wo Tokens gespeichert werden
- Zugriffs-, Refresh- und ID-Tokens werden nur im Arbeitsspeicher gehalten, niemals in localStorage, sessionStorage, IndexedDB, Cookies oder der URL. Nach einem Neuladen der Seite wird der Benutzer per Weiterleitung erneut angemeldet; das SDK stellt die Sitzung nicht wieder her.
- sessionStorage enthält nur die einmaligen Werte einer laufenden Anmeldung.
- Der DPoP-Schlüssel ist ein nicht exportierbarer WebCrypto-Schlüssel, standardmäßig im Arbeitsspeicher gehalten.
Eingebettete Anmeldung
Dasselbe Objekt stellt den eingebetteten Komponenten außerdem einen Frontend-API-Client zur Verfügung:
transactionsfür die Schritte der Anmeldung;mefür das Profil und dieuser_metadatades Kontos, E-Mail-Adressen (mit Bestätigungscode), Passkeys und Sitzungen;webauthnfür Passkey-Zeremonien.
Diese Aufrufe verwenden die Cookies Ihrer Umgebung und einen CSRF-Header. Der Server akzeptiert sie nur vom eigenen Anmelde-Origin der Umgebung.
Anmeldung ohne Weiterleitung
Starten Sie auf einer Seite außerhalb Ihres Anmelde-Origins die Transaktion mit Ihrer clientId. Das SDK bindet sie dann an seinen DPoP-Schlüssel statt an Cookies:
let tx = await auth.transactions.create({ client_id: "app_01j9...", organization: "acme" });
tx = await auth.transactions.step("identifier", { identifier: "ada@acme.example" });
tx = await auth.transactions.step("password", { password });
if (tx.state === "completed") await auth.complete(tx); // { kind: "signed_in" }
- Die Anwendung muss eine First-Party-Anwendung sein, und der Seiten-Origin muss einer ihrer erlaubten Web-Origins sein.
- Jeder Aufruf trägt einen DPoP-Nachweis und kein Cookie. Verlangt der Server einen DPoP-Nonce, wiederholt das SDK den Aufruf einmal mit diesem Nonce.
- Das SDK erstellt den PKCE-Verifier,
stateundnonceselbst und hält sie im Arbeitsspeicher.scopeundresourcestammen aus den Optionen; Sie könnenorganizationunduiLocalesancreateübergeben. Verwenden Sie hier genau einenresource-Wert. completeprüft die Antwort, tauscht den Autorisierungscode mit demselben DPoP-Schlüssel aus und hält die Tokens im Arbeitsspeicher. Die Seite navigiert nicht, und der Code wird nie an Ihren Code zurückgegeben.- Jeder
step-Aufruf, mit oder ohneclient_id, sendet die Zeitzone des Browsers im HeaderIdp-Zoneinfo. Schließt der Schritt die Anmeldung ab und hat der Benutzer noch keine Zeitzone gewählt, speichert KeenAuth sie im Feldzoneinfodes Profils, wie es die gehostete Anmeldeseite tut. Meldet der Browser keine brauchbare Zone, wird der Header weggelassen und der Schritt bleibt unverändert. - Benötigt die Anmeldung eine Seite, die der Benutzer sehen muss (Zustimmung, rechtliche Bedingungen, Auswahl einer Organisation), schlägt der letzte Schritt mit einem
KeenAuthProblemErrormit dem Codeauthorization_failedfehl. Verwenden Sie in diesem FallsignInRedirectfür diesen Benutzer.
Kontoaufrufe von Ihrem eigenen Origin
Sobald der Benutzer angemeldet ist und das SDK ein Refresh-Token hält (der Standard-Scope fordert offline_access an), benötigen die Aufrufe me und organizations kein Sitzungs-Cookie des Anmeldehosts mehr:
- Das SDK aktualisiert mit
resource=<issuer>/fapiund erhält ein kurzlebiges Zugriffstoken für die Frontend-API. Es ist DPoP-gebunden und wie die anderen Tokens nur im Arbeitsspeicher gehalten. - Jeder Kontoaufruf sendet
Authorization: DPoP <token>und einen DPoP-Nachweis für diesen Aufruf, ohne Cookie. Verlangt der Server einen DPoP-Nonce, wiederholt das SDK den Aufruf einmal mit diesem Nonce; ist das Token nicht mehr gültig, holt es ein neues und wiederholt den Aufruf einmal. - Das Refresh-Token rotiert bei jeder Aktualisierung, daher führt das SDK Aktualisierungen nacheinander aus.
- Dies funktioniert nur für eine First-Party-Anwendung, deren erlaubte Web-Origins den Origin Ihrer Seite enthalten.
- Ohne Tokens verwendet das SDK wie bisher das Sitzungs-Cookie.
Die Serverregeln finden Sie unter Zugriffstoken für die Frontend-API.
Organisation wechseln
organizations.switch(orgId) wechselt den angemeldeten Benutzer ohne Seitenaufruf in eine andere Organisation. Übergeben Sie die ID org_… einer Organisation aus organizations.listMine():
const { data } = await auth.organizations.listMine();
await auth.organizations.switch(data[1].id);
- Es benötigt dasselbe wie die oben genannten Kontoaufrufe: ein Refresh-Token,
redirectUriund eine First-Party-Anwendung, deren erlaubte Web-Origins den Origin Ihrer Seite enthalten. - Das SDK sendet eine Wechselanfrage mit dem Frontend-API-Token, tauscht den Code mit einem Nachweis desselben Schlüssels aus und behält die neuen Tokens, deren
org_iddie Organisation ist. Die bisher gehaltenen Tokens bleiben erhalten, bis all das erfolgreich abgeschlossen ist. - Benötigt die Organisation eine Seite, die der Benutzer sehen muss (MFA, SSO, Zustimmung), schlägt der Aufruf mit einem
KeenAuthProblemErrormit dem Codeauthorization_failedfehl. Verwenden Sie in diesem FallsignInRedirect({ organization })für diesen Benutzer.
Aktuelle Einschränkungen
- Profil-Schreibvorgänge (
me.update,me.updateMetadata) sendenIf-Matchmit demETagdes letzten Profil-Lese- oder Schreibvorgangs. Wurde das Profil zwischenzeitlich anderweitig geändert, schlägt der Schreibvorgang mit einemKeenAuthProblemErrormit dem Codeprecondition_failedfehl; rufen Sieme.get()erneut auf, bevor Sie es wiederholen. - Telefonnummern sind noch nicht verfügbar:
me.phonesistundefined.