auth0-flask

À utiliser pour ajouter la connexion, la déconnexion et le profil utilisateur à une application web Flask avec une authentification basée sur les sessions — intègre auth0-server-python pour les applications à rendu serveur avec les flux login/callback/profile/logout.

npx skills add https://github.com/auth0/agent-skills --skill auth0-flask

Intégration Auth0 avec Flask Web App

Ajoutez la connexion, la déconnexion et le profil utilisateur à une application web Flask en utilisant auth0-server-python.


Prérequis

  • Application Flask
  • Application Web Régulière Auth0 configurée (pas une API — doit être une Application)
  • Si vous n'avez pas encore configuré Auth0, utilisez d'abord la skill auth0-quickstart

Quand NE PAS l'utiliser

  • APIs Python avec validation JWT Bearer — Utilisez auth0-fastapi-api pour FastAPI, ou consultez le quickstart Django REST Framework
  • Application web FastAPI avec UI de connexion/déconnexion — Pas de skill dédiée pour le moment ; consultez le quickstart FastAPI
  • Applications monopage — Utilisez auth0-react, auth0-vue ou auth0-angular pour l'authentification côté client
  • Applications Next.js — Utilisez auth0-nextjs qui gère à la fois le client et le serveur
  • Applications web Node.js — Utilisez auth0-express ou auth0-fastify pour l'authentification basée sur les sessions

Workflow de démarrage rapide

1. Installer le SDK

pip install auth0-server-python "flask[async]" python-dotenv

Critique : Vous devez installer flask[async] (pas seulement flask). L'extra [async] installe asgiref qui est requis pour Flask 2.0+ afin de supporter les gestionnaires de route async def. Sans cela, les routes asynchrones ne fonctionneront pas. Dans requirements.txt, utilisez flask[async]>=2.0.0.

2. Configurer l'environnement

Créez .env :

AUTH0_DOMAIN=your-tenant.us.auth0.com
AUTH0_CLIENT_ID=your_client_id
AUTH0_CLIENT_SECRET=your_client_secret
AUTH0_SECRET=your_generated_app_secret
AUTH0_REDIRECT_URI=http://localhost:5000/callback

AUTH0_DOMAIN est votre domaine de locataire Auth0 (sans https://). AUTH0_CLIENT_ID et AUTH0_CLIENT_SECRET proviennent des paramètres de votre Application Auth0. AUTH0_SECRET est utilisé pour chiffrer les données de session — générez avec openssl rand -hex 64.

3. Configurer le tableau de bord Auth0

Dans les paramètres de votre Application Auth0 :

  • Allowed Callback URLs : http://localhost:5000/callback
  • Allowed Logout URLs : http://localhost:5000

4. Créer un module d'authentification

Créez auth.py pour initialiser le ServerClient avec des magasins basés sur la session Flask. Les magasins utilisent la session intégrée de Flask (basée sur les cookies par défaut) pour une configuration sans état — aucune base de données externe requise :

import os
from flask import session as flask_session
from auth0_server_python.auth_server.server_client import ServerClient
from auth0_server_python.auth_types import StateData, TransactionData
from auth0_server_python.store import StateStore, TransactionStore
from dotenv import load_dotenv

load_dotenv()  # Uses .env by default; pass load_dotenv(".env.local") if credentials are in .env.local


class FlaskSessionStateStore(StateStore):
    """State store that uses Flask's session for persistence."""

    def __init__(self, secret: str):
        super().__init__({"secret": secret})

    async def set(self, identifier, state, remove_if_expires=False, options=None):
        data = state.dict() if hasattr(state, "dict") else state
        flask_session[identifier] = self.encrypt(identifier, data)

    async def get(self, identifier, options=None):
        data = flask_session.get(identifier)
        if data is None:
            return None
        decrypted = self.decrypt(identifier, data)
        # Ensure to not return a dict, as the underlying SDK expects a StateData instance, not a dict
        return StateData(**decrypted) if isinstance(decrypted, dict) else decrypted

    async def delete(self, identifier, options=None):
        flask_session.pop(identifier, None)

    async def delete_by_logout_token(self, claims, options=None):
        pass


class FlaskSessionTransactionStore(TransactionStore):
    """Transaction store that uses Flask's session for persistence."""

    def __init__(self, secret: str):
        super().__init__({"secret": secret})

    async def set(self, identifier, state, remove_if_expires=False, options=None):
        data = state.dict() if hasattr(state, "dict") else state
        flask_session[identifier] = self.encrypt(identifier, data)

    async def get(self, identifier, options=None):
        data = flask_session.get(identifier)
        if data is None:
            return None
        decrypted = self.decrypt(identifier, data)
        # Ensure to not return a dict, as the underlying SDK expects a TransactionData instance, not a dict
        return TransactionData(**decrypted) if isinstance(decrypted, dict) else decrypted

    async def delete(self, identifier, options=None):
        flask_session.pop(identifier, None)


secret = os.getenv("AUTH0_SECRET")

auth0 = ServerClient(
    domain=os.getenv("AUTH0_DOMAIN"),
    client_id=os.getenv("AUTH0_CLIENT_ID"),
    client_secret=os.getenv("AUTH0_CLIENT_SECRET"),
    secret=secret,
    redirect_uri=os.getenv("AUTH0_REDIRECT_URI"),
    state_store=FlaskSessionStateStore(secret=secret),
    transaction_store=FlaskSessionTransactionStore(secret=secret),
    authorization_params={"scope": "openid profile email"},
)

Créez une instance ServerClient et réutilisez-la. Ne codez jamais les identifiants en dur — utilisez toujours les variables d'environnement.

Comment cela fonctionne : La session par défaut de Flask est basée sur les cookies (sans état). Le SDK chiffre les données de session (tokens, profil utilisateur) avec JWE avant de les stocker dans la session, donc les données sont à la fois signées et chiffrées dans le cookie. Aucune base de données côté serveur n'est requise.

Pas de store_options ou before_request nécessaires : Le SDK supporte le passage de store_options (par exemple, objets request/response) aux méthodes de magasin. Puisque ces magasins utilisent flask.session — qui est disponible globalement pendant une requête — ils n'ont besoin de rien de store_options, vous pouvez donc appeler les méthodes du SDK sans le passer. Si vous implémentez un magasin personnalisé qui gère directement les cookies (au lieu d'utiliser flask.session), vous devriez réintroduire store_options avec {"request": request, "response": response}.

Note sur la taille des cookies : Les sessions sans état stockent toutes les données dans un cookie (limite ~4 KB). Pour la plupart des applications, c'est suffisant. Si vous stockez de grandes quantités de données de session ou dépassez les limites de taille des cookies, passez à la configuration avec état.

5. Configurer l'application Flask

Dans app.py, configurez Flask avec la clé secrète et la configuration de session :

import os
from flask import Flask, redirect, request
from auth import auth0
from dotenv import load_dotenv

load_dotenv()

app = Flask(__name__)
app.secret_key = os.getenv("AUTH0_SECRET")
app.config.update(
    SESSION_COOKIE_SECURE=False,  # Set to True in production (requires HTTPS)
    SESSION_COOKIE_HTTPONLY=True,
    SESSION_COOKIE_SAMESITE="Lax",
)

Critique : app.secret_key doit être défini pour la gestion des sessions Flask. Sans cela, les sessions ne fonctionneront pas.

Pour la production : Définissez SESSION_COOKIE_SECURE=True lors du déploiement avec HTTPS. Le laisser à False en production permet aux cookies de session d'être envoyés sur des connexions non chiffrées.

6. Ajouter la route d'accueil

@app.route("/")
async def home():
    user = await auth0.get_user()
    if user:
        return f"Hello, {user['name']}! <a href='/profile'>Profile</a> | <a href='/logout'>Logout</a>"
    return "Welcome! <a href='/login'>Login</a>"

7. Ajouter la route de connexion

@app.route("/login")
async def login():
    authorization_url = await auth0.start_interactive_login()
    return redirect(authorization_url)

start_interactive_login() retourne une chaîne URL pointant vers la page Universal Login d'Auth0. Vous devez l'envelopper dans redirect(). Les paramètres d'autorisation (scope, redirect_uri) sont déjà configurés sur le ServerClient.

8. Ajouter la route de callback

@app.route("/callback")
async def callback():
    try:
        await auth0.complete_interactive_login(str(request.url))
        return redirect("/")
    except Exception as e:
        return f"Authentication error: {str(e)}", 400

Passez str(request.url) comme premier argument — c'est l'URL complète de callback, y compris les paramètres de requête du code d'autorisation. Enveloppez toujours dans try/except puisque l'échange de tokens peut échouer (par exemple, code expiré, non-concordance CSRF).

9. Ajouter la route de profil (protégée)

@app.route("/profile")
async def profile():
    user = await auth0.get_user()
    if user is None:
        return redirect("/login")
    return (
        f"<h1>{user['name']}</h1>"
        f"<p>Email: {user['email']}</p>"
        f"<img src='{user['picture']}' alt='{user['name']}' width='100' />"
        f"<p><a href='/logout'>Logout</a></p>"
    )

get_user() retourne le profil de l'utilisateur depuis la session, ou None s'il n'est pas connecté.

10. Ajouter la route de déconnexion

@app.route("/logout")
async def logout():
    url = await auth0.logout()
    return redirect(url)

logout() retourne l'URL de déconnexion Auth0. Redirigez l'utilisateur vers celle-ci.

11. Tester l'application

flask run

Visitez http://localhost:5000/login pour démarrer le flux de connexion.


Configuration avec état avec Redis

Pour les applications de production ou quand les données de session dépassent les limites de taille des cookies, utilisez Flask-Session avec Redis pour stocker les sessions côté serveur. Seul un ID de session est stocké dans le cookie.

1. Installer les dépendances

pip install flask-session redis

2. Configurer Flask-Session

Mettez à jour app.py pour utiliser des sessions sauvegardées par Redis :

import os
from flask import Flask, redirect, request
from flask_session import Session
from auth import auth0
from dotenv import load_dotenv

load_dotenv()

app = Flask(__name__)
app.secret_key = os.getenv("AUTH0_SECRET")
app.config.update(
    SESSION_TYPE="redis",
    SESSION_PERMANENT=True,
    SESSION_KEY_PREFIX="auth0:",
    SESSION_COOKIE_SECURE=False,
    SESSION_COOKIE_HTTPONLY=True,
    SESSION_COOKIE_SAMESITE="Lax",
)
Session(app)

3. Aucun changement de magasin nécessaire

Les mêmes FlaskSessionStateStore et FlaskSessionTransactionStore de auth.py fonctionnent sans modification. Flask-Session bascule de manière transparente le backend de flask.session des cookies à Redis — les magasins continuent à utiliser flask.session comme avant.

Les routes sont identiques à la configuration sans état — aucun changement de code nécessaire.


Erreurs courantes

Erreur Solution
Codage en dur de domain, client_id ou client_secret dans le code source Lisez toujours depuis les variables d'environnement — n'intégrez jamais les identifiants dans le code
Utiliser Authlib ou python-jose directement Non nécessaire ; auth0-server-python gère tous les flux OAuth/OIDC
Utiliser Flask-Login ou Flask-Dance Non nécessaire ; le SDK gère les sessions et l'authentification
Parser manuellement les JWTs avec jwt.decode() Le SDK gère la validation des tokens en interne
Installer flask sans l'extra [async] Doit utiliser flask[async]>=2.0.0 dans requirements.txt — sans cela, les gestionnaires de route asynchrones échouent silencieusement
Utiliser des gestionnaires de route synchrones Tous les routes appelant les méthodes du SDK doivent être async def et utiliser await
Oublier app.secret_key Requis pour la gestion des sessions Flask — sans cela, les sessions échouent silencieusement
Utiliser auth0-fastapi-api dans Flask Ce package est pour les APIs FastAPI — utilisez auth0-server-python pour Flask
Passer domain en tant qu'URL complète avec https:// domain doit être le domaine nu, par exemple my-tenant.us.auth0.com, pas https://my-tenant.us.auth0.com
Ne pas configurer l'URL de callback dans le tableau de bord Auth0 Doit ajouter http://localhost:5000/callback à Allowed Callback URLs
Retourner start_interactive_login() directement Elle retourne une chaîne URL, pas une réponse — doit envelopper dans redirect()
Ne pas gérer les erreurs dans /callback complete_interactive_login() peut échouer — enveloppez toujours dans try/except
Appeler les méthodes du SDK sans await Toutes les méthodes du SDK sont asynchrones — oublier await retourne une coroutine au lieu du résultat
Passer les options positivement à logout() Utilisez logout(store_options=...) — le premier paramètre positionnel est LogoutOptions, pas les options de magasin
S'attendre à ce que la déconnexion backchannel fonctionne Non supportée avec les sessions basées sur les cookies — delete_by_logout_token est une no-op. Utilisez la route /logout standard
Déployer avec SESSION_COOKIE_SECURE=False Doit définir à True en production — les cookies sont envoyés sur HTTP sinon

Méthodes clés du SDK

Toutes les méthodes sont asynchrones :

Méthode Signature Objectif
start_interactive_login await auth0.start_interactive_login() Retourne une chaîne URL d'autorisation — enveloppez dans redirect()
complete_interactive_login await auth0.complete_interactive_login(str(request.url)) Traite l'URL de callback, échange le code contre les tokens
get_user await auth0.get_user() Retourne le dictionnaire utilisateur de session actuel ou None
get_access_token await auth0.get_access_token() Retourne le token d'accès pour appeler les APIs externes
logout await auth0.logout() Retourne la chaîne URL de déconnexion Auth0

Skills associées

  • auth0-express — Pour les applications web Express serveur-rendu avec sessions de connexion/déconnexion
  • auth0-fastify — Pour les applications Fastify avec authentification basée sur les sessions

Référence rapide

Configuration du ServerClient :

auth0 = ServerClient(
    domain=os.getenv("AUTH0_DOMAIN"),                    # required
    client_id=os.getenv("AUTH0_CLIENT_ID"),              # required
    client_secret=os.getenv("AUTH0_CLIENT_SECRET"),      # required
    secret=os.getenv("AUTH0_SECRET"),                    # required (encryption secret)
    redirect_uri=os.getenv("AUTH0_REDIRECT_URI"),        # required
    state_store=FlaskSessionStateStore(secret=secret),   # required
    transaction_store=FlaskSessionTransactionStore(secret=secret),  # required
    authorization_params={"scope": "openid profile email"},  # recommended
)

Motif de protection des routes :

user = await auth0.get_user()
if user is None:
    return redirect("/login")

Variables d'environnement :

  • AUTH0_DOMAIN — votre domaine de locataire Auth0 (par exemple tenant.us.auth0.com)
  • AUTH0_CLIENT_ID — l'ID client de votre Application
  • AUTH0_CLIENT_SECRET — le secret client de votre Application
  • AUTH0_SECRET — clé secrète de chiffrement et de session
  • AUTH0_REDIRECT_URI — URL de callback (par exemple http://localhost:5000/callback)

Documentation détaillée

  • Guide de configuration - Scripts de configuration automatisés, configuration de l'environnement, utilisation de l'Auth0 CLI
  • Guide d'intégration - Routes protégées, appel d'APIs, gestion des sessions, gestion des erreurs
  • Référence API - API complète ServerClient, options de configuration, implémentation des magasins, sécurité

Références

Skills similaires