Saltar al contenido principal
El script Change Password implementa la función definida para cambiar la contraseña del usuario especificado en la base de datos externa. Recomendamos llamar a esta función changePassword. Este script solo se usa en un escenario de autenticación heredado y es obligatorio si desea cambiar la contraseña de un usuario en la base de datos externa. Se ejecutará cuando un usuario realice un flujo de restablecimiento de contraseña o cuando se inicie un flujo de cambio de contraseña desde el o la Auth0 .

Función ChangePassword

La función changePassword debe:
  • Actualizar la contraseña del usuario en la base de datos externa.
  • Devolver true (o un objeto que contenga la propiedad last_password_reset) si la operación de cambio de contraseña se completó correctamente. Si la propiedad last_password_reset está presente en el objeto, se actualizará en el perfil del usuario.
  • Devolver false si la operación de cambio de contraseña falla.
  • Devolver un error si no se puede acceder a la base de datos externa.

Definición

La función changePassword acepta tres parámetros y devuelve una función de devolución de llamada (callback):
changePassword(email, newPassword, callback): function
ParámetroTipoDescripción
emailStringDirección de correo electrónico del usuario en Auth0 y en la base de datos externa.
newPasswordStringValor que se establecerá como la nueva contraseña del usuario en la base de datos externa. Este valor se envía a la función en texto sin formato y debe cifrarse antes de enviarse a la base de datos externa.
callbackFunctionSe utiliza para pasar datos o el resultado de la operación a través del flujo.

Ejemplo

Este es un ejemplo en seudojavaScript de cómo podría implementar la función changePassword. Para ver ejemplos específicos de cada lenguaje, consulte Ejemplos de scripts específicos de cada lenguaje.
function changePassword(email, newPassword, callback) {
  // Aplicar hash a la contraseña proporcionada 
  let hashedPassword = hash(newPassword);

  // Preparar la llamada a la API
  let options = {
    url: "https://example.com/api/users",
    body: {
      email: email,
      password: hashedPassword
    }
  };

  // Llamar a la API
  send(options, err => {
    if (err && err.id == "FAIL_CHANGE_PASSWORD") {
      // Devolver false en el callback si el cambio de contraseña falló
      return callback(null, false);
    } else if (err) {
      // Devolver error en el callback si ocurrió otro error
      return callback(new Error("My custom error message.");
    } else {
      // Devolver true en el callback si la operación de cambio de contraseña se realizó correctamente
      return callback(null, true);

      // O devolver un objeto que contenga la propiedad `last_password_reset` 
      // si la operación de cambio de contraseña se realizó correctamente.
      // Si la propiedad `last_password_reset` está presente en el objeto,
      // se actualizará en el perfil del usuario.
      return callback(null, { "last_password_reset": Date.now() });
    }
  });
}

Cifrado

Evite registrar, almacenar o transportar la credencial de contraseña en cualquier lugar en su forma sin cifrar.
Para evitar cualquier posible filtración de datos, cifre el valor de la contraseña mediante una biblioteca de hash criptográfico como bcrypt.

Ejemplo

bcrypt.hash(password, 10, function (err, hash) {
    if (err) {
        return callback(err);
    } else {
        // Devolver contraseña hasheada
    }
});

Función callback

La función callback acepta dos parámetros y se utiliza para pasar datos de error o indicar el resultado de la operación.

Definición

callback(error, operationResult | resultObj): function
ParámetroTipoObligatorioDescripción
errorObjectObligatorioContiene datos del error.
operationResultBooleanOpcionalIndica el resultado de la operación de cambio de contraseña.
resultObjObjectOpcionalIndica que la operación de cambio de contraseña se completó correctamente. Si la propiedad last_password_reset está presente, se actualizará en el perfil del usuario.

Devolver un resultado exitoso

Si la operación de cambio de contraseña se realizó correctamente, devuelve la función callback y pasa el valor null en el parámetro error y el valor true en el parámetro operationResult.

Ejemplo

return callback(null, true);

Devolver un resultado correcto y actualizar el atributo last_password_reset

Si la operación de cambio de contraseña se completó correctamente, devuelve la función callback y pasa un valor null como parámetro error y un objeto como parámetro profile. Si el atributo last_password_reset se incluye en el objeto, se actualizará en el perfil del usuario.

Ejemplo

return callback(null, { "last_password_reset": Date.now() });

Devolver un fallo

Si la operación de cambio de contraseña falla, devuelva la función callback y pase el valor null como parámetro error y el valor false como parámetro operationResult.

Ejemplo

return callback(null, false);

Devolver un error

Si se produce un error, devuelva la función callback y pase la información pertinente en el parámetro error.

Ejemplo

return callback(new Error("My custom error message."));

Ejemplos de scripts específicos para cada lenguaje

Auth0 proporciona scripts de ejemplo para los siguientes lenguajes/tecnologías:

JavaScript

function changePassword(email, newPassword, callback) {
  // Este script debe cambiar la contraseña almacenada para el usuario actual en
  // su base de datos. Se ejecuta cuando el usuario hace clic en el enlace de confirmación
  // después de una solicitud de restablecimiento de contraseña.
  // El contenido y el comportamiento de los correos electrónicos de confirmación de contraseña se pueden personalizar
  // aquí: https://manage.auth0.com/#/emails
  // El parámetro `newPassword` de esta función está en texto plano. Debe ser
  // hasheado/salteado para coincidir con lo que está almacenado en su base de datos.
  //
  // Hay tres maneras en que este script puede finalizar:
  // 1. La contraseña del usuario se actualizó correctamente:
  //     callback(null, true);
  // 2. La contraseña del usuario no se actualizó:
  //     callback(null, false);
  // 3. Algo salió mal al intentar acceder a su base de datos:
  //     callback(new Error("my error message"));
  //
  // Si se devuelve un error, se pasará en la cadena de consulta de la página
  // a la que se redirige al usuario tras hacer clic en el enlace de confirmación.
  // Por ejemplo, devolver `callback(new Error("error"))` y redirigir a
  // https://example.com redirigirá a la siguiente URL:
  //     https://example.com?email=alice%40example.com&message=error&success=false
  const msg = 'Please implement the Change Password script for this database ' +
    'connection at https://manage.auth0.com/#/connections/database';
  return callback(new Error(msg));
}

Proveedor de membresía de ASP.NET (MVC3 - Proveedores universales)

function changePassword(email, newPassword, callback) {
  var crypto = require('crypto');
  var Connection = require('tedious').Connection;
  var Request = require('tedious').Request;
  var TYPES = require('tedious').TYPES
  var connection = new Connection({
    userName:  'the username',
    password:  'the password',
    server:    'the server',
    options: {
      database:  'the db name',
      // encrypt: true   habilitar esto para Windows Azure
    }
  });
  /**
   * hashPassword
   *
   * Esta función crea una versión cifrada de la contraseña para almacenar en la base de datos.
   *
   * @password  {[string]}      la contraseña ingresada por el usuario
   * @return    {[string]}      la contraseña cifrada
   */
  function hashPassword(password, salt) {
    // la implementación predeterminada usa HMACSHA256 y dado que la longitud de la clave es 64
    // y el salt predeterminado es de 16 bytes, Membership llenará el búfer repitiendo el salt
    var key = Buffer.concat([salt, salt, salt, salt]);
    var hmac = crypto.createHmac('sha256', key);
    hmac.update(Buffer.from(password, 'ucs2'));
    var hashed = hmac.digest('base64');
    return hashed;
  }
  connection.on('debug', function(text) {
      // si tiene problemas de conexión, descomente esto para obtener información más detallada
      //console.log(text);
  }).on('errorMessage', function(text) {
      // esto mostrará cualquier error al conectarse a la base de datos SQL o con las sentencias SQL
    console.log(JSON.stringify(text));
  });
  connection.on('connect', function (err) {
    if (err) {
      return callback(err);
    }
    updateMembershipUser(email, newPassword, function(err, wasUpdated) {
      if (err) {
        return callback(err); // esto devolverá un 500
      }
      callback(null, wasUpdated);
    });
  });
  function updateMembershipUser(email, newPassword, callback) {
    var salt            = crypto.randomBytes(16);
    var hashedPassword  = hashPassword(newPassword, salt);
    var updateMembership =
      'UPDATE Memberships '+
      'SET Password=@NewPassword, PasswordSalt=@NewSalt, LastPasswordChangedDate=GETDATE() '+
      'WHERE Email=@Email';
    var updateMembershipQuery = new Request(updateMembership, function (membershipErr, membershipCount) {
      if (membershipErr) {
        return callback(membershipErr);
      }
      callback(null, membershipCount > 0);
    });
    updateMembershipQuery.addParameter('NewPassword', TYPES.VarChar, hashedPassword);
    updateMembershipQuery.addParameter('NewSalt',     TYPES.VarChar, salt.toString('base64'));
    updateMembershipQuery.addParameter('Email',       TYPES.VarChar, email);
    connection.execSql(updateMembershipQuery);
  }
}

Proveedor de membresía de ASP.NET (MVC4 - Membresía simple)

function changePassword(email, newPassword, callback) {
  var crypto = require('crypto');
  var Connection = require('tedious').Connection;
  var Request = require('tedious').Request;
  var TYPES = require('tedious').TYPES
  var connection = new Connection({
    userName:  'the username',
    password:  'the password',
    server:    'the server',
    options: {
      database:  'the db name',
      // encrypt: true para Windows Azure, habilitar esto
    }
  });
  /**
   * hashPassword
   *
   * Esta función genera un hash de una contraseña usando el algoritmo HMAC SHA256.
   *
   * @password    {[string]}    contraseña a hashear
   * @salt        {[string]}    salt a utilizar en el proceso de hash
   * @callback    {[function]}  callback a invocar después de hashear la contraseña
   */
  function hashPassword(password, salt, callback) {
    var iterations         = 1000;
    var passwordHashLength = 32;
    crypto.pbkdf2(password, salt, iterations, passwordHashLength, function (err, hashed) {
      if (err) {
        return callback(err);
      }
      var result = Buffer.concat([Buffer.from([0], 1), salt, Buffer.from(hashed, 'binary')]);
      var resultBase64 = result.toString('base64');
      callback(null, resultBase64);
    });
  }
  connection.on('debug', function(text) {
      // si tiene problemas de conexión, descomente esto para obtener información más detallada
      //console.log(text);
  }).on('errorMessage', function(text) {
      // esto mostrará cualquier error al conectarse a la base de datos SQL o con las sentencias SQL
    console.log(JSON.stringify(text));
  });
  connection.on('connect', function (err) {
    if (err) {
      return callback(err);
    }
    updateMembershipUser(email, newPassword, function(err, wasUpdated) {
      if (err) {
        return callback(err); // esto devolverá un 500
      }
      callback(null, wasUpdated);
    });
  });
  function findUserId(email, callback) {
    var findUserIdFromEmail =
      'SELECT UserProfile.UserId FROM ' +
      'UserProfile INNER JOIN webpages_Membership ' +
      'ON UserProfile.UserId = webpages_Membership.UserId ' +
      'WHERE UserName = @Email';
    var findUserIdFromEmailQuery = new Request(findUserIdFromEmail, function (err, rowCount, rows) {
      if (err) {
        return callback(err);
      }
      // No se encontró ningún registro con ese correo electrónico
      if (rowCount < 1) {
        return callback(null, null);
      }
      var userId = rows[0][0].value;
      callback(null, userId);
    });
    findUserIdFromEmailQuery.addParameter('Email', TYPES.VarChar, email);
    connection.execSql(findUserIdFromEmailQuery);
  }
  function updateMembershipUser(email, newPassword, callback) {
    findUserId(email, function (err, userId) {
      if (err) {
        return callback(err);
      }
      if (userId === null) {
        return callback();
      }
      var salt = crypto.randomBytes(16);
      var updateMembership =
        'UPDATE webpages_Membership '+
        'SET Password=@NewPassword, PasswordChangedDate=GETDATE() '+
        'WHERE UserId=@UserId';
      var updateMembershipQuery = new Request(updateMembership, function (err, rowCount) {
        if (err) {
          return callback(err);
        }
        if (rowCount < 1) {
          return callback();
        }
        callback(null, rowCount > 0);
      });
      hashPassword(newPassword, salt, function (err, hashedPassword) {
        if (err) {
          return callback(err);
        }
        updateMembershipQuery.addParameter('NewPassword',   TYPES.VarChar, hashedPassword);
        updateMembershipQuery.addParameter('UserId',        TYPES.VarChar, userId);
        connection.execSql(updateMembershipQuery);
      });
    });
  }
}

MongoDB

function changePassword(email, newPassword, 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');
    bcrypt.hash(newPassword, 10, function (err, hash) {
      if (err) {
        client.close();
        return callback(err);
      }
      users.update({ email: email }, { $set: { password: hash } }, function (err, count) {
        client.close();
        if (err) return callback(err);
        callback(null, count > 0);
      });
    });
  });
}

MySQL

function changePassword(email, newPassword, callback) {
  const mysql = require('mysql');
  const bcrypt = require('bcrypt');
  const connection = mysql({
    host: 'localhost',
    user: 'me',
    password: 'secret',
    database: 'mydb'
  });
  connection.connect();
  const query = 'UPDATE users SET password = ? WHERE email = ?';
  bcrypt.hash(newPassword, 10, function(err, hash) {
    if (err) return callback(err);
    connection.query(query, [ hash, email ], function(err, results) {
      if (err) return callback(err);
      callback(null, results.length > 0);
    });
  });
}

PostgreSQL

function changePassword (email, newPassword, callback) {
  //este ejemplo usa la librería "pg"
  //más información aquí: 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);
    bcrypt.hash(newPassword, 10, function (err, hash) {
      if (err) return callback(err);
      const query = 'UPDATE users SET password = $1 WHERE email = $2';
      client.query(query, [hash, email], function (err, result) {
        // NOTA: siempre llama a `done()` aquí para cerrar
        // la conexión a la base de datos
        done();
        return callback(err, result && result.rowCount > 0);
      });
    });
  });
}

SQL Server

function changePassword (email, newPassword, callback) {
  //este ejemplo usa la biblioteca "tedious"
  //más información aquí: http://tediousjs.github.io/tedious/
  const bcrypt = require('bcrypt');
  const sqlserver = require('tedious@1.11.0');
  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'
    }
  });
  const query = 'UPDATE dbo.Users SET Password = @NewPassword 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, rows) {
      if (err) return callback(err);
      callback(null, rows > 0);
    });
    bcrypt.hash(newPassword, 10, function (err, hash) {
      if (err) return callback(err);
      request.addParameter('NewPassword', TYPES.VarChar, hash);
      request.addParameter('Email', TYPES.VarChar, email);
      connection.execSql(request);
    });
  });
}

Base de datos SQL de Azure para Windows

function changePassword (email, newPassword, callback) {
  //este ejemplo usa la librería "tedious"
  //más información aquí: http://pekim.github.io/tedious/index.html
  var Connection = require('tedious@1.11.0').Connection;
  var Request = require('tedious@1.11.0').Request;
  var TYPES = require('tedious@1.11.0').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
    }
  });
  var query = 'UPDATE dbo.Users SET Password = @NewPassword ' +
    'WHERE Email = @Email';
  connection.on('debug', function(text) {
    // Descomenta la siguiente línea para habilitar los mensajes de depuración
    // console.log(text);
  }).on('errorMessage', function(text) {
    console.log(JSON.stringify(text, null, 2));
  }).on('infoMessage', function(text) {
    // Descomenta la siguiente línea para habilitar los mensajes de información
    // console.log(JSON.stringify(text, null, 2));
  });
  connection.on('connect', function (err) {
    if (err) { return callback(err); }
    var request = new Request(query, function (err, rows) {
      if (err) { return callback(err); }
      console.log('rows: ' + rows);
      callback(null, rows > 0);
    });
    bcrypt.hash(newPassword, 10, function (err, hash) {
      if (err) { return callback(err); }
      request.addParameter('NewPassword', TYPES.VarChar, hash);
      request.addParameter('Email', TYPES.VarChar, email);
      connection.execSql(request);
    });
  });
}

Más información