> ## Documentation Index
> Fetch the complete documentation index at: https://auth0-automated-sdk-version-update.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# ユーザー取得データベースアクションスクリプトとテンプレート

> ユーザー取得スクリプトは、Auth0が現在ユーザーが存在しているかどうかを確認する必要がある場合に実行されます。

ユーザー取得スクリプトは、現在ユーザーが存在しているかどうかを確認するために実行する関数を実装します。この関数の名前は `getUser`にすることをお勧めします。

このスクリプトは、[Auth0への自動移行](/docs/ja-jp/manage-users/user-migration/configure-automatic-migration-from-your-database)に必須です。また、移行を有効にしていない場合は、接続に構成された操作によっては必須となります。Auth0では、ユーザープロファイルで返す`user_id`を永続的かつ一貫したものにすることを強く推奨しています。つまり、同じユーザーについて、ユーザー取得スクリプトを実行するたびに同じ`user_id`を返す必要があります。詳しくは、[一貫性のあるuser\_idを返す](#return-a-consistent-user-id)をご覧ください。

［**Auth0にユーザーをインポート**］が有効になっている場合、ユーザーがサインアップを試みたときに、外部ユーザーストアにそのユーザーがすでに存在するかどうかを確認するために、ユーザー取得スクリプトが実行されます。

また、ユーザーが以下を試みたときにもユーザー取得スクリプトが実行されます。

* メールアドレスの変更（[メール変更](/docs/ja-jp/authenticate/database-connections/custom-db/templates/change-email)スクリプト）
* ログイン（[ログイン](/docs/ja-jp/authenticate/database-connections/custom-db/templates/login)スクリプト）
* パスワードの変更（[パスワード変更](/docs/ja-jp/authenticate/database-connections/custom-db/templates/change-password)スクリプト）

［**Auth0にユーザーをインポート**］が無効になっている場合も、ユーザーがサインアップを試みたときに、外部ユーザーストアにそのユーザーがすでに存在するかどうかを確認するためにユーザー取得スクリプトが実行されます。外部ユーザーストアにユーザーがすでに存在する場合、作成スクリプトは実行されません。

また、ユーザーが以下を試みたときにもユーザー取得スクリプトが実行されます。

* サインアップ（[作成](/docs/ja-jp/authenticate/database-connections/custom-db/templates/create)スクリプト）
* メールアドレスの変更（[メール変更](/docs/ja-jp/authenticate/database-connections/custom-db/templates/change-email)スクリプト）
* パスワードの変更またはリセット（[パスワード変更](/docs/ja-jp/authenticate/database-connections/custom-db/templates/change-password)スクリプト）

## ユーザー取得関数

`getUser`関数は以下を行う必要があります。

* ユーザーの識別子を外部データベースのAPIに送信する。
* ユーザーが見つかったら、そのユーザーのプロファイルデータを返す。
* ユーザーが存在するかどうかを確認する際に問題が発生した場合は、エラーを返す。

### 定義

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

```js lines theme={null}
getUser(email, callback): function
```

| パラメーター     | 型   | 説明                                   |
| ---------- | --- | ------------------------------------ |
| `email`    | 文字列 | ユーザーのメールアドレス。                        |
| `callback` | 関数  | パイプラインを介してエラーまたはプロファイルデータを渡すのに使用される。 |

以下は、`getUser` 関数の実装方法を示す疑似JavaScriptの例です。

```javascript lines expandable theme={null}
function getUser(email, callback) {
  // ユーザー識別子を外部データベースAPIに送信する
  let options = {
    url: "https://example.com/api/search-users",
    body: {
      email: email
    }
  };

  send(options, (err, profileData) => {
    // ユーザーの検索で問題が発生した場合は、コールバックでエラーを返す
    if (err) {
      return callback(new Error("Could not determine if user exists or not."));
    } else {
      // ユーザーが見つからない場合はコールバックでnullを返し、見つかった場合はコールバックでプロファイルデータを返す
      if (!profileData) {
        return callback(null);
      } else {
        let profile = {
          email: profileData.email,
          user_id: profileData.userId
        };

        return callback(null, profile);
      }
    }
  });
}
```

## コールバック関数

`callback`関数は、パイプラインを介してユーザープロファイルデータまたはエラーデータを渡すのに使用されます。

### 定義

`callback`関数は最大2つのパラメーターを受け取り、関数を返します。

```js lines theme={null}
callback(error[,profile]): function
```

| パラメーター    | 型      | 必須 | 説明                 |
| --------- | ------ | -- | ------------------ |
| `error`   | オブジェクト | 必須 | エラーデータを含む。         |
| `profile` | オブジェクト | 任意 | ユーザーのプロファイルデータを含む。 |

### ユーザープロファイルを返す（ユーザーが見つかった場合）

<Warning>
  ユーザー取得スクリプトがユーザーについて返すプロファイルデータは、ログインスクリプトが返すプロファイルデータと一貫性がなければなりません。
</Warning>

ユーザーが見つかった場合には、`null`値を`error` パラメーターに渡し、ユーザーのプロファイルデータを[正規化された形式](/docs/ja-jp/manage-users/user-accounts/user-profiles/normalized-user-profile-schema)で`profile`パラメーターに渡します。

```js lines theme={null}
return callback(null, {
    username: "username",
    user_id: "my-custom-db|username@domain.com",
    email: "username@domain.com",
    email_verified: false,
    user_metadata: {
        language: "en"
    },
    app_metadata: {
        plan: "full"
    },
    mfa_factors: [
      {
        phone: {
          value: "+15551234567"
        }
      },
    ]
});
```

標準フィールドに加えて、`user_metadata`、`app_metadata`、`mfa_factors`フィールドを含めることもできます。

### 一貫性のあるuser\_idを返す

Auth0は、ユーザー取得スクリプトが返す`user_id`を使用して、テナント全体でユーザーを識別します。特定のユーザーについて、この値は**一貫**している必要があります。つまり、そのユーザーに対してユーザー取得スクリプトを実行するたびに、同じ`user_id`を返す必要があります。`user_id`は、実行するたびに変化する値ではなく、元のレコードにある安定した永続的なプロパティ（データベースのプライマリーキーなど）から生成してください。

<Callout icon="file-lines" color="#0EA5E9" iconType="regular">
  一貫性のない`user_id`を返すと、重複したユーザーや孤立したユーザーが作成されたり、識別子が不変であることを前提としたフローが機能しなくなったりする可能性があります。
</Callout>

`user_id`は、元のレコードにある変化しないプロパティから生成し、同じユーザーが常に同じ値に対応するようにしてください。

```js lines theme={null}
return callback(null, {
    // `user.id`はデータベースの不変の主キーである
    user_id: "my-custom-db|" + user.id,
    email: user.email,
    email_verified: user.email_verified
});
```

呼び出すたびに新しい`user_id`やランダムな`user_id`を生成しないでください。たとえば新たに生成したUUIDを返すと、同じユーザーに対してスクリプトを実行するたびに、異なる識別子が生成されてしまいます。

```js lines theme={null}
const { v4: uuidv4 } = require("uuid");

return callback(null, {
    // アンチパターン：実行するたびに新しい値が生成されるため、Auth0では毎回新しいユーザーとして扱われる
    user_id: uuidv4(),
    email: user.email,
    email_verified: user.email_verified
});
```

### ユーザープロファイルを返さない（ユーザーが見つからなかった場合）

ユーザーが見つからなかった場合には、`null`値を`error`パラメーターに返し、`profile`パラメーターを省略します。

```js lines theme={null}
return callback(null);
```

### エラーを返す

エラーが発生した場合には、何が起きたかについての関連情報を含むエラーデータを`error`パラメーターに渡します。

```js lines theme={null}
return callback(new Error("My custom error message."));
```

詳細については、「[カスタムデータベースのトラブルシューティング](/docs/ja-jp/authenticate/database-connections/custom-db/error-handling)」をご覧ください。

## 言語別のスクリプトの例

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

<CodeGroup>
  ```javascript Javascript lines theme={null}
  function getByEmail(email, callback) {
    // このスクリプトでは、既存のデータベースからユーザープロファイルを取得する。
    // ユーザーを認証せずに取得する必要がある。
    // 認証を必要としないフロー（サインアップやパスワードリセット）を実行する前に、
    // ユーザーが存在するかどうかを確認するために使用する。
    //
    // このスクリプトは、次の3通りの方法で終了できる。
    // 1. ユーザーが正常に見つかった場合。プロファイルは次の形式にする。
    // フォーマット：https://auth0.com/docs/users/normalized/auth0/normalized-user-profile-schema.
    //     callback(null, profile);
    // 2.ユーザーが見つからなかった場合
    //     callback(null);
    // 3.データベースへの接続中に問題が発生した場合
    //     callback(new Error("my error message"));
    const msg = 'このデータベース接続のユーザー取得スクリプトを実装してください ' +
      'at https://manage.auth0.com/#/connections/database';
    return callback(new Error(msg));
  }
  ```

  ```javascript ASP.NET MVC3 lines expandable theme={null}
  // ASP.NETメンバーシッププロバイダー（MVC3 - Universal Providers）用
  function getByEmail(email, callback) {
    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 // Windows Azure用
      }
    });
    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);
      var user = {};
      const query =
        'SELECT Memberships.UserId, Email, Users.UserName ' +
        'FROM Memberships INNER JOIN Users ' +
        'ON Users.UserId = Memberships.UserId ' +
        'WHERE Memberships.Email = @Username OR Users.UserName = @Username';
      const getMembershipQuery = new Request(query, function(err, rowCount) {
        if (err) return callback(err);
        if (rowCount < 1) return callback();
        callback(null, user);
      });
      getMembershipQuery.addParameter('Username', TYPES.VarChar, email);
      getMembershipQuery.on('row', function(fields) {
        user = {
          user_id: fields.UserId.value,
          nickname: fields.UserName.value,
          email: fields.Email.value
        };
      });
      connection.execSql(getMembershipQuery);
    });
  }
  ```

  ```javascript ASP.NET MVC4 lines expandable theme={null}
  // For ASP.NETメンバーシッププロバイダー（MVC4 - Simple Membership）用
  function getByEmail(email, callback) {
    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 // Windows Azure用
      }
    });
    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);
      var user = {};
      const query =
        'SELECT webpages_Membership.UserId, UserName, UserProfile.UserName from webpages_Membership ' +
        'INNER JOIN UserProfile ON UserProfile.UserId = webpages_Membership.UserId ' +
        'WHERE UserProfile.UserName = @Username';
      const getMembershipQuery = new Request(query, function (err, rowCount) {
        if (err) return callback(err);
        if (rowCount < 1) return callback();
        callback(null, user);
      });
      getMembershipQuery.addParameter('Username', TYPES.VarChar, email);
      getMembershipQuery.on('row', function (fields) {
        user = {
          user_id: fields.UserId.value,
          nickname: fields.UserName.value,
          email: fields.UserName.value
        };
      });
      connection.execSql(getMembershipQuery);
    });
  }
  ```

  ```javascript MongoDB lines theme={null}
  function getByEmail(email, callback) {
    const MongoClient = require('mongodb@5.1.0').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: email }, function (err, user) {
        client.close();
        if (err) return callback(err);
        if (!user) return callback(null, null);
        return callback(null, {
          user_id: user._id.toString(),
          nickname: user.nickname,
          email: user.email
        });
      });
    });
  }
  ```

  ```javascript MySQL lines theme={null}
  function getByEmail(email, callback) {
    const mysql = require('mysql');
    const connection = mysql({
      host: 'localhost',
      user: 'me',
      password: 'secret',
      database: 'mydb'
    });
    connection.connect();
    const query = 'SELECT id, nickname, email FROM users WHERE email = ?';
    connection.query(query, [ email ], function(err, results) {
      if (err || results.length === 0) return callback(err || null);
      const user = results[0];
      callback(null, {
        user_id: user.id.toString(),
        nickname: user.nickname,
        email: user.email
      });
    });
  }
  ```

  ```javascript PostgreSQL lines theme={null}
  function loginByEmail(email, callback) {
    // "pg"ライブラリを使用する例
    // 詳細については、次を参照： https://github.com/brianc/node-postgres
    const postgres = require('pg');
    const conString = 'postgres://user:pass@localhost/mydb';
    postgres.connect(conString, function (err, client, done) {
      if (err) return callback(err);
      const query = 'SELECT id, nickname, email FROM users WHERE email = $1';
      client.query(query, [email], function (err, result) {
        // 注：ここでは必ず`done()`を呼び出して
        // データベースへの接続を閉じる
        done();
        if (err || result.rows.length === 0) return callback(err);
        const user = result.rows[0];
        return callback(null, {
          user_id: user.id,
          nickname: user.nickname,
          email: user.email
        });
      });
    });
  }
  ```

  ```javascript SQL Server lines expandable theme={null}
  function getByEmail(email, callback) {
    // "tedious"ライブラリを使用する例
    // 詳細については、次を参照：http://pekim.github.io/tedious/index.html
    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 = 'SELECT Id, Nickname, Email FROM dbo.Users 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, rowCount, rows) {
        if (err) return callback(err);
        callback(null, {
          user_id: rows[0][0].value,
          nickname: rows[0][1].value,
          email: rows[0][2].value
        });
      });
      request.addParameter('Email', TYPES.VarChar, email);
      connection.execSql(request);
    });
  }
  ```

  ```javascript Azure SQL Database lines theme={null}
  function getByEmail (name, callback) {
    var profile = {
      user_id:     "103547991597142817347",
      nickname:    "johnfoo",
      email:       "johnfoo@gmail.com",
      name:        "John Foo",
      given_name:  "John",
      family_name: "Foo"
    };
    callback(null, profile);
  }
  ```

  ```js Axios lines expandable theme={null}
  async function getUserAsync(email, callback) {
    // Axiosの新しいバージョンが利用可能になったら更新すること（https://auth0-extensions.github.io/canirequire/#axios）
    const axios = require("axios@0.22.0");

    let response;

    try {
      response = await axios.post(
        // SDLC環境をより適切にサポートするため、APIのURLを接続設定に保存する
        configuration.baseAPIUrl + "/getUser",
        // ユーザーの認証情報をリクエスト本文として渡す
        {
          email: email,
        },
        {
          timeout: 10000, // リクエストがタイムアウトした場合は呼び出しを正常に終了し、スクリプトで必要なコールバックを実行できるようにする
          headers: {
            // 接続設定に保存されたapiKeyを使用してAPI呼び出しを保護する。
            // 簡単ですぐに利用できる方法だが、M2Mトークンを使用するほうが
            // クライアントとAPI間でシークレットを共有する必要がないため、より安全である。
            "x-api-key": configuration.apiKey,
          },
        }
      );
    } catch (e) {
      if (e.response.status === 404) {
        // 指定されたメールアドレスまたはユーザー名のユーザーが見つからない場合に、APIが404を返すことを前提とする
        return callback(null, null);
      }
      // その他のエラータイプの場合のコールバック
      return callback(new Error(e.message));
    }

    try {
      let user = response.data;

      // テナントで複数のカスタムデータベース接続を使用している場合は、
      // user_idの前に接続固有のキーを付ける。例： "connName|" + user.user_id
      // これにより、すべてのデータベース接続でユーザーIDが一意になる
      return callback(null, {
        user_id: user.user_id,
        email: user.email,
      });
    } catch (e) {
      return callback(new Error(e.message));
    }
  }
  ```

  ```javascript Stormpath lines expandable theme={null}
  function getByEmail(email, callback) {
    // {yourStormpathClientId}をStormpath IDに置き換える
    var url = 'https://api.stormpath.com/v1/applications/{yourStormpathClientId}/accounts';
    // Stormpath APIのクライアントIDとシークレットを追加する
    var apiCredentials = {
      user : '{yourStormpathApiId}',
      password: '{yourStormpathApiSecret}'
    };
    // メールアドレスでユーザーを検索するGETリクエストを実行する
    request({
      url: url,
      method: 'GET',
      auth: apiCredentials,
      qs: { q: email },
      json: true
    }, function (error, response, body) {
      if (response.statusCode !== 200) return callback();
      var user = body.items[0];
      if (!user) return callback();
      var id = user.href.replace('https://api.stormpath.com/v1/accounts/', '');
      return callback(null, {
        user_id: id,
        username: user.username,
        email: user.email,
        email_verified: true
        // Stormpathから引き継ぐ追加フィールドがあれば追加する
      });
    });
  }
  ```
</CodeGroup>
