Migration d'un IdP legacy vers Auth0 : méthodologie complète

Migrer depuis un système d'authentification maison ou un LDAP vers Auth0. Analyse des risques, plan de migration et gestion des mots de passe.

Par Auth0.Expert · 2025-05-08 · 12 min de lecture

Migration LDAP Legacy IdP

Migration d'un IdP legacy vers Auth0

TL;DR

Migrer depuis un système d'authentification maison ou LDAP vers Auth0 est le projet IAM le plus risqué. Ce guide présente la méthodologie éprouvée pour le faire sans déconnecter vos utilisateurs.

---

Analyse des systèmes legacy fréquents

Voici un tableau comparatif des principaux systèmes legacy et leur compatibilité de migration vers Auth0.

| Système | Complexité migration | Spécificité | |---------|---------------------|-------------| | Auth maison (PHP/Node) | Élevée | Mots de passe en md5/sha1 = reset obligatoire | | Django auth | Moyenne | PBKDF2 compatible avec Auth0 | | LDAP/AD | Moyenne | Custom Database Connection | | Devise (Rails) | Faible | bcrypt compatible | | Spring Security | Moyenne | BCrypt ou Argon2 | | Firebase Auth | Moyenne | Export/Import possible | | Cognito | Moyenne | Voir article dédié |

---

Phase 0 : Inventaire

Avant de démarrer, il est essentiel de répondre à ces questions sur votre base utilisateurs pour mesurer l'effort et les risques de la migration.

# Questions à répondre avant de commencer

# 1. Combien d'utilisateurs ?
SELECT COUNT(*) FROM users WHERE deleted_at IS NULL;

# 2. Quelle algorithme de hash ?
SELECT DISTINCT hash_algorithm FROM users LIMIT 10;

# 3. Quelle proportion a un email vérifié ?
SELECT
  COUNT(*) as total,
  SUM(CASE WHEN email_verified THEN 1 ELSE 0 END) as verified,
  ROUND(100.0 * SUM(CASE WHEN email_verified THEN 1 ELSE 0 END) / COUNT(*), 1) as pct_verified
FROM users;

# 4. Activité récente (pour prioriser)
SELECT
  COUNT(*) as total,
  SUM(CASE WHEN last_login > NOW() - INTERVAL '90 days' THEN 1 ELSE 0 END) as active_90d,
  SUM(CASE WHEN last_login > NOW() - INTERVAL '365 days' THEN 1 ELSE 0 END) as active_1y,
  SUM(CASE WHEN last_login IS NULL THEN 1 ELSE 0 END) as never_logged
FROM users;

---

Phase 1 : Migration des profils (sans mots de passe)

Cette étape consiste à importer les profils utilisateurs dans Auth0, sans le mot de passe, pour préparer l'arrivée des utilisateurs.

# Migrer d'abord les profils → les utilisateurs existaient dans Auth0
# avant même de se connecter

async def migrate_profiles_batch(users: list, connection_id: str):
    """Import des profils utilisateurs sans mots de passe."""
    auth0_users = []
    for user in users:
        auth0_user = {
            "email": user["email"],
            "email_verified": user.get("email_verified", False),
            "name": f"{user.get('first_name', '')} {user.get('last_name', '')}".strip(),
            "given_name": user.get("first_name"),
            "family_name": user.get("last_name"),
            "app_metadata": {
                "legacy_id": str(user["id"]),
                "legacy_created_at": user["created_at"].isoformat() if user.get("created_at") else None,
                "migrated_profile": True,
                "migrated_at": datetime.utcnow().isoformat(),
            }
        }
        auth0_users.append(auth0_user)

    # Auth0 Jobs API : import en masse
    # Le mot de passe n'est PAS inclus → utilisateurs seront invités à définir leur MDP
    # OU on utilise une Custom Database Connection pour la migration lazy

    return await create_import_job(auth0_users, connection_id)

Ce code importe un lot de profils utilisateurs dans Auth0 sans inclure les mots de passe, stockant notamment les métadonnées liées à la migration.

---

Phase 2 : Custom Database Connection pour migration lazy

Ici, on va permettre la migration “à la volée” : les utilisateurs se connectent avec leurs anciens identifiants, Auth0 valide désormais auprès de l’IdP legacy, puis migre leur compte.

// Login Script : vérifier dans legacy, créer dans Auth0
exports.login = async function (email, password, callback) {
  const crypto = require('crypto');
  const bcrypt = require('bcryptjs');

  // Appeler votre API legacy pour vérifier les credentials
  try {
    const resp = await fetch(`${configuration.LEGACY_API}/auth/verify`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ email, password }),
    });

    const { success, user } = await resp.json();

    if (!success || !user) {
      return callback(null, null); // Mauvais credentials
    }

    // Hash du mot de passe en SHA-1 (legacy) → bcrypt (Auth0)
    // Auth0 va automatiquement migrer vers bcrypt
    return callback(null, {
      user_id: user.id.toString(),
      email: user.email,
      email_verified: user.email_verified || false,
      given_name: user.first_name,
      family_name: user.last_name,
    });

  } catch (err) {
    return callback(err);
  }
};

// Change Password Script
exports.changePassword = async function (email, newPassword, callback) {
  // Changer dans le système legacy (pour la période de transition)
  try {
    await fetch(`${configuration.LEGACY_API}/users/password`, {
      method: 'PATCH',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ email, password: newPassword }),
    });
    return callback(null, true);
  } catch (err) {
    return callback(err);
  }
};

Ce script assure la connexion et la mise à jour de mot de passe en interrogeant directement l'API legacy, pour garantir une bascule transparente lors des premiers logins.

---

Gestion des algorithmes de hash legacy

Si plusieurs algorithmes de hash coexistent, il faut tous les gérer pour garantir la migration automatique de chaque utilisateur.

// Auth0 Custom Database : tester les hashs legacy

// MD5 (dangereux, mais fréquent dans les vieux systèmes)
function verifyMd5(password, hash) {
  const crypto = require('crypto');
  return crypto.createHash('md5').update(password).digest('hex') === hash;
}

// SHA-256 avec salt
function verifySha256(password, hash, salt) {
  const crypto = require('crypto');
  const computed = crypto.createHash('sha256')
    .update(salt + password)
    .digest('hex');
  return computed === hash;
}

// Argon2 (moderne)
async function verifyArgon2(password, hash) {
  const argon2 = require('argon2');
  return await argon2.verify(hash, password);
}

// Dans le login script
exports.login = async function (email, password, callback) {
  const user = await getLegacyUser(email);
  if (!user) return callback(null, null);

  let valid = false;
  switch (user.hash_algo) {
    case 'md5': valid = verifyMd5(password, user.password_hash); break;
    case 'sha256': valid = verifySha256(password, user.password_hash, user.salt); break;
    case 'bcrypt':
      const bcrypt = require('bcryptjs');
      valid = await bcrypt.compare(password, user.password_hash);
      break;
    case 'argon2': valid = await verifyArgon2(password, user.password_hash); break;
  }

  if (!valid) return callback(null, null);
  return callback(null, { user_id: user.id, email: user.email });
};

Ce code offre un contrôle multi-algorithme pour comparer les mots de passe entrants selon le hash stocké lors de la migration.

---

Rollout : coupure du système legacy

Cet exemple de planning illustre comment organiser la transition progressive vers Auth0 pour garantir l’absence de coupure d’accès.

Semaine 1-8  : Migration lazy active
               Tous les logins passent par Custom DB Connection (auth legacy)
               À chaque login réussi → compte Auth0 créé avec MDP bcrypt

Semaine 9    : Vérifier le % de comptes migrés
               > 80% actifs migrés → continuer
               < 80% → continuer la migration lazy

Semaine 10   : Migrer les comptes restants en batch
               Reset de mot de passe pour les comptes non actifs

Semaine 11   : Basculer la connexion de Custom DB vers Database Native
               (les mots de passe sont maintenant stockés dans Auth0)

Semaine 12   : Désactiver le système legacy
               Garder le système en lecture seule 30 jours (rollback)

Ce déroulé progressif permet de migrer 100% des comptes tout en offrant une période de fallback en cas d’incident.

---

KPIs de migration à suivre

Mesurez l’avancée de votre migration grâce à ces requêtes SQL qui suivent le nombre de profils basculés quotidiennement.

-- Suivi quotidien de la migration
SELECT
  DATE(created_at) as migration_date,
  COUNT(*) as migrated_that_day,
  SUM(COUNT(*)) OVER (ORDER BY DATE(created_at)) as total_migrated,
  (SELECT COUNT(*) FROM legacy_users) as total_to_migrate,
  ROUND(100.0 * SUM(COUNT(*)) OVER (ORDER BY DATE(created_at)) /
    (SELECT COUNT(*) FROM legacy_users), 1) as progress_pct
FROM auth0_users  -- Table de suivi de migration
WHERE app_metadata->>'migrated_profile' = 'true'
GROUP BY 1
ORDER BY 1;

Ce rapport permet d’obtenir un taux de progression clair pour piloter et ajuster la migration.

---

> 🔄 Migration IdP legacy : nous avons migré des systèmes d'authentification de toutes technologies (PHP, .NET, Java, Ruby, Python) vers Auth0 avec zéro perte d'accès.

📞 +33 1 23 45 67 89 · 📧 [email protected]


Autres articles