メインコンテンツへスキップ
Create スクリプトは、ユーザー作成時に定義された関数を実行します。この関数名は create にすることを推奨します。 このスクリプトは任意です。有効にすると、ユーザーが からサインアップした場合、または または Auth0 を通じて作成された場合に、Auth0 はこのスクリプトを実行し、外部データベースに対応するユーザーレコードを作成します。 Auth0 でユーザーが作成されると、Auth0 は一連のスクリプトを呼び出します。
  1. Get User: ユーザーが Auth0 または外部データベースにまだ存在していないことを確認します。
  2. Create: 外部データベースにユーザーを作成します。
  3. 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 キーです。
tenantAuth0 テナントの名前です。
emailユーザーのメールアドレスです。
passwordユーザーの平文パスワードです。
usernameユーザーの username です。Requires Username 設定が有効なカスタムデータベース接続でのみ必須です。
connectionAuth0 接続の名前です。
user_metadataオブジェクトが存在する場合は、ユーザーの Auth0 プロファイル内の user_metadata オブジェクトのプロパティが含まれます。
app_metadataオブジェクトが存在する場合は、ユーザーの Auth0 プロファイル内の app_metadata オブジェクトのプロパティが含まれます。

Username プロパティ

カスタムデータベース接続で Requires Username 設定が有効になっている場合、Login スクリプトと Get User スクリプトは username プロパティに対応している必要があるため、これを外部データベースに保存する必要があります。

ユーザーとアプリのメタデータ

user_metadataapp_metadata のプロパティを外部データベースに保存する必要はありません。これらの値は、Auth0 が内部的に作成するユーザープロファイルレコードの一部として自動的に保存されます。

カスタムサインアップフィールド

サインアップ時にカスタムフィールドを作成して使用すると、それらのフィールドはuserオブジェクトに含まれます。

{
    client_id: "8tkMo6n1QkKOazqPcSQd8wU7LzXYibgK",
    tenant: "{yourAuth0Tenant}",
    email: "username@domain.com",
    password: "mySuperSecretPassword123",
    username: "username456",
    user_metadata: {
        "language": "en"
    },
    app_metadata: {
        "plan": "full"
    }
}

言語別のスクリプト例

Auth0 では、以下の言語/技術向けのサンプルスクリプトを提供しています。

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

トラブルシューティング

レガシーデータベースでも Auth0 でもユーザーを作成できない場合:
  1. Actions Real-Time Logsconsole.log() の出力を確認します。
  2. レガシーデータベースでユーザーを見つけ、必要に応じて削除します。ユーザーが Auth0 に不完全な状態で存在する場合は、Management API の Delete a User エンドポイントまたは Delete a Connection User エンドポイントを使用します。
  3. Import Mode が無効になっていることを確認してから、create スクリプトを設定します。

詳細