Sesiones de Flask
keenauth.bff.flask añade inicio de sesión a una aplicación Flask 2.3 o posterior. Su servidor es el cliente OAuth y guarda todos los tokens en el servidor. El navegador recibe solo una cookie de sesión cifrada y HttpOnly, y nunca ve un token de acceso, de actualización ni de ID. Este es el patrón backend-for-frontend (BFF). Forma parte de keenauth-python, que todavía no está publicado en PyPI.
Para proteger una API que recibe tokens de portador, consulte Protección de su API.
Instalación
pip install "keenauth-python[flask]"
Antes de empezar
- En el panel de control, cree una aplicación web en su entorno.
- Elija la autenticación del cliente:
private_key_jwt(recomendada,PrivateKeyJwt) o un secreto de cliente (ClientSecretBasic). - Añada el URI de redirección
https://app.acme.example/auth/callbacky el URI de redirección tras cerrar sesiónhttps://app.acme.example/. - Genere un secreto de cookie de al menos 32 caracteres, por ejemplo con
openssl rand -base64 32. Guárdelo en su almacén de secretos, no en el repositorio.
Cree la instancia
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 es la dirección pública de su aplicación. El SDK construye el URI de redirección y todas las redirecciones a partir de ella, nunca a partir de las cabeceras Host o X-Forwarded-* de la solicitud. El http simple solo se acepta para localhost. Las opciones se validan al crear KeenAuthBff, de modo que un error de configuración detiene la aplicación durante el arranque.
Use private_key_jwt
Con private_key_jwt la aplicación no tiene ningún secreto compartido. Proporcione a PrivateKeyJwt su clave privada, y el SDK firma una nueva aserción de cliente (RFC 7523) para cada solicitud al endpoint de token:
from keenauth.bff import PrivateKeyJwt
client_auth = PrivateKeyJwt(key=os.environ["KEENAUTH_PRIVATE_KEY"]) # unencrypted PEM
- Claves admitidas: EC P-256 (
ES256), EC P-384 (ES384), Ed25519 (EdDSA) y RSA de al menos 2048 bits (PS256por defecto; pasealgorithm="RS256"paraRS256). - Cada aserción tiene
issysubcon el ID de su cliente,audcon el emisor, unjtialeatorio y una vigencia de 60 segundos. Sukides la huella RFC 7638 de la clave pública. - Registre la clave pública en la aplicación.
keenauth.m2m.load_private_key(pem).public_jwk()la devuelve como JWK.
Para firmar con su propio servicio de claves (un KMS o HSM), pase una función: PrivateKeyJwt(assertion=sign). sign() debe devolver una nueva aserción firmada en cada llamada.
Registre los hooks
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 sirve las rutas que aparecen a continuación y lee la sesión en cada una de las demás solicitudes para cargarla en g.keenauth. Este SDK no usa la cookie session propia de Flask.
Rutas
| Ruta | Qué hace |
|---|---|
GET /auth/login |
Inicia el inicio de sesión. Parámetros de consulta opcionales: returnTo (a dónde llega el usuario) y organization (iniciar sesión en una organización). |
GET /auth/callback |
Completa el inicio de sesión y establece la cookie de sesión. |
GET o POST /auth/logout |
Cierra la sesión en KeenAuth y regresa a su aplicación. |
El inicio de sesión usa el flujo de código de autorización con PKCE (S256). La solicitud se envía al servidor (PAR) cuando su entorno lo ofrece. En el callback, el SDK comprueba state, iss y el token de ID, incluido su nonce. Cada inicio de sesión correcto crea una sesión nueva, y la anterior se descarta.
returnTo debe ser una ruta de su propia aplicación, como /orders?tab=open. Cualquier otro valor, como //evil.example o una URL completa, se ignora y el usuario llega a /.
Vistas protegidas
Decore una vista con @require_auth(bff), debajo del decorador de ruta.
Sin sesión:
- una solicitud de página (
GEToHEAD) se redirige a/auth/loginconreturnTo; - una solicitud a
/api/…, o con otro método, recibe401con un documento de problema. Paseapi=Truepara responder401en cualquier ruta.
Para POST, PUT, PATCH, DELETE y otros métodos no seguros, el navegador debe indicar que la solicitud proviene de su aplicación: Sec-Fetch-Site: same-origin, o una cabecera Origin igual al origen de su aplicación. Las demás solicitudes reciben 403. Esto protege contra la falsificación de solicitudes entre sitios, además de SameSite=Lax.
Use la sesión y llame a su API
El contexto de sesión tiene:
session:None, o el usuario que ha iniciado sesión.session.usertiene reclamaciones tipadas del token de ID, comosubject,email,name,org_idyorg_roles; todas las reclamaciones están ensession.user.claims. El objeto de sesión nunca contiene un token.access_token: un token de acceso válido para llamadas del lado del servidor, oNone. No lo envíe al navegador.
Cuando el token de acceso expira en menos de 60 segundos, el SDK lo renueva antes de que se ejecute su vista. Cada renovación rota el token de actualización y establece la nueva cookie de sesión en la respuesta. Si el servidor rechaza el token de actualización, la sesión termina. Un error temporal del servidor deja la sesión sin cambios. Las solicitudes concurrentes en un mismo proceso comparten una sola renovación.
Cambie de organización
bff.switch_organization_path("org_01j9…", "/dashboard") devuelve un enlace a /auth/login que vuelve a iniciar sesión al usuario en esa organización. KeenAuth comprueba la pertenencia. Tras el inicio de sesión, session.user.org_id contiene la organización.
Cierre de sesión
Envíe un POST a /auth/logout desde un formulario. El SDK elimina la cookie de sesión (y la sesión almacenada, si usa un almacén), revoca el token de actualización y envía el navegador al endpoint de cierre de sesión de su entorno, que lo devuelve a su aplicación. Se rechaza una solicitud que el navegador marca como procedente de otro sitio. Los navegadores que no envían Sec-Fetch-Site solo pueden cerrar sesión con un POST desde sus propias páginas.
Almacenamiento de sesiones
Por defecto, toda la sesión está en la cookie, cifrada y autenticada con AES-256-GCM usando una clave derivada de secret. Una cookie modificada o copiada se rechaza. La cookie se llama __Host-keenauth_session.0 y se establece con Secure, HttpOnly, SameSite=Lax y Path=/. En http://localhost se omiten el prefijo __Host- y Secure. Las sesiones grandes se dividen en hasta tres cookies.
Para mantener los tokens en el servidor, pase store= con un objeto que tenga get(id), set(id, session, ttl_seconds) y delete(id), por ejemplo respaldado por Redis. La cookie contendrá entonces solo un ID de sesión aleatorio y cifrado. Cada inicio de sesión crea un ID nuevo y elimina el registro anterior. El almacén contiene tokens, así que restrinja su acceso. MemorySessionStore es solo para desarrollo y pruebas.
Una sesión dura como máximo 7 días (session_max_age); renovar no la extiende. Para cambiar el secreto, pase una lista: secret=[new_secret, old_secret]. Elimine el antiguo después de session_max_age.
Límites actuales
- Los tokens vinculados a DPoP y el cierre de sesión back-channel todavía no se gestionan.
- Las renovaciones se comparten solo dentro de un mismo proceso del servidor. Con varios procesos, el ajuste de la aplicación
refresh_reuse_interval_s(por defecto 5 segundos) permite que una renovación paralela tenga éxito; si lo establece en0, la segunda renovación termina la sesión.