Flask-Sitzungen
keenauth.bff.flask ergänzt eine Flask-Anwendung ab Version 2.3 um Anmeldung. Ihr Server ist der OAuth-Client und bewahrt alle Tokens serverseitig auf. Der Browser erhält nur ein verschlüsseltes Session-Cookie mit dem Attribut HttpOnly und sieht niemals 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-Tokens entgegennimmt, siehe Ihre API absichern.
Installation
pip install "keenauth-python[flask]"
Bevor Sie beginnen
- Erstellen Sie im Dashboard eine Webanwendung in Ihrer Umgebung.
- Wählen Sie die Client-Authentifizierung:
private_key_jwt(empfohlen,PrivateKeyJwt) oder ein Client-Secret (ClientSecretBasic). - Fügen Sie die Redirect-URI
https://app.acme.example/auth/callbackund die Redirect-URI nach der Abmeldunghttps://app.acme.example/hinzu. - 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 erzeugt daraus die Redirect-URI und alle Weiterleitungen, niemals aus dem Header Host oder den Headern X-Forwarded-* der Anfrage. Klartext-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
- Unterstützte Schlüssel: EC P-256 (
ES256), EC P-384 (ES384), Ed25519 (EdDSA) sowie RSA mit mindestens 2048 Bit (standardmäßigPS256; übergeben Siealgorithm="RS256"fürRS256). - Jede Assertion hat
issundsubmit Ihrer Client-ID,audmit dem Issuer, einen zufälligenjtiund eine Gültigkeitsdauer von 60 Sekunden. Ihrekidist der RFC-7638-Thumbprint des öffentlichen Schlüssels. - Hinterlegen Sie den öffentlichen Schlüssel an der Anwendung.
keenauth.m2m.load_private_key(pem).public_jwk()gibt ihn als JWK zurück.
Wenn Sie mit Ihrem eigenen Schlüsseldienst (einem KMS oder HSM) signieren möchten, übergeben Sie stattdessen eine Funktion: PrivateKeyJwt(assertion=sign). sign() muss bei jedem Aufruf eine neue signierte Assertion zurückgeben.
Hooks registrieren
from flask import Flask, g
from keenauth.bff.flask import init_app, require_auth
app = Flask(__name__)
init_app(app, bff)
@app.get("/orders")
@require_auth(bff)
def orders():
return f"Signed in as {g.keenauth.session.user.email}"
init_app stellt die unten aufgeführten Routen bereit und liest bei jeder anderen Anfrage die Sitzung in g.keenauth ein. Dieses SDK verwendet das eigene session-Cookie von Flask nicht.
Routen
| Route | Funktion |
|---|---|
GET /auth/login |
Startet die Anmeldung. Optionale Query-Parameter: returnTo (wohin der Benutzer gelangt) und organization (Anmeldung bei einer Organisation). |
GET /auth/callback |
Schließt die Anmeldung ab und setzt das Session-Cookie. |
GET oder POST /auth/logout |
Meldet bei KeenAuth ab und leitet zurück zu Ihrer Anwendung. |
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 erstellt eine neue Sitzung; die alte wird verworfen.
returnTo muss ein Pfad Ihrer eigenen Anwendung sein, etwa /orders?tab=open. Jeder andere Wert, etwa //evil.example oder eine vollständige URL, wird ignoriert und der Benutzer gelangt zu /.
Geschützte Ansichten
Versehen Sie eine Ansicht mit @require_auth(bff) unterhalb des Route-Decorators.
Ohne Sitzung gilt:
- Eine Seitenanfrage (
GEToderHEAD) wird mitreturnToauf/auth/loginumgeleitet; - eine Anfrage an
/api/…oder mit einer anderen Methode erhält401mit einem Problem-Dokument. Übergeben Sieapi=True, um auf jedem Pfad mit401zu antworten.
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 Ursprung Ihrer Anwendung entspricht. Andere Anfragen erhalten 403. Dies schützt zusätzlich zu SameSite=Lax vor Cross-Site-Request-Forgery.
Sitzung verwenden und Ihre API aufrufen
Der Sitzungskontext enthält:
session:Noneoder der angemeldete Benutzer.session.userenthält typisierte Claims des ID-Tokens wiesubject,email,name,org_idundorg_roles; alle Claims befinden sich insession.user.claims. Das Sitzungsobjekt enthält niemals ein Token.access_token: ein gültiges Access-Token für serverseitige Aufrufe oderNone. Senden Sie es nicht an den Browser.
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 Session-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 Session-Cookie (und die gespeicherte Sitzung, falls Sie einen Store verwenden), widerruft das Refresh-Token und schickt den Browser zum Logout-Endpunkt Ihrer Umgebung, der ihn zu Ihrer Anwendung zurückleitet. Eine Anfrage, die der Browser als von einer anderen Seite stammend kennzeichnet, wird abgelehnt. Browser, die kein Sec-Fetch-Site senden, können sich nur mit einem POST von Ihren eigenen Seiten abmelden.
Sitzungsspeicher
Standardmäßig liegt die gesamte Sitzung im Cookie, verschlüsselt und mit AES-256-GCM authentifiziert, unter einem Schlüssel, der aus secret abgeleitet wird. 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 Tokens auf dem Server zu halten, übergeben Sie store= mit einem Objekt, das get(id), set(id, session, ttl_seconds) und delete(id) bereitstellt, 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 Tokens, daher sollten Sie den Zugriff darauf einschränken. MemorySessionStore ist nur für Entwicklung und Tests gedacht.
Eine Sitzung dauert höchstens 7 Tage (session_max_age); eine Erneuerung verlängert sie nicht. Um das Secret zu ändern, übergeben Sie eine Liste: secret=[new_secret, old_secret]. Entfernen Sie das alte Secret nach session_max_age.
Aktuelle Einschränkungen
- An DPoP gebundene Tokens und Back-Channel-Logout werden noch nicht unterstützt.
- Erneuerungen werden nur innerhalb eines Serverprozesses geteilt. Bei mehreren Prozessen lässt die Anwendungseinstellung
refresh_reuse_interval_s(Standard: 5 Sekunden) eine parallele Erneuerung erfolgreich sein; setzen Sie sie auf0, beendet die zweite Erneuerung die Sitzung.