> ## Documentation Index
> Fetch the complete documentation index at: https://auth0.generaltranslation.app/llms.txt
> Use this file to discover all available pages before exploring further.

# ユーザーアカウントをリンクする

> さまざまなIDプロバイダーのユーザーアカウントをリンクして、ユーザーが任意のアカウントから認証し、アプリで認識できるようにする方法を説明します。

export const AuthCodeBlock = ({filename, icon, language, highlight, children}) => {
  const [processedChildren, setProcessedChildren] = useState(children);
  useEffect(() => {
    let unsubscribe = null;
    function init() {
      unsubscribe = window.autorun(() => {
        let processedChildren = children;
        for (const [key, value] of window.rootStore.variableStore.values.entries()) {
          processedChildren = processedChildren.replace(new RegExp(key, "g"), value);
        }
        setProcessedChildren(processedChildren);
      });
    }
    if (window.rootStore) {
      init();
    } else {
      window.addEventListener("adu:storeReady", init);
    }
    return () => {
      window.removeEventListener("adu:storeReady", init);
      unsubscribe?.();
    };
  }, [children]);
  return <CodeBlock filename={filename} icon={icon} language={language} lines highlight={highlight}>
      {processedChildren}
    </CodeBlock>;
};

ユーザーアカウントをリンクして、プライマリとセカンダリの関係を形成します。リンクが成功すると、エンドポイントはプライマリアカウントIDの新しい配列を返します。

Auth0は、様々なIDプロバイダーのユーザーアカウントのリンクをサポートします。これにより、ユーザーはどのアカウントからでも認証でき、アプリによって認識され、同じユーザープロファイルに関連付けられます。

<Card title="利用可能性はAuth0プランによって異なる">
  この機能が利用できるかどうかは、使用しているログイン実装とAuth0プラン（または契約）によります。詳細については、「[価格設定](https://auth0.com/pricing)」をお読みください。
</Card>

Auth0 は、デフォルトですべてのIDを個別のものとして扱います。たとえば、ユーザーが最初にAuth0データベースにログインし、次にGoogle またはFacebook経由でログインした場合、Auth0ではこれら2つの試行は2人の別々のユーザーとして表示されます。

アカウントのリンクには3つの方法があります。

* アカウントリンク拡張機能の使用
* <Tooltip data-tooltip-id="react-containers-DefinitionTooltip-0" href="/docs/ja-JP/ja-jp/glossary?term=management-api" tip="Management API: 顧客が管理タスクを実行できるようにするための製品。" cta="用語集の表示">Management API</Tooltip>の使用
* Auth0.jsの使用

各オプションの詳細を確認するには、下のタブを使用してください。

* [アカウントリンク拡張機能](#extension)
* [Management API](#mgmt-api)
* [Auth0 js](#auth0js)

<div id="action-with-external-linking-application">
  ## アカウントリンク拡張機能
</div>

Dashboardの[アカウントリンク拡張機能](/docs/ja-JP/ja-jp/customize/extensions/account-link)をインストールして構成すると、2つ目のアカウントを作成したユーザーの初回ログイン時に、古いアカウントと新しいアカウントをリンクするよう求めることができます。ユーザーは、2つのアカウントをリンクするか、別々にしておくかを選択できます。

## Management APIエンドポイント

Auth0 Management APIが提供する[ユーザーアカウントのリンク](/docs/ja-JP/ja-jp/api/v2#!/Users/post_identities)エンドポイントは、次の2つの方法で呼び出すことができます：

* ユーザーが開始するアカウントリンクで、`update:current_user_identities`スコープを含むアクセストークンを使用する
* サーバー側のアカウントリンクで、`update:users`スコープを含むアクセストークンを使用する

### ユーザーが開始するクライアント側のアカウントリンク

クライアント側のコードからユーザーが開始するアカウントリンクの場合は、ペイロードに次の項目を含んでいるアクセストークンを使用します：

* `update:current_user_identites`スコープ
* URLの一部としてプライマリアカウントの`user_id`
* セカンダリアカウントのIDトークンが`RS256`で署名されていること、クライアントを識別する`aud`クレームが要求アクセストークンの`azp`クレームの値と一致していること。`update:current_user_identities`スコープを含むアクセストークンは、現在ログインしているユーザーの情報を更新する場合**のみ** 使用できます。そのため、このメソッドは、ユーザーがリンクプロセスを開始する場合に適しています。

export const codeExample1 = `{
  "method": "POST",
  "url": "https://{yourDomain}/api/v2/users/PRIMARY_ACCOUNT_USER_ID/identities",
  "httpVersion": "HTTP/1.1",
  "headers": [{
    "name": "Authorization",
    "value": "Bearer ACCESS_TOKEN"
  },
  {
    "name": "content-type",
    "value": "application/json"
  }],
  "postData" : {
    "mimeType": "application/json",
    "text": "{\"link_with\":\"SECONDARY_ACCOUNT_ID_TOKEN\"}"
  }
}`;

<AuthCodeBlock children={codeExample1} language="javascript" />

<div id="user-initiated-client-side-account-linking">
  ### サーバー側のアカウントリンク
</div>

サーバー側のアカウントリンクの場合は、ペイロードに次の項目を含んでいるアクセストークンを使用します：

* `update:users`スコープ
* URLの一部としてプライマリアカウントの`user_id`
* セカンダリアカウントの`user_id`
* セカンダリアカウントのIDトークンが`RS256`で署名されていること、クライアントを識別する`aud`クレームが要求アクセストークンの`azp`クレームの値と一致していること。`update:users`スコープを含むアクセストークンは、**任意の** ユーザーの情報更新に使用できます。そのため、このメソッドは、サーバー側コードでの使用のみを意図しています。

export const codeExample2 = `{
  "method": "POST",
  "url": "https://{yourDomain}/api/v2/users/PRIMARY_ACCOUNT_USER_ID/identities",
  "httpVersion": "HTTP/1.1",
  "headers": [{
    "name": "Authorization",
    "value": "Bearer ACCESS_TOKEN"
  },
  {
    "name": "content-type",
    "value": "application/json"
  }],
  "postData" : {
    "mimeType": "application/json",
    "text": "{\"provider\":\"SECONDARY_ACCOUNT_PROVIDER\", \"user_id\": \"SECONDARY_ACCOUNT_USER_ID\"}"
  }
}`;

<AuthCodeBlock children={codeExample2} language="javascript" />

`SECONDARY_ACCOUNT_USER_ID`と`SECONDARY_ACCOUNT_PROVIDER`は、ユーザーの一意のIDによって推測できます。たとえば、ユーザーIDが`google-oauth2|108091299999329986433`の場合、要求で`google-oauth2`の部分を`provider`、`108091299999329986433`の部分を`user_id`として設定します。`provider`と`user_id`の代わりに、セカンダリアカウントのIDトークンをペイロードの一部として送信できます：

export const codeExample3 = `{
  "method": "POST",
  "url": "https://{yourDomain}/api/v2/users/PRIMARY_ACCOUNT_USER_ID/identities",
  "httpVersion": "HTTP/1.1",
  "headers": [{
    "name": "Authorization",
    "value": "Bearer ACCESS_TOKEN"
  },
  {
    "name": "content-type",
    "value": "application/json"
  }],
  "postData" : {
    "mimeType": "application/json",
    "text": "{\"link_with\":\"SECONDARY_ACCOUNT_ID_TOKEN\"}"
  }
}`;

<AuthCodeBlock children={codeExample3} language="javascript" />

## Auth0.jsライブラリー

[Auth0.js](/docs/ja-JP/ja-jp/libraries/auth0js)ライブラリーを使用できます。まず、Management APIの呼び出しに使用できるアクセストークンを取得しなければなりません。それには、Auth0.jsを初期化する際に`https://{yourDomain}/api/v2/`オーディエンスを指定します。認証フローの一部としてアクセストークンを取得します。代わりに、`checkSession`メソッドを使用することもできます。アクセストークンを取得したら、そのアカウントのAuth0ドメインとアクセストークンを渡して新しい`auth0.Management`インスタンスを作成することができます。詳細については、[Auth0.js > ユーザー管理](/docs/ja-JP/ja-jp/libraries/auth0js/v9#user-management)をご覧ください。

<div id="auth0js-library">
  ## ルールを使用して不足している情報を追加する
</div>

ユーザーがログインすると、アプリは**プライマリID** からユーザー情報を受け取ります。Auth0は、セカンダリIDの情報を使用して、不足しているプロファイルフィールドを自動的に補完しようとしません。たとえば、プライマリIDがデータベース接続から取得され、`given_name`プロパティと`family_name`プロパティが欠落しており、セカンダリIDがユーザーの姓名を含むGoogleソーシャル接続から取得されている場合、アプリケーションは2番目のIDに含まれるデータを受信**しません** 。

セカンダリIDの情報を使用してプライマリIDに不足している情報を追加するには、次の例のようなルールを記述します。

```javascript lines theme={null}
function(user, context, callback) {
  
  const propertiesToComplete = ["given_name", "family_name", "name"];

  // go over each property and try to get missing
  // information from secondary identities
  for(var property of propertiesToComplete) {
    if (!user[property]) {
      for(var identity of user.identities) {
        if (identity.profileData && identity.profileData[property]) {
          user[property] = identity.profileData[property];
          break;
        }
      }
    }
  }
  
  callback(null, user, context);
}
```

<div id="learn-more">
  ## Actionsを使ったアカウントリンク
</div>

Auth0 Actionsを使用して、Management APIを呼び出してユーザーアカウントをリンクできます。Auth0 は、アカウントのリンク後に正しいプライマリユーザーに自動的に変更されないため、アカウントのリンクが成功したらアクションコード内で変更してください。

<Card title="Actionsを使ったアカウントリンク">
  ActionsはAuth0機能を柔軟に拡張できますが、ユーザーアカウントをリンクするときには注意が必要です。

  アカウントリンクが安全でないと、悪意のあるアクターが正当なユーザーアカウントにアクセスできるようになってしまいます。

  以下の点を常に念頭に置いてください：

  アカウントを手動でリンクする際には**毎回** 、ユーザーに資格情報の入力を求めようにします。リンクさせる前には必ず、テナントが**両方** のアカウントに認証を要求するようにします。
</Card>

### Actionsを使ったアカウントリンクの例

基本的なアカウントリンクの実装は次のとおりです。

<Callout icon="file-lines" color="#0EA5E9" iconType="regular">
  プライマリユーザーがアカウントとアクションを正常にリンクするためには、ログインフローを開始する必要があります。
</Callout>

1. アクションにリンクする可能性のあるユーザーアカウントを識別します。
2. [アクションのリダイレクト機能](/docs/ja-JP/ja-jp/customize/actions/explore-triggers/signup-and-login-triggers/login-trigger/redirect-with-actions)とトークンを使用して、外部リンクアプリにリダイレクトします。
3. リンクするすべてのアカウントに対して、ユーザーの資格情報を使用して認証することを要求します。
4. 署名済みJWTにエンコードされた認証の結果を使用してアクションにリダイレクトし、そのトークンの信頼性と内容を検証します。
5. 結果に基づいて、Management API呼び出しでアカウントリンクを実行します。
6. アクションを使用して、トランザクションの残りの部分をプライマリユーザーに切り替えます。

これらの手順を実行するために、ログイン後のアクションの例には次の内容が含まれます。

```javascript lines theme={null}
// @ts-check
const { ManagementClient } = require("auth0");

// High-level Workflow for Performing Account Linking in Actions
// --------------------------------------------------------------------------------------------
//
// The goal of this workflow is to systematically process all users for potential account
// linking. We want to detect situations where an end-user
// may have other identities in Auth0. These other identities would be discovered through
// matching verified email addresses. The Auth0 Action will ensure that all users are processed
// for account linking.
//
// A redirect app will be hosted by the customer to which we will redirect the user's browser
// when account linking might be available. The customer's account linking app is responsible
// authenticating that the current user owns the candidate linking identities. It will actually
// perform the account linking via Management API before redirecting back to the login flow's
// `/continue?state` endpoint.
//
// The Action will pick up here and update the primary user for the login if necessary and mark
// the user as having been successfully processed.
//
// Here are the details of the workflow:
//
// 1. Check if the user has been processed for account linking. The state for this is encoded in
//    the user's `app_metadata`. If the user has already been processed, exit this flow.
// 2. Discover other user identities that are candidates for account linking. If candidates are
//    found:
//   1. Encode the current user and any candidate identities into a signed session token to be
//      passed to the account linking app.
//   2. Redirect the user's browser to the account linking app with the signed session token.
//   3. The account linking app should challenge the user to authenticate using the candidate
//      identities.
//   4. If the user choses to proceed with account linking and successfully authenticates with
//      candidate identities, determine the primary user under which to consolidate identities.
//   5. Use the management API to perform account linking and to map any user or app metadata
//      from the other user accounts into the new primary user account.
//   6. Redirect back to `/continue?state` with a new signed token encoding the primary user
//      that is the outcome of the account linking app.
//   7. Validate the returned session token in the `onContinuePostLogin` entrypoint of the
//      Action. If a change of primary user is required, change the primary user via
//      `api.authentication.setPrimaryUser(newPrimaryUserId)`.
// 3. Mark the user as having been processed for account linking by storing a flag in the
//    user's `app_metadata`.

const LINKING_STATE_KEY = 'account_linking_state';

/**
 * Handler that will be called during the execution of a PostLogin flow.
 *
 * @param {Event} event - Details about the user and the context in which they are logging in.
 * @param {PostLoginAPI} api - Interface whose methods can be used to change the behavior of the login.
 */
exports.onExecutePostLogin = async (event, api) => {
  if (!event.user.email_verified) {
    // We won't process users for account linking until they have verified their email address.
    // We might consider rejecting logins here or redirecting users to an external tool to
    // remind the user to confirm their email address before proceeding.
    //
    // In this example, we simply won't process users unless their email is verified.
    return;
  }

  const accountLinkingState = event.user.app_metadata[LINKING_STATE_KEY];

  if (accountLinkingState) {
    // Account linking has already been processed and completed for this user. No further work
    // to be done in this Action.
    return;
  }

  const token = await getManagementApiToken(event, api);
  const domain = event.secrets.TENANT_DOMAIN;
  const management = new ManagementClient({ domain, token });

  // Search for other candidate users
  const { data: candidateUsers } = await management.usersByEmail.getByEmail({
    email: event.user.email,
  });

  if (!Array.isArray(candidateUsers)) {
    return;
  }

  const candidateIdentities = candidateUsers.flatMap((user) => user.identities);

  if (!candidateIdentities.length) {
    // No candidate users for linking so mark the user as processed.
    api.user.setAppMetadata(LINKING_STATE_KEY, Date.now());
  }

  // Encode the current user and an array of their 
  const sessionToken = api.redirect.encodeToken({
    payload: {
      current_user: event.user,
      candidate_identities: candidateIdentities,
    },
    secret: event.secrets.REDIRECT_SECRET,
    expiresInSeconds: 20,
  });

  api.redirect.sendUserTo('https://url.for/account/linking/service', {
    query: { session_token: sessionToken },
  });
};

/**
 * Handler that will be invoked when this action is resuming after an external redirect. If your
 * onExecutePostLogin function does not perform a redirect, this function can be safely ignored.
 *
 * @param {Event} event - Details about the user and the context in which they are logging in.
 * @param {PostLoginAPI} api - Interface whose methods can be used to change the behavior of the login.
 */
exports.onContinuePostLogin = async (event, api) => {
  // Validate the session token passed to `/continue?state` and extract the `user_id` claim. 
  const { user_id } = api.redirect.validateToken({
    secret: event.secrets.REDIRECT_SECRET,
    tokenParameterName: 'session_token',
  });

  if (user_id !== event.user.user_id) {
    // The account linking service indicated that the primary user changed.
    api.authentication.setPrimaryUser(user_id);
  }

  // Mark the user as having been processed for account linking
  api.user.setAppMetadata(LINKING_STATE_KEY, Date.now());
};
```

## もっと詳しく

* [ユーザーアカウントのリンク：サーバー側実装](/docs/ja-JP/ja-jp/manage-users/user-accounts/user-account-linking/suggested-account-linking-server-side-implementation)
* [ユーザー起点のアカウントのリンク：クライアント側実装](/docs/ja-JP/ja-jp/manage-users/user-accounts/user-account-linking/user-initiated-account-linking-client-side-implementation)
* [ユーザーアカウントのリンクを解除する](/docs/ja-JP/ja-jp/manage-users/user-accounts/user-account-linking/unlink-user-accounts)
