changePassword にすることをお勧めします。
このスクリプトは、レガシー認証シナリオでのみ使用され、外部データベース内のユーザーのパスワードを変更する場合に必要です。これは、ユーザーがパスワードリセットのワークフローを実行したとき、または または Auth0 からパスワード変更ワークフローが開始されたときに実行されます。
ChangePassword function
changePassword 関数では、次の処理を行う必要があります。
- 外部データベース内のユーザーのパスワードを更新すること。
- パスワード変更に成功した場合は、
true(またはlast_password_resetプロパティを含むオブジェクト) を返すこと。オブジェクトにlast_password_resetプロパティが含まれている場合は、その値がユーザーのプロファイルに反映されます。 - パスワード変更に失敗した場合は、
falseを返すこと。 - 外部データベースに接続できない場合は、エラーを返すこと。
定義
changePassword 関数は 3 つのパラメータを受け取り、callback 関数を返します:
changePassword(email, newPassword, callback): function
| パラメータ | 型 | 説明 |
|---|---|---|
email | String | Auth0 および外部データベースにおけるユーザーのメールアドレス。 |
newPassword | String | 外部データベースでユーザーの新しいパスワードとして設定する値。この値は平文で関数に送信されるため、外部データベースに送信する前に暗号化する必要があります。 |
callback | Function | パイプラインを通じてデータや処理結果を渡すために使用するコールバック関数。 |
例
changePassword 関数の実装方法を示す擬似的なJavaScriptの例です。各言語の例については、言語別スクリプト例をご覧ください。
function changePassword(email, newPassword, callback) {
// 指定されたパスワードをハッシュ化する
let hashedPassword = hash(newPassword);
// API呼び出しを準備する
let options = {
url: "https://example.com/api/users",
body: {
email: email,
password: hashedPassword
}
};
// APIを呼び出す
send(options, err => {
if (err && err.id == "FAIL_CHANGE_PASSWORD") {
// パスワード変更に失敗した場合はコールバックでfalseを返す
return callback(null, false);
} else if (err) {
// その他のエラーが発生した場合はコールバックでエラーを返す
return callback(new Error("My custom error message.");
} else {
// パスワード変更が成功した場合はコールバックでtrueを返す
return callback(null, true);
// または、パスワード変更が成功した場合は、`last_password_reset`プロパティを
// 含むオブジェクトを返すこともできる。
// オブジェクトに`last_password_reset`プロパティが存在する場合、
// ユーザーのプロファイルに反映される。
return callback(null, { "last_password_reset": Date.now() });
}
});
}
暗号化
bcrypt などの暗号学的ハッシュライブラリを使用してパスワード値をハッシュ化します。暗号化されていないパスワード認証情報をログに記録したり、保存または転送したりしないでください。
たとえば:
bcrypt.hash(password, 10, function (err, hash) {
if (err) {
return callback(err);
} else {
// ハッシュ化されたパスワードを返す
}
});
コールバック関数
callback 関数は 2 つのパラメータを受け取り、エラー情報を渡したり、処理の結果を示したりするために使用されます。
定義
callback(error, operationResult | resultObj): function
| パラメータ | 型 | 必須 | 説明 |
|---|---|---|---|
error | オブジェクト | 必須 | エラーデータが含まれます。 |
operationResult | Boolean | 任意 | パスワード変更処理の結果を示します。 |
resultObj | オブジェクト | 任意 | パスワード変更処理が成功したことを示します。last_password_reset プロパティが存在する場合は、ユーザーのプロファイル上で更新されます。 |
成功を返す
callback 関数を呼び出し、error パラメータに null を、operationResult パラメータに true を渡します。例:
return callback(null, true);
成功を返し、last_password_reset 属性を更新する
callback 関数を返し、error パラメータには null を、profile パラメータにはオブジェクトを渡します。オブジェクトに last_password_reset 属性が含まれている場合、その値はユーザーのプロファイルに更新されます。例:
return callback(null, { "last_password_reset": Date.now() });
失敗を返す
callback 関数を呼び出し、error パラメータには null を、operationResult パラメータには false を渡します。例:
return callback(null, false);
エラーを返す
callback 関数に関連するエラー情報を error パラメータとして渡します。例:
return callback(new Error("My custom error message."));
言語別スクリプト例
function changePassword(email, newPassword, callback) {
// このスクリプトでは、現在のユーザーのデータベースに保存されているパスワードを変更します。
// ユーザーがパスワードリセットのリクエスト後に確認リンクをクリックすると実行されます。
// パスワード確認メールの内容と動作は、こちらでカスタマイズできます。
// https://manage.auth0.com/#/emails
// この関数の `newPassword` パラメータは平文です。データベースに保存されている値に
// 合わせてハッシュ化し、saltを付加する必要があります。
//
// このスクリプトの完了方法は3通りあります。
// 1. ユーザーのパスワードが正常に更新された場合:
// callback(null, true);
// 2. ユーザーのパスワードが更新されなかった場合:
// callback(null, false);
// 3. データベースへの接続中に問題が発生した場合:
// callback(new Error("my error message"));
//
// エラーが返された場合、確認リンクをクリックした後にユーザーがリダイレクトされるページの
// クエリ文字列に渡されます。たとえば、`callback(new Error("error"))` を返して
// https://example.com にリダイレクトすると、次の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));
}
// 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 Windows Azure の場合はこれを有効にする
}
});
/**
* hashPassword
*
* この関数は、データベースに保存するパスワードのハッシュを作成します。
*
* @password {[string]} ユーザーが入力したパスワード
* @return {[string]} ハッシュ化されたパスワード
*/
function hashPassword(password, salt) {
// デフォルトの実装は HMACSHA256 を使用し、キー長が64バイト、
// デフォルトのソルトが16バイトのため、Membership はソルトを繰り返してバッファを埋めます
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) {
// 接続に問題がある場合は、詳細情報を得るためにこのコメントを外してください
//console.log(text);
}).on('errorMessage', function(text) {
// SQL データベースへの接続時や 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); // これは 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);
}
}
// 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 Windows Azureの場合はこれを有効にする
}
});
/**
* hashPassword
*
* この関数は、HMAC SHA256アルゴリズムを使用してパスワードをハッシュ化します。
*
* @password {[string]} ハッシュ化するパスワード
* @salt {[string]} ハッシュ化プロセスで使用するsalt
* @callback {[function]} パスワードのハッシュ化後に呼び出すコールバック
*/
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) {
// 接続に問題がある場合は、詳細情報を確認するためにコメントアウトを解除する
//console.log(text);
}).on('errorMessage', function(text) {
// SQLデータベースへの接続時または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); // 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);
}
// このメールアドレスに該当するレコードは見つかりませんでした
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);
});
});
}
}
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);
});
});
});
}
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);
});
});
}
function changePassword (email, newPassword, callback) {
// この例では「pg」ライブラリを使用します
// 詳細はこちら: 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) {
// 注: データベースへの接続を閉じるために、
// ここでは常に `done()` を呼び出してください
done();
return callback(err, result && result.rowCount > 0);
});
});
});
}
function changePassword (email, newPassword, callback) {
//この例では「tedious」ライブラリを使用します
//詳細はこちら: 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);
});
});
}
function changePassword (email, newPassword, callback) {
//この例では「tedious」ライブラリを使用します
//詳細: 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) {
// デバッグメッセージを有効にするには、次の行のコメントアウトを解除します
// 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); }
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);
});
});
}