Passer au contenu principal
Prérequis : Avant de commencer, assurez-vous d’avoir installé les éléments suivants :
  • Python 3.9 ou version ultérieure
  • Le gestionnaire de paquets pip ou Poetry
  • jq - Requis pour configurer l’interface de ligne de commande d’Auth0
  • Votre éditeur de code préféré
Compatibilité des versions de Flask : Ce guide de démarrage rapide nécessite Flask 3.0 ou une version ultérieure pour la prise en charge native d’async.

Pour commencer

Ce guide explique comment intégrer Auth0 à une API Python, nouvelle ou existante, développée avec Flask.
1

Créez un nouveau projet Flask

Créez un nouveau répertoire pour votre API Flask :
mkdir flask-auth0-api
cd flask-auth0-api
Créez un environnement virtuel et activez-le :
python -m venv venv
source venv/bin/activate  # Sur Windows : venv\Scripts\activate
2

Installer les dépendances

Créez un fichier requirements.txt contenant les dépendances suivantes :
requirements.txt
flask>=3.0
auth0-api-python
python-dotenv
Installez les dépendances :
pip install -r requirements.txt
3

Configurez votre API Auth0

Ensuite, vous devez créer une nouvelle API sur votre locataire Auth0 et configurer votre application.Vous pouvez le faire automatiquement en exécutant une commande CLI, ou manuellement dans le Dashboard :
  1. Accédez à Auth0 DashboardApplicationsAPIs
  2. Cliquez sur Create API
  3. Saisissez les détails de votre API :
    • Nom : My Flask API
    • Identifiant : https://my-flask-api (ce sera votre audience)
    • Algorithme de signature : RS256
  4. Cliquez sur Create
  5. Copiez votre Domaine depuis le Dashboard (sous ApplicationsApplications[Your App]Settings)
  6. Copiez l’Identifiant que vous venez de créer (il s’agit de votre audience)
Votre Domaine ne doit pas inclure https:// — utilisez uniquement le nom de domaine (par exemple, your-tenant.auth0.com).L’Audience (identifiant de l’API) est un identifiant unique pour votre API et peut être n’importe quel URI valide.
4

Définir les autorisations de l’API

Configurez les autorisations (scopes) de votre API pour contrôler l’accès à des ressources précises :
  1. Dans le Auth0 Dashboard, accédez à ApplicationsAPIs
  2. Sélectionnez votre API (My Flask API)
  3. Ouvrez l’onglet Permissions
  4. Cliquez sur Add Permission
  5. Ajoutez l’autorisation suivante :
    • Permission (Scope) : read:messages
    • Description : Read messages
  6. Cliquez sur Add
Les autorisations définissent les actions qui peuvent être effectuées sur votre API. Vous pouvez ajouter plusieurs autorisations, comme write:messages, delete:messages, etc. Le point de terminaison /api/private-scoped de ce guide de démarrage rapide nécessite l’autorisation read:messages.
5

Configurer l’application Auth0

Si vous avez utilisé la méthode CLI à l’étape 3, votre fichier .env a été créé automatiquement. Passez directement à la création du fichier app.py ci-dessous.
Si vous avez utilisé la méthode Dashboard, créez un fichier .env à la racine de votre projet pour y stocker votre configuration Auth0 :
.env
AUTH0_DOMAIN=your-tenant.us.auth0.com
AUTH0_AUDIENCE=https://my-flask-api
Remplacez your-tenant.us.auth0.com par votre domaine Auth0 et mettez à jour API_IDENTIFIER pour qu’il corresponde à l’identifiant de votre API dans le tableau de bord.
Créez un fichier app.py et configurez le client API Auth0 :
app.py
import os
import asyncio
from flask import Flask, request, jsonify, g
from functools import wraps
from dotenv import load_dotenv
from auth0_api_python import ApiClient, ApiClientOptions
from auth0_api_python.errors import BaseAuthError

# Charger les variables d'environnement
load_dotenv()

app = Flask(__name__)

# Initialiser le client API Auth0 (singleton - créé une seule fois)
api_client = ApiClient(ApiClientOptions(
    domain=os.getenv("AUTH0_DOMAIN"),
    audience=os.getenv("AUTH0_AUDIENCE")
))
5

Créer des routes protégées

Ajoutez un décorateur pour protéger les routes et créez des points de terminaison publics et privés :
app.py
# Décorateur d'authentification
def require_auth(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        auth_header = request.headers.get("Authorization", "")
        
        if not auth_header.startswith("Bearer "):
            return jsonify({"error": "Missing or invalid authorization header"}), 401
        
        token = auth_header.split(" ")[1]
        
        try:
            claims = asyncio.run(api_client.verify_access_token(token))
            g.user_claims = claims
            return f(*args, **kwargs)
        except BaseAuthError as e:
            return (
                jsonify({"error": str(e)}),
                e.get_status_code(),
                e.get_headers()
            )
    
    return decorated_function


# Point de terminaison public - aucune authentification requise
@app.route("/api/public", methods=["GET"])
def public():
    return jsonify({"message": "This endpoint is public"})


# Point de terminaison protégé - authentification requise
@app.route("/api/private", methods=["GET"])
@require_auth
def private():
    return jsonify({
        "message": "This endpoint requires authentication",
        "user": g.user_claims.get("sub")
    })


# Point de terminaison protégé avec validation des permissions
@app.route("/api/private-scoped", methods=["GET"])
@require_auth
def private_scoped():
    scopes = g.user_claims.get("scope", "").split()
    
    if "read:messages" not in scopes:
        return jsonify({"error": "Insufficient permissions"}), 403
    
    return jsonify({
        "message": "Private scoped endpoint - read:messages permission granted",
        "user": g.user_claims.get("sub")
    })


if __name__ == "__main__":
    app.run(debug=True, port=5000)
6

Lancez votre API

Démarrez votre application Flask :
python app.py
Votre API est maintenant en cours d’exécution sur http://localhost:5000.
Point de contrôleVous devriez maintenant avoir une API Flask protégée par Auth0, entièrement fonctionnelle, s’exécutant sur votre localhost, avec trois points de terminaison :
  • /api/public - Accessible sans authentification
  • /api/private - Nécessite un jeton d’accès Auth0 valide
  • /api/private-scoped - Nécessite une authentification et la permission read:messages

Testez votre API

Pour tester vos points de terminaison protégés, vous avez besoin d’un jeton d’accès.

Obtenir un jeton de test

  1. Accédez à l’Auth0 Dashboard
  2. Accédez à Applications → APIs
  3. Sélectionnez votre API
  4. Ouvrez l’onglet Test
  5. Copiez le jeton d’accès

Effectuer une requête

Testez le point de terminaison public (aucun token requis) :
curl http://localhost:5000/api/public
Testez le point de terminaison protégé (jeton requis) :
curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  http://localhost:5000/api/private
Remplacez YOUR_ACCESS_TOKEN par le jeton que vous avez copié dans l’Auth0 Dashboard.

Utilisation avancée

Exigez que certaines revendications soient présentes dans le jeton d’accès :
try:
    claims = await api_client.verify_access_token(
        access_token=token,
        required_claims=["email_verified", "org_id"]
    )
except BaseAuthError as e:
    return jsonify({"error": "Revendications requises manquantes"}), 401
Pour renforcer la sécurité, activez DPoP (Demonstrating Proof-of-Possession). DPoP améliore OAuth 2.0 en associant les jetons d’accès à des clés cryptographiques.
import asyncio


# Configurer le client API avec DPoP activé (mode Allowed)
api_client = ApiClient(ApiClientOptions(
    domain=os.getenv("AUTH0_DOMAIN"),
    audience=os.getenv("AUTH0_AUDIENCE"),
    dpop_enabled=True,   # Par défaut - active la prise en charge de DPoP
    dpop_required=False  # Par défaut - autorise les jetons Bearer et DPoP
))

# Pour le mode Required (DPoP seulement, rejette les jetons Bearer)
# dpop_required=True

# Décorateur compatible avec DPoP
def require_auth_dpop(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        headers = {
            "authorization": request.headers.get("Authorization", ""),
            "dpop": request.headers.get("DPoP", "")  # En-tête de preuve DPoP
        }
        
        try:
            # verify_request() détecte automatiquement le schéma Bearer ou DPoP
            claims = asyncio.run(api_client.verify_request(
                headers=headers,
                http_method=request.method,
                http_url=request.url
            ))
            g.user_claims = claims
            return f(*args, **kwargs)
        except BaseAuthError as e:
            return jsonify({"error": str(e)}), e.get_status_code(), e.get_headers()
    
    return decorated_function

@app.route("/api/dpop-protected")
@require_auth_dpop
def dpop_protected():
    return jsonify({
        "message": "Authentification avec DPoP ou Bearer réussie",
        "user": g.user_claims.get("sub")
    })
La méthode verify_request() détecte automatiquement si la requête utilise l’authentification Bearer ou DPoP. Lorsque DPoP est utilisé, elle valide à la fois le jeton d’accès et la preuve DPoP conformément à la RFC 9449.
Créez un décorateur pour vérifier des scopes précis :
def require_scope(required_scope):
    def decorator(f):
        @wraps(f)
        async def wrapper(*args, **kwargs):
            if not hasattr(g, "user_claims"):
                return jsonify({"error": "Non autorisé"}), 401
            
            scopes = g.user_claims.get("scope", "").split()
            
            if required_scope not in scopes:
                return jsonify({"error": "Permissions insuffisantes"}), 403
            
            return await f(*args, **kwargs)
        return wrapper
    return decorator


@app.route("/api/admin")
@require_auth
@require_scope("admin:write")
async def admin_endpoint():
    return jsonify({"message": "Accès administrateur accordé"})
Implémentez une gestion robuste des erreurs avec des types d’erreur précis :
from auth0_api_python.errors import (
    BaseAuthError,
    VerifyAccessTokenError,
    GetTokenByExchangeProfileError,
    ApiError
)

@app.errorhandler(BaseAuthError)
def handle_auth_error(error):
    """Gérer toutes les erreurs d’authentification Auth0"""
    return jsonify({
        "error": error.get_error_code(),
        "error_description": str(error)
    }), error.get_status_code(), error.get_headers()


@app.errorhandler(VerifyAccessTokenError)
def handle_token_verification_error(error):
    """Gérer spécifiquement les échecs de validation du jeton"""
    app.logger.warning(f"Token verification failed: {str(error)}")
    return jsonify({
        "error": "invalid_token",
        "error_description": str(error)
    }), 401, error.get_headers()


@app.errorhandler(ApiError)
def handle_api_error(error):
    """Gérer les erreurs d’API Auth0 (réseau, limites de débit, etc.)"""
    app.logger.error(f"Auth0 API error: {error.code} - {error.message}")
    return jsonify({
        "error": error.code,
        "error_description": error.message
    }), error.status_code or 500


@app.errorhandler(Exception)
def handle_generic_error(error):
    """Intercepter toute erreur inattendue"""
    app.logger.error(f"Unexpected error: {str(error)}", exc_info=True)
    return jsonify({"error": "Internal server error"}), 500
Toutes les erreurs d’authentification héritent de BaseAuthError, qui fournit des méthodes comme get_status_code(), get_headers() et get_error_code() pour renvoyer des réponses HTTP appropriées avec des en-têtes WWW-Authenticate.
Pour les applications où la plupart des points de terminaison exigent une authentification, utilisez before_request de Flask pour valider les jetons de façon globale :
from flask import g

PUBLIC_ROUTES = ["/api/public", "/health"]

@app.before_request
async def verify_token():
    # Ignorer l’authentification pour les routes publiques
    if request.path in PUBLIC_ROUTES:
        return
    
    auth_header = request.headers.get("Authorization", "")
    
    if not auth_header.startswith("Bearer "):
        return jsonify({"error": "Missing authorization"}), 401
    
    token = auth_header.split(" ")[1]
    
    try:
        claims = await api_client.verify_access_token(token)
        g.user_claims = claims
    except BaseAuthError as e:
        return jsonify({"error": str(e)}), e.get_status_code(), e.get_headers()


@app.route("/api/protected-data")
async def protected_data():
    # Claims automatiquement accessibles via g.user_claims
    return jsonify({"user_id": g.user_claims["sub"]})

Problèmes courants

Symptôme : Vous obtenez des erreurs 401 même avec des jetons qui semblent validesCause : L’audience de votre jeton ne correspond pas à celle configurée dans votre client APISolution :
  1. Vérifiez que AUTH0_AUDIENCE dans votre fichier .env correspond exactement à l’identifiant de l’API Auth0
  2. L’audience est sensible à la casse
  3. Assurez-vous que l’audience est au format URL ou URN (par ex. : https://my-api et non my-api)
Symptôme : La validation du jeton échoue en raison d’une non-correspondance de l’émetteurCause : La configuration du Domaine ne correspond pas à l’émetteur du jetonSolution :
  1. Vérifiez que AUTH0_DOMAIN est correct (par ex. : tenant.us.auth0.com)
  2. N’incluez pas https:// dans le domaine
  3. N’ajoutez pas de barre oblique à la fin
Symptôme : Valeurs None ou erreurs liées aux variables d’environnementCause : Les variables d’environnement ne sont pas chargées ou le fichier .env est introuvableSolution :
  1. Assurez-vous que le fichier .env existe à la racine de votre projet
  2. Vérifiez que load_dotenv() est appelé avant d’accéder à os.getenv()
  3. Vérifiez que les noms des variables correspondent exactement (respect de la casse)
Symptôme : RuntimeError: This event loop is already running ou erreurs async semblablesCause : Utilisation de routes async sans Flask 3.0+ ou mélange inadéquat de sync et d’asyncSolution :
  1. Passez à Flask 3.0 ou version ultérieure : pip install --upgrade flask
  2. Assurez-vous que tous les gestionnaires de routes qui utilisent api_client sont déclarés avec async def
  3. N’utilisez pas asyncio.run() dans les gestionnaires de routes
Symptôme : VerifyAccessTokenError: Token is expiredCause : Le jeton d’accès a dépassé sa date d’expirationSolution :
  1. Demandez un nouveau jeton à partir de l’onglet Test dans Auth0 Dashboard
  2. Implémentez le renouvellement du jeton dans votre application cliente
  3. Les jetons du Dashboard sont généralement valides pendant 24 heures
Symptôme : Erreur Missing or invalid authorization headerCause : La requête n’inclut pas l’en-tête Authorization ou utilise un format incorrectSolution :
  1. Assurez-vous que l’en-tête s’appelle Authorization (A majuscule)
  2. Utilisez le format suivant : Authorization: Bearer YOUR_TOKEN
  3. N’ajoutez pas de guillemets autour du jeton

Ressources supplémentaires

Documentation du SDK

Documentation complète du SDK et référence de l’API

Documentation de Flask

Documentation officielle du framework Flask

Auth0 Dashboard

Gérez votre locataire Auth0 et vos API

Guide d’authentification des API

En savoir plus sur les jetons d’accès et la sécurité des API

Documentation sur DPoP

En savoir plus sur la sécurité par preuve de possession

Forum communautaire

Obtenez de l’aide auprès de la communauté Auth0

Prochaines étapes

Consultez le dépôt d’exemples de l’API Python d’Auth0 pour voir des exemples complets et fonctionnels avec Flask.