パスワード変更スクリプトのテンプレート

パスワード変更スクリプトは、外部データベース内の指定されたユーザーパスワードを変更するために、定義された関数を実装します。この関数の名前はchangePasswordにすることをお勧めします。

このスクリプトはレガシーの認証シナリオでのみ使用され、外部データベースのユーザーパスワードを変更したい場合には必須です。ユーザーがパスワードリセットワークフローを実行するか、パスワード変更フローがAuth0 DashboardまたはAuth0 Management APIから開始されるときに実行されます。

ChangePassword関数

changePassword関数は以下を行います。

  • 外部データベースでユーザーパスワードを更新します。

  • パスワード変更操作が成功した場合、true(またはlast_password_resetプロパティを含むオブジェクト)を返します。オブジェクトにlast_password_resetプロパティがある場合は、ユーザーのプロファイルで更新されます。

  • パスワード変更操作が失敗した場合、falseを返します。

  • 外部データベースにリーチできなかった場合は、エラーを返します。

定義

changePassword関数は3つのパラメーターを受け取り、callback関数を返します。

changePassword(email, newPassword, callback): function

Was this helpful?

/

パラメーター タイプ 説明
email 文字列 Auth0および外部データベースにあるユーザーのメールアドレス。
newPassword 文字列 外部データベースでユーザーの新しいパスワードとして設定される値。この値はプレーンテキストとして関数に送信されます。外部データベースに送信される前に、暗号化する必要があります
callback 関数 パイプラインを通じてデータまたは操作結果データを渡すのに使用されます。

これは疑似JavaScriptを使った例で、どのようにすればchangePassword関数を実装できるかがわかります。言語固有の例については、「言語固有のスクリプトの例」をお読みください。

function changePassword(email, newPassword, callback) {
  // Hash the provided password 
  let hashedPassword = hash(newPassword);

  // Prepare the API call
  let options = {
    url: "https://example.com/api/users",
    body: {
      email: email,
      password: hashedPassword
    }
  };

  // Call the API
  send(options, err => {
    if (err && err.id == "FAIL_CHANGE_PASSWORD") {
      // Return false in callback if password change failed
      return callback(null, false);
    } else if (err) {
      // Return error in callback if other error occurred
      return callback(new Error("My custom error message.");
    } else {
      // Return true in callback if password change operation succeeded
      return callback(null, true);

      // Or return an object containing the `last_password_reset` property 
      // if the password change operation succeeded.
      // If the `last_password_reset` property is present in the object,
      // it will be updated on the user's profile.
      return callback(null, { "last_password_reset": Date.now() });
    }
  });
}

Was this helpful?

/

Encryption(暗号化)

潜在的なデータ漏洩を防ぐために、bcryptなどの暗号学的ハッシュ関数の暗号化ライブラリを使用してパスワード値を暗号化します。

bcrypt.hash(password, 10, function (err, hash) {
    if (err) {
        return callback(err);
    } else {
        // Return hashed password
    }
});

Was this helpful?

/

コールバック関数

callback関数は2つのパラメーターを受け取り、エラーデータを渡すか、操作の結果を示すために使用されます。

定義

callback(error, operationResult | resultObj): function

Was this helpful?

/

パラメーター タイプ 必須 説明
error オブジェクト 必須 エラーデータを含みます。
operationResult ブール値 任意 パスワードの変更操作の結果を示します。
resultObj オブジェクト 任意 パスワードの変更操作が成功したことを示します。last_password_resetプロパティがある場合は、ユーザーのプロファイルで更新されます。

成功の場合

パスワード変更操作が成功した場合、callback関数を返し、errorパラメーターにはnullを、operationResultパラメーターにはtrueを渡します。

return callback(null, true);

Was this helpful?

/

成功を返し、last_password_reset属性を更新する

パスワード変更操作が成功した場合、callback関数を返し、errorパラメーターにはnullを、profileパラメーターにはオブジェクト値を渡します。オブジェクトにlast_password_reset属性がある場合は、ユーザーのプロファイルで更新されます。

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

Was this helpful?

/

失敗の場合

パスワード変更操作が失敗した場合、callback関数を返し、errorパラメーターにはnullを、operationResultパラメーターにはfalseを渡します。

return callback(null, false);

Was this helpful?

/

エラーの場合

エラーが発生した場合は、callback関数を返し、関連するエラー情報をerrorパラメーターとして渡します。

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

Was this helpful?

/

言語固有のスクリプトの例

Auth0は、以下の言語や技術で使用できるサンプルスクリプトを提供しています。

JavaScript

function changePassword(email, newPassword, callback) {
  // This script should change the password stored for the current user in your
  // database. It is executed when the user clicks on the confirmation link
  // after a reset password request.
  // The content and behavior of password confirmation emails can be customized
  // here: https://manage.auth0.com/#/emails
  // The `newPassword` parameter of this function is in plain text. It must be
  // hashed/salted to match whatever is stored in your database.
  //
  // There are three ways that this script can finish:
  // 1. The user's password was updated successfully:
  //     callback(null, true);
  // 2. The user's password was not updated:
  //     callback(null, false);
  // 3. Something went wrong while trying to reach your database:
  //     callback(new Error("my error message"));
  //
  // If an error is returned, it will be passed to the query string of the page
  // to which the user is being redirected after clicking the confirmation link.
  // For example, returning `callback(new Error("error"))` and redirecting to
  // https://example.com would redirect to the following 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));
}

Was this helpful?

/

ASP.NET Membership Provider(MVC3 - Universal Providers)

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   for Windows Azure enable this
    }
  });
  /**
   * 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
    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) {
      // 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);
    }
    updateMembershipUser(email, newPassword, function(err, wasUpdated) {
      if (err) {
        return callback(err); // this will return a 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);
  }
}

Was this helpful?

/

ASP.NET Membership Provider(MVC4 - Simple Membership)

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 for Windows Azure enable this
    }
  });
  /**
   * hashPassword
   *
   * This function hashes a password using HMAC SHA256 algorithm.
   *
   * @password    {[string]}    password to be hased
   * @salt        {[string]}    salt to be used in the hashing process
   * @callback    {[function]}  callback to be called after hashing the password
   */
  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) {
      // 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);
    }
    updateMembershipUser(email, newPassword, function(err, wasUpdated) {
      if (err) {
        return callback(err); // this will return a 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 record found with that email
      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);
      });
    });
  }
}

Was this helpful?

/

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);
      });
    });
  });
}

Was this helpful?

/

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);
    });
  });
}

Was this helpful?

/

PostgreSQL

function changePassword (email, newPassword, callback) {
  //this example uses the "pg" library
  //more info here: 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) {
        // NOTE: always call `done()` here to close
        // the connection to the database
        done();
        return callback(err, result && result.rowCount > 0);
      });
    });
  });
}

Was this helpful?

/

SQL Server

function changePassword (email, newPassword, callback) {
  //this example uses the "tedious" library
  //more info here: 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);
    });
  });
}

Was this helpful?

/

Windows Azure SQL Database

function changePassword (email, newPassword, callback) {
  //this example uses the "tedious" library
  //more info here: 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) {
    // Uncomment next line in order to enable debugging messages
    // console.log(text);
  }).on('errorMessage', function(text) {
    console.log(JSON.stringify(text, null, 2));
  }).on('infoMessage', function(text) {
    // Uncomment next line in order to enable information messages
    // 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);
    });
  });
}

Was this helpful?

/

もっと詳しく