create にすることを推奨します。
このスクリプトは任意です。有効にすると、ユーザーが からサインアップした場合、または または Auth0 を通じて作成された場合に、Auth0 はこのスクリプトを実行し、外部データベースに対応するユーザーレコードを作成します。
Auth0 でユーザーが作成されると、Auth0 は一連のスクリプトを呼び出します。
- Get User: ユーザーが Auth0 または外部データベースにまだ存在していないことを確認します。
- Create: 外部データベースにユーザーを作成します。
- Login: ユーザーが正常に作成されたことを確認します。
Create 関数
create 関数では、次の処理を行う必要があります。
- ユーザーのプロフィールデータを外部データベースの API に送信します。
- ユーザー作成に失敗した場合は、エラーを返します。
定義
create 関数は 2 つのパラメーターを受け取り、callback 関数を返します。
create(user, callback): function
| パラメーター | 説明 |
|---|---|
user | オブジェクト。ユーザープロファイルデータ が含まれます。このデータはユーザー作成プロセスに由来します。 |
callback | 関数。パイプラインを通じてエラーデータを渡すために使用します。 |
例
これは、create 関数の実装方法を示す擬似 JavaScript の例です。言語別の例については、言語別のスクリプト例 を参照してください。
function create(user, callback) {
// ユーザープロファイルデータを外部データベースAPIに送信する
let hashedPassword = hash(user.password);
let options = {
url: "https://example.com/api/create",
body: {
email: user.email,
username: user.username,
password: hashedPassword
}
};
send(options, err => {
// ユーザーがすでに存在する場合はコールバックでエラーを返す
if (err && err.id === "USER_ALREADY_EXISTS") {
return callback(new ValidationError("user_exists", "My custom error message."));
} else if (err) {
// エラーが発生した場合はコールバックでエラーを返す
return callback(new Error("My custom error message."));
}
// ユーザー作成操作が成功した場合はコールバックで`null`を返す
return callback(null);
});
}
暗号化
パスワード認証情報を、暗号化されていない状態のままログに出力したり、保存したり、転送したりしないでください。
bcrypt などの暗号学的ハッシュライブラリを使用してパスワード値をハッシュ化します。
例
bcrypt.hash(password, 10, function (err, hash) {
if (err) {
return callback(err);
} else {
// ハッシュ化されたパスワードを返す
}
});
コールバック関数
callback 関数は、1 つのパラメーターを受け取り、関数を返します。
定義
callback(error): function
| パラメーター | 型 | 必須 | 説明 |
|---|---|---|---|
error | オブジェクト | 必須 | エラーのデータが含まれます。 |
成功を返す
callback 関数を返し、error パラメーターに null を渡します。
例
return callback(null);
エラーを返す
error パラメーターに関連するエラー情報を渡します。
ValidationError 型オブジェクト
ValidationError カスタムエラー型オブジェクトを使用すると、テナントログに表示するデータを渡せます。
コンストラクター
ValidationError コンストラクターは、最大 2 つのパラメーターを受け取ります。
new ValidationError(errorCode[, message]): ValidationError
| パラメーター | 説明 |
|---|---|
errorCode | (必須) 文字列。エラーの種類を指定します。 |
message | (任意) 文字列。エラーに関する情報が含まれます。 |
ユーザーがすでに存在することを示すエラーを返す
errorCode パラメーターに user_exists を指定してエラーを返すと、Auth0 は fs のテナントログイベントを記録します。
例
return callback(new ValidationError("user_exists", "My custom error message."));
| テナントのログイベントフィールド | 値 |
|---|---|
| コード | fs |
| イベント | サインアップの失敗 |
| 説明 | My custom error message. |
ユーザーオブジェクトパラメーター
user オブジェクトパラメーターには、ユーザー作成プロセスで設定される、あらかじめ定義された一連のプロパティが含まれます。
| プロパティ | 説明 |
|---|---|
client_id | ユーザーが Universal Login 経由でサインアップした場合は Auth0 アプリケーションのクライアントID、Auth0 Dashboard または Management API 経由でユーザーが作成された場合は API キーです。 |
tenant | Auth0 テナントの名前です。 |
email | ユーザーのメールアドレスです。 |
password | ユーザーの平文パスワードです。 |
username | ユーザーの username です。Requires Username 設定が有効なカスタムデータベース接続でのみ必須です。 |
connection | Auth0 接続の名前です。 |
user_metadata | オブジェクトが存在する場合は、ユーザーの Auth0 プロファイル内の user_metadata オブジェクトのプロパティが含まれます。 |
app_metadata | オブジェクトが存在する場合は、ユーザーの Auth0 プロファイル内の app_metadata オブジェクトのプロパティが含まれます。 |
Username プロパティ
username プロパティに対応している必要があるため、これを外部データベースに保存する必要があります。
ユーザーとアプリのメタデータ
user_metadata と app_metadata のプロパティを外部データベースに保存する必要はありません。これらの値は、Auth0 が内部的に作成するユーザープロファイルレコードの一部として自動的に保存されます。
カスタムサインアップフィールド
userオブジェクトに含まれます。
例
{
client_id: "8tkMo6n1QkKOazqPcSQd8wU7LzXYibgK",
tenant: "{yourAuth0Tenant}",
email: "username@domain.com",
password: "mySuperSecretPassword123",
username: "username456",
user_metadata: {
"language": "en"
},
app_metadata: {
"plan": "full"
}
}
言語別のスクリプト例
- JavaScript
- ASP.NET Membership Provider (MVC3 - Universal Providers)
- ASP.NET Membership Provider (MVC4 - Simple Membership)
- MongoDB
- MySQL
- PostgreSQL
- SQL Server
- Windows Azure SQL Database
- Basic 認証を使用したリクエスト
JavaScript
function create(user, callback) {
// このスクリプトは、既存のデータベースにユーザーエントリを作成します。
// ユーザーがサインアップしようとしたとき、またはAuth0 DashboardやManagement APIを通じて
// ユーザーが作成されたときに実行されます。
// このスクリプトの実行が完了すると、ユーザーが正常に作成されたことを確認するために、
// 直後にLoginスクリプトが実行されます。
//
// userオブジェクトには常に以下のプロパティが含まれます:
// * email: ユーザーのメールアドレス
// * password: ユーザーが入力したパスワード(平文)
// * tenant: このAuth0アカウントの名前
// * client_id: ユーザーがサインアップしたアプリケーションのクライアントID、または
// Management APIやAuth0 Dashboardを通じて作成された場合はAPIキー
// * connection: このデータベース接続の名前
//
// このスクリプトの終了方法は3通りあります:
// 1. ユーザーが正常に作成された場合
// callback(null);
// 2. このユーザーがすでにデータベースに存在する場合
// callback(new ValidationError("user_exists", "my error message"));
// 3. データベースへの接続中にエラーが発生した場合
// callback(new Error("my error message"));
const msg = 'Please implement the Create 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 create(user, callback) {
const crypto = require('crypto');
const sqlserver = require('tedious@1.11.0');
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,
// Required to retrieve userId needed for Membership entity creation
rowCollectionOnRequestCompletion: true
}
});
const applicationId = 'your-application-id-goes-here';
/**
* hashPassword
*
* この関数は、データベースに保存するためのパスワードのハッシュ値を作成します。
*
* @password {[string]} ユーザーが入力したパスワード
* @return {[string]} ハッシュ化されたパスワード
*/
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);
}
createMembershipUser(user, function(err, user) {
if (err) return callback(err); // this will return a 500
if (!user) return callback(); // this will return a 401
callback(null, user);
});
});
function createMembershipUser(user, callback) {
const userData = {
UserName: user.email,
ApplicationId: applicationId
};
const createUser =
'INSERT INTO Users (UserName, LastActivityDate, ApplicationId, UserId, IsAnonymous) ' +
'OUTPUT Inserted.UserId ' +
'VALUES (@UserName, GETDATE(), @ApplicationId, NEWID(), \'false\')';
const createUserQuery = new Request(createUser, function(err, rowCount, rows) {
if (err) return callback(err);
// No records added
if (rowCount === 0) return callback(null);
const userId = rows[0][0].value;
const salt = crypto.randomBytes(16);
const membershipData = {
ApplicationId: applicationId,
Email: user.email,
Password: hashPassword(user.password, salt),
PasswordSalt: salt.toString('base64'),
UserId: userId
};
const createMembership =
'INSERT INTO Memberships (ApplicationId, UserId, Password, PasswordFormat, ' +
'PasswordSalt, Email, isApproved, isLockedOut, CreateDate, LastLoginDate, ' +
'LastPasswordChangedDate, LastLockoutDate, FailedPasswordAttemptCount, ' +
'FailedPasswordAttemptWindowStart, FailedPasswordAnswerAttemptCount, ' +
'FailedPasswordAnswerAttemptWindowsStart) ' +
'VALUES ' +
'(@ApplicationId, @UserId, @Password, 1, @PasswordSalt, ' +
'@Email, \'false\', \'false\', GETDATE(), GETDATE(), GETDATE(), GETDATE(), 0, 0, 0, 0)';
const createMembershipQuery = new Request(createMembership, function(err, rowCount) {
if (err) return callback(err);
if (rowCount === 0) return callback(null);
callback(null, rowCount > 0);
});
createMembershipQuery.addParameter('ApplicationId', TYPES.VarChar, membershipData.ApplicationId);
createMembershipQuery.addParameter('Email', TYPES.VarChar, membershipData.Email);
createMembershipQuery.addParameter('Password', TYPES.VarChar, membershipData.Password);
createMembershipQuery.addParameter('PasswordSalt', TYPES.VarChar, membershipData.PasswordSalt);
createMembershipQuery.addParameter('UserId', TYPES.VarChar, membershipData.UserId);
connection.execSql(createMembershipQuery);
});
createUserQuery.addParameter('UserName', TYPES.VarChar, userData.UserName);
createUserQuery.addParameter('ApplicationId', TYPES.VarChar, userData.ApplicationId);
connection.execSql(createUserQuery);
}
}
ASP.NET Membership Provider (MVC4 - Simple Membership)
function create(user, callback) {
const crypto = require('crypto');
const sqlserver = require('tedious@1.11.0');
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,
// Required to retrieve userId needed for Membership entity creation
rowCollectionOnRequestCompletion: true
}
});
/**
* hashPassword
*
* この関数はHMAC SHA256アルゴリズムを使用してパスワードをハッシュ化します。
*
* @password {[string]} ハッシュ化するパスワード
* @salt {[string]} ハッシュ化処理に使用するソルト
* @callback {[function]} パスワードのハッシュ化後に呼び出されるコールバック
*/
function hashPassword(password, salt, callback) {
const iterations = 1000;
const passwordHashLength = 32;
crypto.pbkdf2(password, salt, iterations, passwordHashLength, 'sha1', function (err, hashed) {
if (err) return callback(err);
const result = Buffer.concat([Buffer.from([0], 1), salt, Buffer.from(hashed, 'binary')]);
const 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);
const createUser =
'INSERT INTO UserProfile (UserName) ' +
'OUTPUT Inserted.UserId ' +
'VALUES (@UserName)';
const createUserQuery = new Request(createUser, function (err, rowCount, rows) {
if (err || rowCount === 0) return callback(err);
const userId = rows[0][0].value;
const salt = crypto.randomBytes(16);
const createMembership =
'INSERT INTO webpages_Membership ' +
'(UserId, CreateDate, IsConfirmed, PasswordFailuresSinceLastSuccess, Password, PasswordSalt) ' +
'VALUES ' +
'(@UserId, GETDATE(), \'false\', 0, @Password, \'\')';
const createMembershipQuery = new Request(createMembership, function (err, rowCount) {
if (err || rowCount === 0) return callback(err);
callback(null, rowCount > 0);
});
hashPassword(user.password, salt, function (err, hashedPassword) {
if (err) return callback(err);
createMembershipQuery.addParameter('Password', TYPES.VarChar, hashedPassword);
createMembershipQuery.addParameter('PasswordSalt', TYPES.VarChar, salt.toString('base64'));
createMembershipQuery.addParameter('UserId', TYPES.VarChar, userId);
connection.execSql(createMembershipQuery);
});
});
createUserQuery.addParameter('UserName', TYPES.VarChar, user.email);
connection.execSql(createUserQuery);
});
}
MongoDB
function create(user, 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: user.email }, function (err, withSameMail) {
if (err || withSameMail) {
client.close();
return callback(err || new Error('the user already exists'));
}
bcrypt.hash(user.password, 10, function (err, hash) {
if (err) {
client.close();
return callback(err);
}
user.password = hash;
users.insert(user, function (err, inserted) {
client.close();
if (err) return callback(err);
callback(null);
});
});
});
});
}
MySQL
function create(user, callback) {
const mysql = require('mysql');
const bcrypt = require('bcrypt');
const connection = mysql({
host: 'localhost',
user: 'me',
password: 'secret',
database: 'mydb'
});
connection.connect();
const query = 'INSERT INTO users SET ?';
bcrypt.hash(user.password, 10, function(err, hash) {
if (err) return callback(err);
const insert = {
password: hash,
email: user.email
};
connection.query(query, insert, function(err, results) {
if (err) return callback(err);
if (results.length === 0) return callback();
callback(null);
});
});
}
PostgreSQL
function create(user, 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(user.password, 10, function (err, hashedPassword) {
if (err) return callback(err);
const query = 'INSERT INTO users(email, password) VALUES ($1, $2)';
client.query(query, [user.email, hashedPassword], function (err, result) {
// 注意: データベースへの接続を閉じるために
// 必ずここで `done()` を呼び出してください
done();
return callback(err);
});
});
});
}
SQL Server
function create(user, callback) {
//この例では "tedious" ライブラリを使用しています
//詳細はこちら: http://pekim.github.io/tedious/index.html
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 = 'INSERT INTO dbo.Users SET Email = @Email, Password = @Password';
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);
});
bcrypt.hash(user.password, 10, function(err, hash) {
if (err) return callback(err);
request.addParameter('Email', TYPES.VarChar, user.email);
request.addParameter('Password', TYPES.VarChar, hash);
connection.execSql(request);
});
});
}
Windows Azure SQL Database
function create (user, 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 = "INSERT INTO users (Email, Password) VALUES (@Email, @Password)";
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);
});
bcrypt.hash(user.password, 10, function (err, hashedPassword) {
if (err) { return callback(err); }
request.addParameter('Email', TYPES.VarChar, user.email);
request.addParameter('Password', TYPES.VarChar, hashedPassword);
connection.execSql(request);
});
});
}
Basic 認証を使用したリクエスト
function create(user, callback) {
const request = require('request');
request.post({
url: 'https://myserviceurl.com/users',
json: user
//その他のオプションについては以下を確認:
//https://github.com/mikeal/request#requestoptions-callback
}, function(err, response, body) {
if (err) return callback(err);
callback(null);
});
}
トラブルシューティング
- Actions Real-Time Logs で
console.log()の出力を確認します。 - レガシーデータベースでユーザーを見つけ、必要に応じて削除します。ユーザーが Auth0 に不完全な状態で存在する場合は、Management API の Delete a User エンドポイントまたは Delete a Connection User エンドポイントを使用します。
- Import Mode が無効になっていることを確認してから、create スクリプトを設定します。