Tester Auth0 dans votre CI/CD : tests d'intégration et mocking

Comment tester votre intégration Auth0 dans un pipeline CI/CD ? Stratégies de mocking, tests d'intégration avec un tenant dédié et tests E2E avec Playwright.

Par Auth0.Expert · 2025-04-03 · 10 min de lecture

CI/CD Tests Testing

Tester Auth0 dans votre CI/CD

TL;DR

Tester l'authentification en CI/CD est complexe. Ce guide présente 3 niveaux : tests unitaires avec JWT mockés, tests d'intégration avec un tenant Auth0 dédié, et tests E2E avec Playwright.

---

Niveau 1 : Tests unitaires avec JWT mocké

Avant ce bloc de code, voici comment générer des JWT factices pour simuler l’authentification Auth0 dans vos tests unitaires Python. Cela permet de tester les règles de sécurité localement, sans dépendre d’Auth0.

# conftest.py (pytest)
import pytest
from datetime import datetime, timedelta
from jose import jwt

TEST_PRIVATE_KEY = """-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA...  # Clé RSA de test uniquement
-----END RSA PRIVATE KEY-----"""

def create_test_token(
    sub: str = "test_user_id",
    email: str = "[email protected]",
    roles: list = None,
    plan: str = "pro",
    expires_in: int = 3600,
) -> str:
    """Créer un JWT de test signé avec une clé privée de test."""
    now = datetime.utcnow()
    payload = {
        "sub": sub,
        "email": email,
        "email_verified": True,
        "iss": "https://test.auth0.com/",
        "aud": "https://api.yourapp.com",
        "iat": int(now.timestamp()),
        "exp": int((now + timedelta(seconds=expires_in)).timestamp()),
        "https://yourapp.com/roles": roles or ["user"],
        "https://yourapp.com/plan": plan,
    }
    return jwt.encode(payload, TEST_PRIVATE_KEY, algorithm="RS256")

@pytest.fixture
def admin_token():
    return create_test_token(roles=["admin"], plan="enterprise")

@pytest.fixture
def user_token():
    return create_test_token(roles=["user"], plan="free")

@pytest.fixture
def expired_token():
    return create_test_token(expires_in=-1)

Ce bloc crée plusieurs jetons adaptés à chaque scénario de test : admin, utilisateur simple, et expiration.

Voici comment utiliser les JWT mockés pour simuler l’accès à vos API, et valider que la logique d’autorisation côté backend fonctionne correctement.

# test_api.py
async def test_admin_can_delete_post(client, admin_token):
    resp = await client.delete(
        "/api/posts/123",
        headers={"Authorization": f"Bearer {admin_token}"}
    )
    assert resp.status_code == 200

async def test_user_cannot_delete_post(client, user_token):
    resp = await client.delete(
        "/api/posts/123",
        headers={"Authorization": f"Bearer {user_token}"}
    )
    assert resp.status_code == 403

async def test_expired_token_rejected(client, expired_token):
    resp = await client.get(
        "/api/data",
        headers={"Authorization": f"Bearer {expired_token}"}
    )
    assert resp.status_code == 401

Ce code valide que chaque profil d’utilisateur obtient bien le comportement attendu sur les routes protégées : autorisé, refusé ou rejeté selon les cas.

---

Remplacer la validation Auth0 en test

Ce code permet de remplacer la validation réelle Auth0 par une version simulée, qui s’active en environnement de test. Idéal pour séparer les tests locaux de l’intégration Auth0 en production.

# Injecter une validation mockée en environnement de test
import os
from unittest.mock import AsyncMock

def get_token_validator():
    if os.environ.get("TESTING"):
        # En test : utiliser la clé publique de test
        return validate_with_test_key
    else:
        # En prod : utiliser les JWKS Auth0
        return validate_with_auth0_jwks

async def validate_with_test_key(token: str) -> dict:
    """Valide avec la clé publique RSA de test (pas Auth0)."""
    TEST_PUBLIC_KEY = os.environ.get("TEST_PUBLIC_KEY")
    return jwt.decode(
        token,
        TEST_PUBLIC_KEY,
        algorithms=["RS256"],
        audience="https://api.yourapp.com",
    )

Ce système garantit que vos tests restent robustes en isolant la couche d’authentification Auth0.

---

Niveau 2 : Tests d'intégration avec tenant dédié

Cette section explique comment automatiser l’exécution de tests d’intégration via un tenant Auth0 isolé dans un pipeline CI/CD avec GitHub Actions. Cela permet de valider l’authentification réelle sur un environnement proche de la production, sans impacter les utilisateurs.

# GitHub Actions : tenant Auth0 de CI
name: Integration Tests

on: [push]

jobs:
  integration-tests:
    runs-on: ubuntu-latest
    env:
      AUTH0_DOMAIN: ${{ secrets.AUTH0_CI_DOMAIN }}
      AUTH0_CLIENT_ID: ${{ secrets.AUTH0_CI_CLIENT_ID }}
      AUTH0_CLIENT_SECRET: ${{ secrets.AUTH0_CI_CLIENT_SECRET }}
      AUTH0_MGMT_TOKEN: ${{ secrets.AUTH0_CI_MGMT_TOKEN }}
      AUTH0_TEST_USER_EMAIL: [email protected]
      AUTH0_TEST_USER_PASSWORD: ${{ secrets.AUTH0_CI_TEST_PASSWORD }}

    steps:
      - uses: actions/checkout@v4

      - name: Start services
        run: docker compose -f docker-compose.test.yml up -d

      - name: Get Auth0 token for tests
        run: |
          TOKEN=$(curl -s -X POST "https://$AUTH0_DOMAIN/oauth/token"             -H "Content-Type: application/json"             -d '{
              "grant_type": "password",
              "username": "'$AUTH0_TEST_USER_EMAIL'",
              "password": "'$AUTH0_TEST_USER_PASSWORD'",
              "client_id": "'$AUTH0_CLIENT_ID'",
              "client_secret": "'$AUTH0_CLIENT_SECRET'",
              "audience": "https://api.yourapp.com",
              "scope": "openid email"
            }' | jq -r '.access_token')
          echo "AUTH0_TEST_TOKEN=$TOKEN" >> $GITHUB_ENV

      - name: Run integration tests
        run: pytest tests/integration/ -v
        env:
          AUTH0_TEST_TOKEN: ${{ env.AUTH0_TEST_TOKEN }}

Ce workflow obtient un vrai token Auth0, puis exécute les tests d’intégration avec les services montés — il couvre l’ensemble du parcours d’authentification.

---

Niveau 3 : Tests E2E avec Playwright

Ici, nous automatisons les parcours utilisateur authentifiés de bout en bout avec Playwright, en intégrant la page de login Auth0 Universal Login. Ceci permet de garantir l’expérience réelle depuis le navigateur.

// tests/e2e/auth.spec.ts
import { test, expect } from '@playwright/test';

const TEST_EMAIL = process.env.E2E_TEST_EMAIL!;
const TEST_PASSWORD = process.env.E2E_TEST_PASSWORD!;

test.describe('Authentication Flow', () => {
  test('should login successfully with valid credentials', async ({ page }) => {
    await page.goto('/');
    await page.click('[data-testid="login-button"]');

    // Auth0 Universal Login
    await page.waitForURL(/auth0\.com/);
    await page.fill('[name="email"]', TEST_EMAIL);
    await page.fill('[name="password"]', TEST_PASSWORD);
    await page.click('[name="submit"]');

    // Callback + redirect
    await page.waitForURL('/dashboard');
    await expect(page.locator('[data-testid="user-menu"]')).toBeVisible();
  });

  test('should show error with invalid credentials', async ({ page }) => {
    await page.goto('/');
    await page.click('[data-testid="login-button"]');
    await page.fill('[name="email"]', TEST_EMAIL);
    await page.fill('[name="password"]', 'wrong-password');
    await page.click('[name="submit"]');

    await expect(page.locator('.error-message')).toBeVisible();
  });

  test('should protect authenticated routes', async ({ page }) => {
    await page.goto('/dashboard');
    await page.waitForURL(/auth0\.com/); // Redirect vers Auth0
  });
});

// Fixture : se connecter une fois et réutiliser la session
test.beforeEach(async ({ page, context }) => {
  // Stocker les tokens Auth0 en localStorage
  await context.addInitScript(() => {
    // Bypass Auth0 en environnement de test
    window.E2E_BYPASS_AUTH = true;
  });
});

Ce script E2E simule le login, la gestion d’erreur, et la protection des routes, pour une validation complète côté Front.

---

Mock Auth0 complet pour les tests locaux

Vous pouvez simuler complètement Auth0 en local avec ce serveur Express, ce qui permet d’avoir tous les endpoints principaux requis pour les tests (OpenID, token, JWKS).

// mock-auth0-server.js : serveur Auth0 minimal pour les tests locaux
const express = require('express');
const jwt = require('jsonwebtoken');
const { generateKeyPairSync } = require('crypto');

const { privateKey, publicKey } = generateKeyPairSync('rsa', { modulusLength: 2048 });
const app = express();

app.get('/.well-known/openid-configuration', (req, res) => {
  res.json({
    issuer: 'http://localhost:4444/',
    authorization_endpoint: 'http://localhost:4444/authorize',
    token_endpoint: 'http://localhost:4444/token',
    jwks_uri: 'http://localhost:4444/.well-known/jwks.json',
  });
});

app.post('/token', express.json(), (req, res) => {
  const token = jwt.sign(
    {
      sub: 'test_user_id',
      email: '[email protected]',
      'https://yourapp.com/roles': ['admin'],
    },
    privateKey,
    { algorithm: 'RS256', expiresIn: '1h', audience: 'https://api.yourapp.com' }
  );
  res.json({ access_token: token, token_type: 'Bearer', expires_in: 3600 });
});

app.listen(4444, () => console.log('Mock Auth0 server running on port 4444'));

Ce serveur factice permet de remplacer Auth0 dans votre stack locale pour accélérer le développement et les tests.

---

> 🧪 Testing Auth0 : nos équipes mettent en place votre stratégie de tests d'authentification complète (unit, integration, E2E) en 1 semaine.

📧 [email protected]


Autres articles