Sessions Django

keenauth.bff.django ajoute la connexion à une application Django 4.2 ou version ultérieure. Votre serveur est le client OAuth et conserve tous les jetons côté serveur. Le navigateur reçoit uniquement un cookie de session chiffré, HttpOnly, et ne voit jamais de jeton d'accès, de jeton de rafraîchissement ou de jeton d'identifiant. C'est le modèle backend-for-frontend (BFF). Il fait partie de keenauth-python, qui n'est pas encore publié sur PyPI.

Pour protéger à la place une API qui reçoit des jetons porteurs, voir Protéger votre API.

Installation

pip install "keenauth-python[django]"

Avant de commencer

  1. Dans le tableau de bord, créez une application web dans votre environnement.
  2. Choisissez l'authentification du client : private_key_jwt (recommandé, PrivateKeyJwt) ou un secret client (ClientSecretBasic).
  3. Ajoutez l'URI de redirection https://app.acme.example/auth/callback et l'URI de redirection après déconnexion https://app.acme.example/.
  4. Générez un secret de cookie d'au moins 32 caractères, par exemple avec openssl rand -base64 32. Conservez-le dans votre gestionnaire de secrets, et non dans le dépôt.

Créer l'instance

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 est l'adresse publique de votre application. Le SDK construit à partir de celle-ci l'URI de redirection et toutes les redirections, jamais à partir de l'en-tête Host ou des en-têtes X-Forwarded-* de la requête. Le protocole http n'est accepté que pour localhost. Les options sont vérifiées lors de la création de KeenAuthBff, de sorte qu'une erreur de configuration arrête l'application au démarrage.

Utiliser private_key_jwt

Avec private_key_jwt, l'application ne possède aucun secret partagé. Transmettez votre clé privée à PrivateKeyJwt : le SDK signe une nouvelle assertion client (RFC 7523) pour chaque requête vers le point de terminaison de jeton :

from keenauth.bff import PrivateKeyJwt

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

Pour signer avec votre propre service de clés (KMS ou HSM), transmettez une fonction à la place : PrivateKeyJwt(assertion=sign). sign() doit renvoyer une nouvelle assertion signée à chaque appel.

Ajouter le middleware

# settings.py
KEENAUTH_BFF = "myproject.auth.bff"  # dotted path to the KeenAuthBff instance
MIDDLEWARE = [
    # ...
    "keenauth.bff.django.KeenAuthMiddleware",
]

Le middleware sert les routes ci-dessous et lit la session à chaque autre requête pour la placer dans request.keenauth.

# views.py
from django.http import HttpResponse
from keenauth.bff.django import require_auth

@require_auth()
def orders(request):
    return HttpResponse(f"Signed in as {request.keenauth.session.user.email}")

Routes

Route Ce qu'elle fait
GET /auth/login Démarre la connexion. Paramètres de requête facultatifs : returnTo (où l'utilisateur atterrit) et organization (se connecter à une organisation).
GET /auth/callback Termine la connexion et définit le cookie de session.
GET ou POST /auth/logout Déconnecte l'utilisateur auprès de KeenAuth et renvoie vers votre application.

La connexion utilise le flux de code d'autorisation avec PKCE (S256). La requête est envoyée au serveur (PAR) lorsque votre environnement le propose. Lors du callback, le SDK vérifie state, iss et le jeton d'identifiant, y compris son nonce. Chaque connexion réussie crée une nouvelle session ; l'ancienne est supprimée.

returnTo doit être un chemin de votre propre application, comme /orders?tab=open. Toute autre valeur, comme //evil.example ou une URL complète, est ignorée et l'utilisateur atterrit sur /.

Vues protégées

Décorez une vue avec @require_auth().

Sans session :

Pour POST, PUT, PATCH, DELETE et les autres méthodes non sûres, le navigateur doit indiquer que la requête provient de votre application : Sec-Fetch-Site: same-origin, ou un en-tête Origin égal à l'origine de votre application. Les autres requêtes reçoivent 403. Cela protège contre la falsification de requête intersite, en complément de SameSite=Lax.

Utiliser la session et appeler votre API

Le contexte de session contient :

Lorsque le jeton d'accès expire dans moins de 60 secondes, le SDK le rafraîchit avant l'exécution de votre vue. Chaque rafraîchissement remplace le jeton de rafraîchissement et définit le nouveau cookie de session dans la réponse. Si le serveur refuse le jeton de rafraîchissement, la session prend fin. Une erreur serveur temporaire laisse la session inchangée. Les requêtes simultanées d'un même processus partagent un seul rafraîchissement.

Changer d'organisation

bff.switch_organization_path("org_01j9…", "/dashboard") renvoie un lien vers /auth/login qui reconnecte l'utilisateur dans cette organisation. KeenAuth vérifie l'appartenance. Après la connexion, session.user.org_id contient l'organisation.

Se déconnecter

Envoyez une requête POST vers /auth/logout depuis un formulaire. Le SDK supprime le cookie de session (et la session stockée, si vous utilisez un stockage), révoque le jeton de rafraîchissement et envoie le navigateur vers le point de terminaison de déconnexion de votre environnement, qui le renvoie vers votre application. Une requête que le navigateur signale comme provenant d'un autre site est refusée. Les navigateurs qui n'envoient pas Sec-Fetch-Site ne peuvent se déconnecter que par un POST depuis vos propres pages.

Stockage des sessions

Par défaut, la session entière se trouve dans le cookie, chiffrée et authentifiée avec AES-256-GCM à l'aide d'une clé dérivée de secret. Un cookie modifié ou copié est rejeté. Le cookie se nomme __Host-keenauth_session.0 et est défini avec Secure, HttpOnly, SameSite=Lax et Path=/. Sur http://localhost, le préfixe __Host- et Secure sont omis. Les sessions volumineuses sont réparties sur trois cookies au maximum.

Pour conserver les jetons côté serveur, transmettez store= avec un objet qui possède get(id), set(id, session, ttl_seconds) et delete(id), par exemple adossé à Redis. Le cookie ne contient alors qu'un identifiant de session aléatoire chiffré. Chaque connexion crée un nouvel identifiant et supprime l'enregistrement précédent. Le stockage contient des jetons : restreignez l'accès à celui-ci. MemorySessionStore est réservé au développement et aux tests.

Une session dure au maximum 7 jours (session_max_age) ; le rafraîchissement ne la prolonge pas. Pour changer le secret, transmettez une liste : secret=[new_secret, old_secret]. Supprimez l'ancien après session_max_age.

Limites actuelles