Passer au contenu principal
Le script Login implémente la fonction exécutée chaque fois qu’un utilisateur doit s’authentifier. Nous vous recommandons de nommer cette fonction login. Ce script est requis à la fois pour l’authentification héritée et pour la migration automatique. Si la migration automatique est configurée pour la connexion, le processus de migration se déclenche après la première connexion réussie de l’utilisateur. Auth0 recommande de définir un user_id permanent dans le profil utilisateur renvoyé afin d’éviter la création d’utilisateurs en double.

Fonction login

La fonction login doit :
  • Envoyer les informations d’identification fournies de l’utilisateur à l’API de la base de données externe.
  • Retourner les données de profil de l’utilisateur si l’authentification réussit.
  • Retourner une erreur si l’authentification échoue.

Définition

La fonction login accepte trois paramètres et renvoie une fonction de rappel :
login(userNameOrEmail, password, callback): function
ParamètreTypeDescription
userNameOrEmailStringLe nom d’utilisateur ou le courriel de l’utilisateur.
passwordStringLe mot de passe de l’utilisateur en texte brut.
callbackFunctionPermet de transmettre des erreurs ou des données de profil dans le pipeline.

Exemple

Voici un exemple en pseudo-JavaScript montrant comment vous pourriez implémenter la fonction login. Pour des exemples propres à chaque langage, consultez Exemples de scripts propres à chaque langage.
function login(userNameOrEmail, password, callback) {
  // Envoyer les identifiants à l'API de base de données externe
  let hashedPassword = hash(password);

  let options = {
    url: "https://example.com/api/authenticate",
    body: {
      email: userNameOrEmail,
      password: hashedPassword
    }
  };

  send(options, (err, profileData) => {
    // Retourner une erreur dans le rappel si l'authentification échoue
    if (err) {
      return callback(new WrongUsernameOrPasswordError(userNameOrEmail, "My custom error message."));
    } else {
      // Retourner les données du profil dans le rappel si l'authentification réussit
      let profile = {
        username: profileData.username,
        email: profileData.emailAddress,
        user_id: profileData.userId
      };

      return callback(null, profile);
    }
  });
}

Chiffrement

Évitez de journaliser, de stocker ou de transmettre le mot de passe sous une forme non chiffrée, où que ce soit.
Chiffrez la valeur password à l’aide d’une bibliothèque de hachage cryptographique telle que bcrypt afin d’éviter toute fuite potentielle de données.

Exemple

bcrypt.hash(password, 10, function (err, hash) {
    if (err) {
        return callback(err);
    } else {
        // Retourner le mot de passe haché
    }
});

Fonction de rappel

La fonction callback sert à transmettre les données de profil de l’utilisateur ou les données d’erreur au sein du pipeline.

Définition

La fonction callback accepte jusqu’à deux paramètres et renvoie une fonction :
callback(error[, profile]): function
ParamètreTypeObligatoireDescription
errorObjectObligatoireContient les données d’erreur.
profileObjectFacultatifContient les données du profil de l’utilisateur.

Retourner le profil de l’utilisateur

Les données de profil renvoyées par le script Login pour un utilisateur doivent être cohérentes avec celles renvoyées par le script Get User.
Si l’utilisateur s’authentifie avec succès, ses données de profil doivent être renvoyées dans l’objet profile sous forme normalisée. En plus des champs standard, vous pouvez inclure les champs user_metadata, app_metadata et mfa_factors.

Exemple

return callback(null, {
    username: "username",
    user_id: "my-custom-db|username@domain.com",
    email: "username@domain.com",
    email_verified: false,
    user_metadata: {
        language: "en"
    },
    app_metadata: {
        plan: "full"
    },
    mfa_factors: [
      {
        phone: {
          value: "+15551234567"
        }
      },
    ]
});

Renvoyer une erreur

Si une erreur se produit, le paramètre error doit contenir les renseignements pertinents sur le problème survenu.

Objet du type WrongUsernameOrPasswordError

L’objet de type d’erreur personnalisée WrongUsernameOrPasswordError vous permet de transmettre des données qui seront affichées dans les journaux du locataire.
Constructeur
Le constructeur WrongUsernameOrPasswordError accepte jusqu’à deux paramètres :
new WrongUsernameOrPasswordError(userNameOrEmail[, message]): WrongUsernameOrPasswordError
ParamètreTypeObligatoireDescription
userNameOrEmailStringObligatoireContient le nom d’utilisateur ou le courriel de l’utilisateur, ou la valeur null.
messageStringFacultatifContient de l’information sur l’erreur.

Retourner une erreur avec un nom d’utilisateur ou un courriel

Si vous retournez une erreur contenant une valeur pour le champ userNameOrEmail, Auth0 consignera un événement de journal du locataire fp.
Exemple
return callback(new WrongUsernameOrPasswordError(userNameOrEmail, "My custom error message"));
champ de l’événement du journal du locataireValeur
Codefp
ÉvénementÉchec de connexion (mot de passe erroné)
DescriptionMy custom error message

Retourner une erreur sans nom d’utilisateur ni courriel

Si vous retournez une erreur avec une valeur null pour le champ userNameOrEmail, Auth0 consignera un événement de journal du locataire fu.
Exemple
return callback(new WrongUsernameOrPasswordError(null, "My custom error message"));
Champ de l’événement du journal du locataireValeur
Codefu
ÉvénementÉchec de connexion (courriel/nom d’utilisateur invalide)
DescriptionMon message d’erreur personnalisé

Synchroniser les attributs du profil de l’utilisateur à chaque connexion

Activez le paramètre Sync user profile attributes at each login si vous voulez qu’Auth0 mette à jour les champs name, nickname, given_name, family_name et/ou picture avec les valeurs renvoyées par la base de données externe à chaque connexion. Si vous n’activez pas ce paramètre, les valeurs renvoyées par la base de données externe lors de la première connexion de l’utilisateur seront conservées aux connexions suivantes, même si elles ont changé dans la base de données externe.

Exemples de scripts par langage

Auth0 fournit des scripts d’exemple pour les langages/technologies suivants :

JavaScript

function login(email, password, callback) {
  // Ce script doit authentifier un utilisateur à partir des informations d'identification stockées dans
  // votre base de données.
  // Il est exécuté lorsqu'un utilisateur tente de se connecter ou immédiatement après l'inscription
  // (à titre de vérification que l'utilisateur s'est bien inscrit).
  //
  // Tout ce que ce script retourne sera défini dans le profil de l'utilisateur
  // et sera visible par tous les administrateurs du locataire. Évitez d'ajouter des attributs
  // avec des valeurs telles que des mots de passe, des clés, des secrets, etc.
  //
  // Le paramètre `password` de cette fonction est en texte brut. Il doit être
  // haché/salé pour correspondre à ce qui est stocké dans votre base de données. Par exemple :
  //
  //     var bcrypt = require('bcrypt@0.8.5');
  //     bcrypt.compare(password, dbPasswordHash, function(err, res)) { ... }
  //
  // Ce script peut se terminer de trois façons :
  // 1. Les informations d'identification de l'utilisateur sont valides. Le profil utilisateur retourné doit être au
  // format suivant : https://auth0.com/docs/users/normalized/auth0/normalized-user-profile-schema
  //     var profile = {
  //       user_id: ..., // user_id est obligatoire
  //       email: ...,
  //       [...]
  //     };
  //     callback(null, profile);
  // 2. Les informations d'identification de l'utilisateur sont invalides
  //     callback(new WrongUsernameOrPasswordError(email, "my error message"));
  // 3. Une erreur s'est produite lors de la tentative d'accès à votre base de données
  //     callback(new Error("my error message"));
  //
  // Une liste des modules Node.js pouvant être référencés est disponible ici :
  //
  //    https://tehsis.github.io/webtaskio-canirequire/
  const msg = 'Please implement the Login script for this database connection ' +
    'at https://manage.auth0.com/#/connections/database';
  return callback(new Error(msg));
}

ASP.NET Membership Provider (MVC3 - Universal Providers)

function login(email, password, callback) {
  const crypto = require('crypto');
  const sqlserver = require('tedious@11.0.3');
  const Connection = sqlserver.Connection;
  const Request = sqlserver.Request;
  const TYPES = sqlserver.TYPES;
  const connection = new Connection({
    userName: 'the username',
    password: 'the password',
    server: 'the server',
    options: {
      database: 'the db name',
      encrypt: true // for Windows Azure
    }
  });
  /**
   * hashPassword
   *
   * This function creates a hashed version of the password to store in the database.
   *
   * @password  {[string]}      the password entered by the user
   * @return    {[string]}      the hashed password
   */
  function hashPassword(password, salt) {
    // the default implementation uses HMACSHA256 and since Key length is 64
    // and default salt is 16 bytes, Membership will fill the buffer repeating the salt
    const key = Buffer.concat([salt, salt, salt, salt]);
    const hmac = crypto.createHmac('sha256', key);
    hmac.update(Buffer.from(password, 'ucs2'));
    return hmac.digest('base64');
  }
  connection.on('debug', function(text) {
    // if you have connection issues, uncomment this to get more detailed info
    //console.log(text);
  }).on('errorMessage', function(text) {
    // this will show any errors when connecting to the SQL database or with the SQL statements
    console.log(JSON.stringify(text));
  });
  connection.on('connect', function(err) {
    if (err) return callback(err);
    getMembershipUser(email, function(err, user) {
      if (err || !user || !user.profile || !user.password) return callback(err || new WrongUsernameOrPasswordError(email));
      const salt = Buffer.from(user.password.salt, 'base64');
      if (hashPassword(password, salt).toString('base64') !== user.password.password) {
        return callback(new WrongUsernameOrPasswordError(email));
      }
      callback(null, user.profile);
    });
  });

  // Membership Provider implementation used on Microsoft.AspNet.Providers NuGet
  /**
   * getMembershipUser
   *
   * Cette fonction reçoit un nom d'utilisateur ou un courriel et retourne les informations
   * du fournisseur Membership de l'utilisateur, les hachages de mot de passe et le salt
   *
   * @usernameOrEmail  {[string]}       le nom d'utilisateur ou le courriel, la méthode effectuera une
   *                                    requête sur les deux avec un OR
   *
   * @callback         {[Function]}     le premier argument sera l'erreur le cas échéant,
   *                                    et le second argument sera un objet utilisateur
   */
  function getMembershipUser(usernameOrEmail, done) {
    var user = null;
    const query =
      'SELECT Memberships.UserId, Email, Users.UserName, Password ' +
      'FROM Memberships INNER JOIN Users ' +
      'ON Users.UserId = Memberships.UserId ' +
      'WHERE Memberships.Email = @Username OR Users.UserName = @Username';
    const getMembershipQuery = new Request(query, function(err, rowCount) {
      if (err || rowCount < 1) return done(err);
      done(err, user);
    });
    getMembershipQuery.addParameter('Username', TYPES.VarChar, usernameOrEmail);
    getMembershipQuery.on('row', function(fields) {
      user = {
        profile: {
          user_id: fields.UserId.value,
          nickname: fields.UserName.value,
          email: fields.Email.value,
        },
        password: {
          password: fields.Password.value,
          salt: fields.PasswordSalt.value
        }
      };
    });
    connection.execSql(getMembershipQuery);
  }
}

ASP.NET Membership Provider (MVC4 - Simple Membership)

function login(email, password, callback) {
  const crypto = require('crypto');
  const sqlserver = require('tedious@11.0.3');
  const Connection = sqlserver.Connection;
  const Request = sqlserver.Request;
  const TYPES = sqlserver.TYPES;
  const connection = new Connection({
    userName: 'the username',
    password: 'the password',
    server: 'the server',
    options: {
      database: 'the db name',
      encrypt: true // for Windows Azure
    }
  });
  function fixedTimeComparison(a, b) {
    var mismatch = (a.length === b.length ? 0 : 1);
    if (mismatch) {
      b = a;
    }
    for (var i = 0, il = a.length; i < il; ++i) {
      const ac = a.charCodeAt(i);
      const bc = b.charCodeAt(i);
      mismatch += (ac === bc ? 0 : 1);
    }
    return (mismatch === 0);
  }

  /**
   * validatePassword
   *
   * Cette fonction récupère le mot de passe saisi par l'utilisateur, ainsi que le hachage
   * et le sel du mot de passe original provenant de la base de données, et effectue un hachage HMAC SHA256.
   *
   * @password      {[string]}      le mot de passe saisi par l'utilisateur
   * @originalHash  {[string]}      le hachage du mot de passe original provenant de la base de données
   *                                (incluant le sel).
   * @return        {[bool]}        true si le mot de passe est valide
   */
  function validatePassword(password, originalHash, callback) {
    const iterations = 1000;
    const hashBytes = Buffer.from(originalHash, 'base64');
    const salt = hashBytes.slice(1, 17);
    const hash = hashBytes.slice(17, 49);
    crypto.pbkdf2(password, salt, iterations, hash.length, 'sha1', function(err, hashed) {
      if (err) return callback(err);
      const hashedBase64 = Buffer.from(hashed, 'binary').toString('base64');
      const isValid = fixedTimeComparison(hash.toString('base64'), hashedBase64);
      return callback(null, isValid);
    });
  }

  connection.on('debug', function(text) {
    // if you have connection issues, uncomment this to get more detailed info
    //console.log(text);
  }).on('errorMessage', function(text) {
    // this will show any errors when connecting to the SQL database or with the SQL statements
    console.log(JSON.stringify(text));
  });
  connection.on('connect', function(err) {
    if (err) return callback(err);
    getMembershipUser(email, function(err, user) {
      if (err || !user || !user.profile) return callback(err || new WrongUsernameOrPasswordError(email));
      validatePassword(password, user.password, function(err, isValid) {
        if (err || !isValid) return callback(err || new WrongUsernameOrPasswordError(email));
        callback(null, user.profile);
      });
    });
  });

  // Membership Provider implementation used on Microsoft.AspNet.Providers NuGet
  /**
   * getMembershipUser
   *
   * This function gets a username or email and returns a user info, password hashes and salt
   *
   * @usernameOrEamil   {[string]}    the username or email, the method will do a query
   *                                  on both with an OR
   * @callback          {[Function]}  first argument will be the Error if any, and second
   *                                  argument will be a user object
   */
  function getMembershipUser(usernameOrEmail, done) {
    var user = null;
    const query =
      'SELECT webpages_Membership.UserId, UserName, UserProfile.UserName, Password from webpages_Membership ' +
      'INNER JOIN UserProfile ON UserProfile.UserId = webpages_Membership.UserId ' +
      'WHERE UserProfile.UserName = @Username';
    const getMembershipQuery = new Request(query, function(err, rowCount) {
      if (err || rowCount < 1) return done(err);
      done(err, user);
    });
    getMembershipQuery.addParameter('Username', TYPES.VarChar, usernameOrEmail);
    getMembershipQuery.on('row', function(fields) {
      user = {
        profile: {
          user_id: fields.UserId.value,
          nickname: fields.UserName.value,
          email: fields.UserName.value,
        },
        password: fields.Password.value
      };
    });
    connection.execSql(getMembershipQuery);
  }
}

MongoDB

function login(email, password, callback) {
  const bcrypt = require('bcrypt');
  const MongoClient = require('mongodb@3.1.4').MongoClient;
  const client = new MongoClient('mongodb://user:pass@mymongoserver.com');
  client.connect(function (err) {
    if (err) return callback(err);
    const db = client.db('db-name');
    const users = db.collection('users');
    users.findOne({ email: email }, function (err, user) {
      if (err || !user) {
        client.close();
        return callback(err || new WrongUsernameOrPasswordError(email));
      }
      bcrypt.compare(password, user.password, function (err, isValid) {
        client.close();
        if (err || !isValid) return callback(err || new WrongUsernameOrPasswordError(email));
        return callback(null, {
            user_id: user._id.toString(),
            nickname: user.nickname,
            email: user.email
          });
      });
    });
  });
}

MySQL

function login(email, password, callback) {
  const mysql = require('mysql');
  const bcrypt = require('bcrypt');
  const connection = mysql({
    host: 'localhost',
    user: 'me',
    password: 'secret',
    database: 'mydb'
  });
  connection.connect();
  const query = 'SELECT id, nickname, email, password FROM users WHERE email = ?';
  connection.query(query, [ email ], function(err, results) {
    if (err) return callback(err);
    if (results.length === 0) return callback(new WrongUsernameOrPasswordError(email));
    const user = results[0];
    bcrypt.compare(password, user.password, function(err, isValid) {
      if (err || !isValid) return callback(err || new WrongUsernameOrPasswordError(email));
      callback(null, {
        user_id: user.id.toString(),
        nickname: user.nickname,
        email: user.email
      });
    });
  });
}

PostgreSQL

function login(email, password, callback) {
  //cet exemple utilise la bibliothèque "pg"
  //plus d'informations ici : https://github.com/brianc/node-postgres
  const bcrypt = require('bcrypt');
  const postgres = require('pg');
  const conString = 'postgres://user:pass@localhost/mydb';
  postgres.connect(conString, function (err, client, done) {
    if (err) return callback(err);
    const query = 'SELECT id, nickname, email, password FROM users WHERE email = $1';
    client.query(query, [email], function (err, result) {
      // REMARQUE : appelez toujours `done()` ici pour fermer
      // la connexion à la base de données
      done();
      if (err || result.rows.length === 0) return callback(err || new WrongUsernameOrPasswordError(email));
      const user = result.rows[0];
      bcrypt.compare(password, user.password, function (err, isValid) {
        if (err || !isValid) return callback(err || new WrongUsernameOrPasswordError(email));
        return callback(null, {
          user_id: user.id,
          nickname: user.nickname,
          email: user.email
        });
      });
    });
  });
}

SQL Server

function login(email, password, callback) {
  //cet exemple utilise la bibliothèque "tedious"
  //plus d'info ici : http://pekim.github.io/tedious/index.html
  const bcrypt = require('bcrypt');
  const sqlserver = require('tedious@11.0.3');
  const Connection = sqlserver.Connection;
  const Request = sqlserver.Request;
  const TYPES = sqlserver.TYPES;
  const connection = new Connection({
    userName:  'test',
    password:  'test',
    server:    'localhost',
    options:  {
      database: 'mydb',
      rowCollectionOnRequestCompletion: true
    }
  });
  const query = 'SELECT Id, Nickname, Email, Password FROM dbo.Users WHERE Email = @Email';
  connection.on('debug', function (text) {
    console.log(text);
  }).on('errorMessage', function (text) {
    console.log(JSON.stringify(text, null, 2));
  }).on('infoMessage', function (text) {
    console.log(JSON.stringify(text, null, 2));
  });
  connection.on('connect', function (err) {
    if (err) return callback(err);
    const request = new Request(query, function (err, rowCount, rows) {
      if (err || rowCount < 1) return callback(err || new WrongUsernameOrPasswordError(email));
      bcrypt.compare(password, rows[0][3].value, function (err, isValid) {
        if (err || !isValid) return callback(err || new WrongUsernameOrPasswordError(email));
        callback(null, {
          user_id: rows[0][0].value,
          nickname: rows[0][1].value,
          email: rows[0][2].value
        });
      });
    });
    request.addParameter('Email', TYPES.VarChar, email);
    connection.execSql(request);
  });
}

Windows Azure SQL Database

function login(email, password, callback) {
  //cet exemple utilise la bibliothèque "tedious"
  //plus d'informations ici : http://pekim.github.io/tedious/index.html
  var Connection = require('tedious@11.0.3').Connection;
  var Request = require('tedious@11.0.3').Request;
  var TYPES = require('tedious@11.0.3').TYPES;
  var bcrypt = require('bcrypt');
  var connection = new Connection({
    userName: 'your-user@your-server-id.database.windows.net',
    password: 'the-password',
    server: 'your-server-id.database.windows.net',
    options: {
      database: 'mydb',
      encrypt: true,
      rowCollectionOnRequestCompletion: true
    }
  });
  var query = "SELECT Id, Email, Password " +
    "FROM dbo.Users WHERE Email = @Email";
  connection.on('debug', function (text) {
    // Décommentez la ligne suivante pour activer les messages de débogage
    // console.log(text);
  }).on('errorMessage', function (text) {
    console.log(JSON.stringify(text, null, 2));
    return callback(text);
  }).on('infoMessage', function (text) {
    // Décommentez la ligne suivante pour activer les messages d'information
    // console.log(JSON.stringify(text, null, 2));
  });
  connection.on('connect', function (err) {
    if (err) { return callback(err); }
    var request = new Request(query, function (err, rowCount, rows) {
      if (err) {
        callback(new Error(err));
      } else if (rowCount < 1) {
        callback(new WrongUsernameOrPasswordError(email));
      } else {
        bcrypt.compare(password, rows[0][2].value, function (err, isValid) {
          if (err) { callback(new Error(err)); }
          else if (!isValid) { callback(new WrongUsernameOrPasswordError(email)); }
          else {
            callback(null, {
              user_id: rows[0][0].value,
              email: rows[0][1].value
            });
          }
        });
      }
    });
    request.addParameter('Email', TYPES.VarChar, email);
    connection.execSql(request);
  });
}

Axios

async function loginAsync(email, password, callback) {
  //à mettre à jour lorsque de nouvelles versions d'axios sont disponibles (https://auth0-extensions.github.io/canirequire/#axios)
  const axios = require("axios@0.22.0");

  let response;

  try {
    response = await axios.post(
      //stocker l'URL de l'API dans les paramètres de connexion pour mieux prendre en charge les environnements SDLC
      configuration.baseAPIUrl + "/login",
      //identifiants de l'utilisateur transmis dans le corps de la requête
      {
        email: email,
        password: password,
      },
      {
        timeout: 10000, //terminer l'appel proprement si la requête expire afin que le script puisse effectuer le rappel nécessaire
        headers: {
          //sécurisation de l'appel API avec l'apiKey stockée dans les paramètres de connexion.
          //approche simple et rapide, mais l'utilisation de jetons M2M est plus sécurisée car
          // un secret ne doit pas être partagé entre l'application et l'API.
          "x-api-key": configuration.apiKey,
        },
      }
    );
  } catch (e) {
    if (e.response.status === 404) {
      //supposant que l'API retourne 404 lorsque le courriel/nom d'utilisateur/mot de passe est invalide
      return callback(
        new WrongUsernameOrPasswordError(email, "Invalid credentials provided.")
      );
    }
    //rappel pour tout autre type d'erreur
    return callback(new Error(e.message));
  }

  try {
    let user = response.data;

    //si vous utilisez plusieurs connexions de base de données personnalisées dans votre locataire, préfixez
    //l'user_id avec une clé propre à la connexion, ex. : "connName|" + user.user_id
    //cela garantit des identifiants d'utilisateur uniques pour toutes les connexions de base de données
    return callback(null, {
      user_id: user.user_id,
      email: user.email,
    });
  } catch (e) {
    return callback(new Error(e.message));
  }
}

Stormpath

function login(username, password, callback) {
  // Remplacez {yourStormpathClientId} par votre ID Stormpath
  var url = 'https://api.stormpath.com/v1/applications/{yourStormpathClientId}/loginAttempts';
  // Ajoutez votre ID client et votre secret d'API Stormpath
  var apiCredentials = {
    user : '{yourStormpathApiId}',
    password: '{yourStormpathApiSecret}'
  };
  // Stormpath exige que les informations d'identification de l'utilisateur soient transmises sous forme de message encodé en base64
  var credentials = Buffer.from(username + ':' + password).toString('base64');
  // Effectuez une requête POST pour authentifier un utilisateur
  request({
    url: url,
    method: 'POST',
    auth: apiCredentials,
    json: {
      type: 'basic',
      // Transmission des informations d'identification encodées en base64
      value: credentials
    }
  }, function (error, response, body) {
    // Si la réponse est réussie, on continue
    if (response.statusCode !== 200) return callback();
    // Une réponse réussie retourne une URL pour récupérer les informations de l'utilisateur
    var accountUrl = body.account.href;
    // Effectuez une deuxième requête pour récupérer les informations de l'utilisateur.
    request({
      url: accountUrl,
      auth: apiCredentials,
      json: true
    }, function (errorUserInfo, responseUserInfo, bodyUserInfo) {
      // Si la réponse est réussie, on la traite
      if (responseUserInfo.statusCode !== 200) return callback();
      // Pour obtenir l'identifiant de l'utilisateur, on retire l'URL de l'API Stormpath
      var id = bodyUserInfo.href.replace('https://api.stormpath.com/v1/accounts/', '');
      // Enfin, on définit les données à stocker dans Auth0 et on migre l'utilisateur
      return callback(null, {
        user_id : id,
        username: bodyUserInfo.username,
        email: bodyUserInfo.email,
        // On définit email_verified à true en supposant que si l'utilisateur était valide
        // dans Stormpath, il a déjà vérifié son courriel
        // Si ce champ n'est pas défini, l'utilisateur recevra un courriel lui demandant de vérifier
        // son compte. Vous pouvez décider comment gérer cela selon votre cas d'utilisation
        email_verified: true
        // Ajoutez tout champ supplémentaire que vous souhaitez transférer depuis Stormpath
      });
    });
  });
}

Pour en savoir plus