Intégrer Auth0 et Stripe : lier identité et abonnements

Synchroniser les identités Auth0 avec les abonnements Stripe. Accorder l'accès selon le plan, gérer les upgrades et les churns sans API supplémentaire.

Par Auth0.Expert · 2025-02-08 · 11 min de lecture

Stripe Abonnements SaaS

Auth0 + Stripe : identité et abonnements synchronisés

TL;DR

La plupart des SaaS ont besoin de lier l'identité utilisateur (Auth0) à l'abonnement (Stripe). Ce guide présente le pattern complet : création du customer Stripe au signup, injection du plan dans le token, et gestion des webhooks Stripe.

---

Architecture recommandée

Ce diagramme illustre les étapes clés pour synchroniser utilisateurs Auth0 et abonnements Stripe, depuis l'inscription jusqu'à la mise à jour du plan.

1. Signup Auth0 → Action Post Registration
                → Créer Customer Stripe
                → Stocker stripe_customer_id dans app_metadata

2. Login Auth0  → Action Post Login
                → Lire le plan depuis app_metadata
                → Injecter dans le token

3. Stripe Event → Webhook → Backend
                → Mettre à jour app_metadata Auth0
                → Plan actif ou expiré

---

Étape 1 : Créer un Customer Stripe au signup

Ce code déclenche la création d'un client Stripe lorsque l'utilisateur s'inscrit sur votre application. L'ID Stripe est ensuite stocké dans l'app_metadata Auth0, ce qui permet de relier les comptes.

// Action: Post User Registration
exports.onExecutePostUserRegistration = async (event, api) => {
  const stripe = require('stripe')(event.secrets.STRIPE_SECRET_KEY);

  // Créer le customer Stripe
  const customer = await stripe.customers.create({
    email: event.user.email,
    name: event.user.name,
    metadata: {
      auth0_user_id: event.user.user_id,
    },
  });

  // Stocker l'ID Stripe dans Auth0 app_metadata
  await fetch(
    `https://${event.secrets.AUTH0_DOMAIN}/api/v2/users/${event.user.user_id}`,
    {
      method: 'PATCH',
      headers: {
        Authorization: `Bearer ${await getMgmtToken(event.secrets)}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        app_metadata: {
          stripe_customer_id: customer.id,
          plan: 'free',
          plan_status: 'active',
        },
      }),
    }
  );
};

Ce script lie chaque nouvel utilisateur Auth0 à un compte Stripe unique et initialise son plan d'abonnement.

---

Étape 2 : Injecter le plan dans le token

Lors de chaque connexion, ce code lit le plan de l'utilisateur depuis l'app_metadata et ajoute ses informations d'abonnement directement dans le token. Cela permet à votre API de contrôler l'accès selon le plan.

// Action: Post Login
exports.onExecutePostLogin = async (event, api) => {
  const namespace = 'https://yourapp.com/';
  const meta = event.user.app_metadata || {};

  api.accessToken.setCustomClaim(`${namespace}plan`, meta.plan || 'free');
  api.accessToken.setCustomClaim(`${namespace}plan_status`, meta.plan_status || 'active');
  api.accessToken.setCustomClaim(`${namespace}stripe_customer_id`, meta.stripe_customer_id);

  // Features disponibles selon le plan
  const PLAN_FEATURES = {
    free:       ['basic_analytics', 'up_to_5_projects'],
    pro:        ['advanced_analytics', 'unlimited_projects', 'api_access'],
    enterprise: ['all', 'custom_domain', 'sla', 'audit_logs'],
  };

  const features = PLAN_FEATURES[meta.plan] || PLAN_FEATURES.free;
  api.accessToken.setCustomClaim(`${namespace}features`, features);
};

Cette action enrichit le token JWT avec les droits et paramètres du plan, simplifiant leur vérification côté API.

---

Étape 3 : Webhooks Stripe → Mise à jour Auth0

Ce handler FastAPI reçoit les événements Stripe (webhooks) comme le changement de souscription ou la fin d'un abonnement. Lorsqu'un changement a lieu côté Stripe, il met à jour automatiquement l'app_metadata Auth0 pour refléter le nouveau statut d'abonnement de l'utilisateur.

# FastAPI webhook handler
import stripe
from fastapi import FastAPI, Request, Header, HTTPException

app = FastAPI()
stripe.api_key = os.environ['STRIPE_SECRET_KEY']
STRIPE_WEBHOOK_SECRET = os.environ['STRIPE_WEBHOOK_SECRET']

@app.post("/webhooks/stripe")
async def stripe_webhook(
    request: Request,
    stripe_signature: str = Header(None, alias="stripe-signature"),
):
    body = await request.body()

    try:
        event = stripe.Webhook.construct_event(
            body, stripe_signature, STRIPE_WEBHOOK_SECRET
        )
    except stripe.error.SignatureVerificationError:
        raise HTTPException(400, "Signature invalide")

    if event.type == "customer.subscription.updated":
        await handle_subscription_updated(event.data.object)
    elif event.type == "customer.subscription.deleted":
        await handle_subscription_deleted(event.data.object)
    elif event.type == "invoice.payment_failed":
        await handle_payment_failed(event.data.object)

    return {"received": True}

async def handle_subscription_updated(subscription):
    """Met à jour le plan dans Auth0 quand Stripe change."""
    customer_id = subscription.customer
    status = subscription.status  # active, past_due, canceled, unpaid
    price_id = subscription.items.data[0].price.id

    PRICE_TO_PLAN = {
        'price_pro_monthly': 'pro',
        'price_pro_yearly': 'pro',
        'price_enterprise': 'enterprise',
    }
    plan = PRICE_TO_PLAN.get(price_id, 'free')

    # Trouver l'utilisateur Auth0 via stripe_customer_id
    users = await auth0_search_users(f'app_metadata.stripe_customer_id:"{customer_id}"')
    if not users:
        return

    user_id = users[0]['user_id']
    await auth0_update_metadata(user_id, {
        'plan': plan,
        'plan_status': status,
        'stripe_subscription_id': subscription.id,
    })

Ce code assure la synchronisation automatique des données d’abonnement Stripe dans Auth0 pour chaque utilisateur concerné.

---

Protéger les routes selon le plan

Avec ce middleware Express, vous contrôlez l'accès aux routes API selon le plan d'abonnement contenu dans le token JWT. Cela permet de rendre certaines fonctionnalités exclusives aux utilisateurs premium.

// Middleware Express : vérifier le plan dans le token
function requirePlan(...allowedPlans) {
  return (req, res, next) => {
    const plan = req.auth?.['https://yourapp.com/plan'];
    if (!allowedPlans.includes(plan)) {
      return res.status(403).json({
        error: 'plan_required',
        required: allowedPlans,
        current: plan,
        upgrade_url: 'https://yourapp.com/pricing',
      });
    }
    next();
  };
}

// Usage
app.get('/api/analytics/advanced', requirePlan('pro', 'enterprise'), handler);
app.get('/api/export', requirePlan('enterprise'), handler);

---

Portail client Stripe (self-service)

Ce code permet de rediriger l'utilisateur vers le portail client Stripe, où il peut gérer son abonnement de façon autonome (factures, mise à niveau, etc.).

// Rediriger l'utilisateur vers le portail Stripe (gérer son abonnement)
app.get('/billing/portal', requireAuth, async (req, res) => {
  const stripeCustomerId = req.auth['https://yourapp.com/stripe_customer_id'];

  const session = await stripe.billingPortal.sessions.create({
    customer: stripeCustomerId,
    return_url: 'https://yourapp.com/dashboard',
  });

  res.redirect(session.url);
});

---

Trial Management

Ce code ajoute lors de la phase de post-inscription la création automatique d'un abonnement d'essai (trial) Stripe, puis met à jour le statut utilisateur dans Auth0 avec la période de test.

// Action Post Registration : démarrer un trial de 14 jours
exports.onExecutePostUserRegistration = async (event, api) => {
  const stripe = require('stripe')(event.secrets.STRIPE_SECRET_KEY);

  // Créer un abonnement trial
  const subscription = await stripe.subscriptions.create({
    customer: customerId,
    items: [{ price: event.secrets.STRIPE_PRO_PRICE_ID }],
    trial_period_days: 14,
  });

  await updateAuth0Metadata(event.user.user_id, {
    plan: 'pro',
    plan_status: 'trialing',
    trial_end: new Date(subscription.trial_end * 1000).toISOString(),
  });
};

Cela permet d’activer l’accès au plan payant pendant la période d’essai sans intervention manuelle.

---

> 💳 Architecture SaaS clé en main : nous intégrons Auth0, Stripe et votre API pour un système de gestion des abonnements robuste avec provisioning et déprovisionning automatiques.

📧 [email protected]


Autres articles