FastAPI-Sitzungen

keenauth.bff.fastapi fügt einer FastAPI-Anwendung die Anmeldung hinzu. Ihr Server ist der OAuth-Client und bewahrt alle Token auf dem Server auf. Der Browser erhält nur ein verschlüsseltes Sitzungs-Cookie mit dem Attribut HttpOnly und sieht zu keinem Zeitpunkt ein Access-, Refresh- oder ID-Token. Dies ist das Backend-for-Frontend-(BFF)-Muster. Es ist Teil von keenauth-python, das noch nicht auf PyPI veröffentlicht ist.

Um stattdessen eine API abzusichern, die Bearer-Token entgegennimmt, lesen Sie Ihre API schützen.

Installation

pip install "keenauth-python[fastapi]"

Bevor Sie beginnen

  1. Erstellen Sie im Dashboard eine Webanwendung in Ihrer Umgebung.
  2. Wählen Sie die Client-Authentifizierung: private_key_jwt (empfohlen, PrivateKeyJwt) oder ein Client-Secret (ClientSecretBasic).
  3. Fügen Sie die Redirect-URI https://app.acme.example/auth/callback und die Redirect-URI nach dem Abmelden https://app.acme.example/ hinzu.
  4. Erzeugen Sie ein Cookie-Secret mit mindestens 32 Zeichen, zum Beispiel mit openssl rand -base64 32. Bewahren Sie es in Ihrem Secret-Store auf, nicht im Repository.

Instanz erstellen

import os
from keenauth.bff import BffOptions, ClientSecretBasic, KeenAuthBff

bff = KeenAuthBff(BffOptions(
    issuer="https://acme.eu.keenauth.app",  # your environment's issuer, exactly
    client_id="app_01j9zzzzzzzzzzzzzzzzzzzz",
    client_auth=ClientSecretBasic(os.environ["KEENAUTH_CLIENT_SECRET"]),
    app_base_url="https://app.acme.example",
    secret=os.environ["KEENAUTH_COOKIE_SECRET"],
))

app_base_url ist die öffentliche Adresse Ihrer Anwendung. Das SDK erstellt daraus die Redirect-URI und alle Weiterleitungen, niemals aus dem Header Host oder X-Forwarded-* der Anfrage. Ungesichertes http wird nur für localhost akzeptiert. Die Optionen werden beim Erstellen von KeenAuthBff geprüft, sodass ein Konfigurationsfehler die Anwendung beim Start anhält.

private_key_jwt verwenden

Bei private_key_jwt besitzt die Anwendung kein gemeinsames Geheimnis. Übergeben Sie Ihren privaten Schlüssel an PrivateKeyJwt. Das SDK signiert dann für jede Anfrage an den Token-Endpunkt eine neue Client-Assertion (RFC 7523):

from keenauth.bff import PrivateKeyJwt

client_auth = PrivateKeyJwt(key=os.environ["KEENAUTH_PRIVATE_KEY"])  # unencrypted PEM

Um mit Ihrem eigenen Schlüsseldienst zu signieren (einem KMS oder HSM), übergeben Sie stattdessen eine Funktion: PrivateKeyJwt(assertion=sign). sign() muss bei jedem Aufruf eine neue signierte Assertion zurückgeben.

Middleware installieren

from fastapi import Depends, FastAPI
from keenauth.bff import SessionContext
from keenauth.bff.fastapi import RequireSession, install

app = FastAPI()
install(app, bff)

@app.get("/orders")
def orders(ctx: SessionContext = Depends(RequireSession(bff))):
    return {"email": ctx.session.user.email}

install stellt die unten aufgeführten Routen bereit und liest bei jeder anderen Anfrage die Sitzung in request.state.keenauth. Die Netzwerkaufrufe des SDK sind synchron; install führt sie im Thread-Pool von Starlette aus, damit sie die Event-Loop nicht blockieren.

Routen

Route Funktion
GET /auth/login Startet die Anmeldung. Optionale Query-Parameter: returnTo (wohin der Benutzer gelangt) und organization (Anmeldung in einer Organisation).
GET /auth/callback Schließt die Anmeldung ab und setzt das Sitzungs-Cookie.
GET oder POST /auth/logout Meldet bei KeenAuth ab und kehrt zu Ihrer Anwendung zurück.

Die Anmeldung verwendet den Authorization-Code-Flow mit PKCE (S256). Die Anfrage wird an den Server übermittelt (PAR), sofern Ihre Umgebung dies unterstützt. Beim Callback prüft das SDK state, iss und das ID-Token einschließlich seines nonce. Jede erfolgreiche Anmeldung erzeugt eine neue Sitzung; die alte wird verworfen.

returnTo muss ein Pfad Ihrer eigenen Anwendung sein, zum Beispiel /orders?tab=open. Jeder andere Wert, etwa //evil.example oder eine vollständige URL, wird ignoriert, und der Benutzer gelangt zu /.

Geschützte Ansichten

Fügen Sie einer Pfadoperation Depends(RequireSession(bff)) hinzu. Sie gibt den Sitzungskontext zurück.

Ohne Sitzung gilt:

Bei POST, PUT, PATCH, DELETE und anderen unsicheren Methoden muss der Browser angeben, dass die Anfrage von Ihrer Anwendung stammt: Sec-Fetch-Site: same-origin oder ein Header Origin, der dem Origin Ihrer Anwendung entspricht. Andere Anfragen erhalten 403. Dies schützt zusätzlich zu SameSite=Lax vor Cross-Site-Request-Forgery.

Sitzung nutzen und Ihre API aufrufen

Der Sitzungskontext enthält:

Läuft das Access-Token innerhalb von 60 Sekunden ab, erneuert das SDK es, bevor Ihre Ansicht ausgeführt wird. Jede Erneuerung rotiert das Refresh-Token und setzt das neue Sitzungs-Cookie in der Antwort. Verweigert der Server das Refresh-Token, endet die Sitzung. Ein vorübergehender Serverfehler lässt die Sitzung unverändert. Gleichzeitige Anfragen in einem Prozess teilen sich eine Erneuerung.

Organisation wechseln

bff.switch_organization_path("org_01j9…", "/dashboard") gibt einen Link zu /auth/login zurück, der den Benutzer erneut in dieser Organisation anmeldet. KeenAuth prüft die Mitgliedschaft. Nach der Anmeldung enthält session.user.org_id die Organisation.

Abmelden

Senden Sie aus einem Formular ein POST an /auth/logout. Das SDK löscht das Sitzungs-Cookie (und die gespeicherte Sitzung, falls Sie einen Store verwenden), widerruft das Refresh-Token und leitet den Browser zum Logout-Endpunkt Ihrer Umgebung weiter, der ihn zu Ihrer Anwendung zurückführt. Eine Anfrage, die der Browser als von einer anderen Website stammend kennzeichnet, wird abgelehnt. Browser, die kein Sec-Fetch-Site senden, können sich nur über ein POST von Ihren eigenen Seiten abmelden.

Sitzungsspeicher

Standardmäßig liegt die gesamte Sitzung im Cookie, verschlüsselt und mit AES-256-GCM authentifiziert, unter Verwendung eines aus secret abgeleiteten Schlüssels. Ein geändertes oder kopiertes Cookie wird abgelehnt. Das Cookie heißt __Host-keenauth_session.0 und wird mit Secure, HttpOnly, SameSite=Lax und Path=/ gesetzt. Auf http://localhost entfallen das Präfix __Host- und Secure. Große Sitzungen werden auf bis zu drei Cookies aufgeteilt.

Um Token auf dem Server zu halten, übergeben Sie store= mit einem Objekt, das get(id), set(id, session, ttl_seconds) und delete(id) besitzt, zum Beispiel auf Basis von Redis. Das Cookie enthält dann nur noch eine verschlüsselte zufällige Sitzungs-ID. Jede Anmeldung erzeugt eine neue ID und löscht den vorherigen Datensatz. Der Store enthält Token, beschränken Sie daher den Zugriff darauf. MemorySessionStore ist nur für Entwicklung und Tests gedacht.

Eine Sitzung dauert höchstens 7 Tage (session_max_age); Erneuerungen verlängern sie nicht. Um das Secret zu ändern, übergeben Sie eine Liste: secret=[new_secret, old_secret]. Entfernen Sie das alte nach session_max_age.

Aktuelle Einschränkungen