> ## Documentation Index
> Fetch the complete documentation index at: https://docs-dev-docs-event-stream-action-templates.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# カスタムトークン交換の早期アクセス

> 早期アクセス版のカスタムトークン交換機能について説明します。

<Callout icon="file-lines" color="#0EA5E9" iconType="regular">
  カスタムトークン交換は、現在早期アクセス版として提供されています。Auth0のリリースについては、「[製品のリリース段階](/docs/ja-jp/troubleshoot/product-lifecycle/product-release-stages)」を参照してください。
</Callout>

[RFC 8693](https://datatracker.ietf.org/doc/html/rfc8693)で定義されているように、カスタムトークン交換はアプリケーションが`/oauth/token`エンドポイントを呼び出したときに既存のトークンをAuth0トークンに交換できるようにします。これは以下のような高度な統合に役立ちます。

* 別のオーディエンスのAuth0トークンを取得する
* 外部のIDプロバイダーを統合する
* Auth0に移行する

詳細については、「[ユースケースの例とサンプルコード](#example-use-cases-and-code-samples)」をお読みください。

トークン交換を管理して必要な特定のユースケースに調整するために、1つ以上の[カスタムトークン交換プロファイル](#custom-token-exchange-profile)を定義できます。それぞれのプロファイルでは、トランザクションにユーザー情報を提供する`subject_token_type`と[アクション](/docs/ja-jp/customize/actions/actions-overview)が1対1でマッピングされます。アクション内にはカスタムコードを作成して、`/oauth/token`エンドポイントに渡されたサブジェクトトークンの復号化と検証を行うことができます。

カスタムトークン交換はユーザーの認証に使用できます。たとえば、アクション内でユースケースの認可ロジックを適用して、トランザクションにユーザーを設定できます。そうすると、Auth0がアクセストークン、IDトークンとリフレッシュトークンをユーザーに発行します。

<Callout icon="file-lines" color="#0EA5E9" iconType="regular">
  カスタムトークン交換は、トランザクションでユーザーを識別するサブジェクトトークンの安全な検証を追加で担うことにより、トランザクションのユーザー設定に優れた柔軟性を提供します。

  カスタムトークン交換に使用するサブジェクトトークンは、必要なアクションコードが解釈できるのであれば、どのような形式や種類のトークンでも構いません。**受信して受け入れるトークンに対して強固な検証を実装しなければなりません。** 検証に不備があった場合、なりすましやリプレイ攻撃などのあらゆる攻撃にさらされることになり、悪意のある行為者が他者のユーザーIDを使用して認証できるようになります。

  サブジェクトトークンの安全な検証を実装する各種のオプションについては、「[ユースケースの例とサンプルコード](#example-use-cases-and-code-samples)」に記載の推奨を読んだ上で適用してください。また、[攻撃防御](#attack-protection)機能の適用も検討してください。
</Callout>

## セットアップ

### アプリケーション

カスタムトークン交換を使用するには、<Tooltip data-tooltip-id="react-containers-DefinitionTooltip-0" href="/docs/ja-jp/glossary?term=auth0-dashboard" tip="Auth0 Dashboard: サービスを構成するためのAuth0の主製品。" cta="用語集の表示">Auth0 Dashboard</Tooltip>または<Tooltip data-tooltip-id="react-containers-DefinitionTooltip-0" href="/docs/ja-jp/glossary?term=management-api" tip="Management API: 顧客が管理タスクを実行できるようにするための製品。" cta="用語集の表示">Management API</Tooltip>を使用して[新しいアプリケーションを作成](/docs/ja-jp/get-started/auth0-overview/create-applications)する必要があります。カスタムトークン交換には、複数のアプリケーションを作成して使用できます。

新しいアプリケーションを作成するには以下を行います。

1. カスタムトークン交換はデフォルトで無効に設定されます。カスタムトークン交換を有効化するには、Management APIを使用してPOST呼び出しを[クライアント作成](https://auth0.com/docs/api/management/v2/clients/post-clients)エンドポイントに対して行うか、PATCH呼び出しを[クライアント更新](https://auth0.com/docs/api/management/v2/clients/patch-clients-by-id)エンドポイントに対して行います。`token_exchange`の`allow_any_profile_of_type`属性を`["custom_authentication"]`に設定します。

```json lines theme={null}
{
  "token_exchange": {
    "allow_any_profile_of_type": ["custom_authentication"]
  }
}
```

2. アプリケーションに[データベース接続](/docs/ja-jp/authenticate/database-connections)または[エンタープライズ接続](/docs/ja-jp/authenticate/enterprise-connections)を有効化して、カスタムトークン交換で使用できるようにします。

3. アプリケーションに[［First-Party（ファーストパーティー）］](/docs/ja-jp/get-started/applications/confidential-and-public-applications/first-party-and-third-party-applications)フラグがあり、[［Dashboard］>［Applications（アプリケーション）］>［Advanced Settings（詳細設定）］>［OAuth］で［OIDC Conformant（OIDC準拠）］](/docs/ja-jp/get-started/applications/application-settings#oauth)として構成されていることを確認します。

<Callout icon="file-lines" color="#0EA5E9" iconType="regular">
  カスタムデータベースは`setUserById`操作にのみ対応しています。次回のカスタムトークン交換の EAリリースでは対応が`setUserByConnection`まで拡大される予定です。
</Callout>

アプリケーションを作成したら、`client_id`と`client_secret`を必ずメモして、後で`/oauth/token`エンドポイントを呼び出すときに使用できるようにします。

### カスタムトークン交換プロファイル

カスタムトークン交換プロファイルはそれぞれ`subject_token_type`にマッピングされ、該当するユースケースのコードロジックがあるアクションと関連付けられます。

特定の`subject_token_type`値を含めて`/oauth/token`エンドポイントに送信されたカスタムトークン交換要求は、対応するカスタムトークン交換プロファイルにマッピングされ、処理のために関連するアクションに送られます。

カスタムトークン交換プロファイルを作成するには、まずプロファイルにアクションを作成します。

#### アクションを作成する

Auth0 Dashboardで以下を行います。

1. **［Actions（アクション）］** > **［Library（ライブラリー）］** に移動します。

2. **［Create Action（アクションを作成）］** > **［Build from scratch（初めから構築する）］** を選択します。

3. **［Create Action（アクションを作成）］** ダイアログに名前を入力し、ドロップダウンから **［Custom Token Exchange（カスタムトークン交換）］** トリガーを選択します。

<Frame>
  <img src="https://mintcdn.com/docs-dev-docs-event-stream-action-templates/5l8GTXHD40NsjgmL/docs/images/ja-jp/cdy7uua7fh8z/22vz9dsCFj5Ruot7U0HIVx/1c3dc4b562334dab9d6ac415028ea76e/Screenshot_2025-02-05_at_8.48.34_AM.png?fit=max&auto=format&n=5l8GTXHD40NsjgmL&q=85&s=f2781278624b8c1458a42fcb1d1965c2" alt="" width="400" height="440" data-path="docs/images/ja-jp/cdy7uua7fh8z/22vz9dsCFj5Ruot7U0HIVx/1c3dc4b562334dab9d6ac415028ea76e/Screenshot_2025-02-05_at_8.48.34_AM.png" />
</Frame>

4. **［Create（作成）］** を選択します。

5. アクションを **［Deploy（デプロイ）］** します。

<Frame>
  <img src="https://mintcdn.com/docs-dev-docs-event-stream-action-templates/oCq_HmOgBHEv-wbr/docs/images/ja-jp/cdy7uua7fh8z/56NZA69Gmzha167xfgRD0W/302e59276815d2e2644ab2da3b9b5f1f/Screenshot_2025-02-03_at_10.29.17_AM.png?fit=max&auto=format&n=oCq_HmOgBHEv-wbr&q=85&s=9357a65983cff535628422f1546020ff" alt="" width="1244" height="574" data-path="docs/images/ja-jp/cdy7uua7fh8z/56NZA69Gmzha167xfgRD0W/302e59276815d2e2644ab2da3b9b5f1f/Screenshot_2025-02-03_at_10.29.17_AM.png" />
</Frame>

アクションをデプロイすると、Auth0がアクションIDを割り当てます。アクションにはカスタムロジックを追加しなければなりませんが、その前にアクションIDを取得して、カスタムトークン交換プロファイルを作成します。

6. Auth0 DashboardでアクションIDを取得するには、ブラウザーウィンドウのURLを確認します。下の画像が示すように、アクションIDはURLの末尾の部分にあります。

<Frame>
  <img src="https://mintcdn.com/docs-dev-docs-event-stream-action-templates/1FU_hDyg-ytA7ilg/docs/images/ja-jp/cdy7uua7fh8z/1Xx4UbgZR0FIuLC1KVvhKG/bde4469d770c2ff8d37c19895a0c8e66/Screenshot_2025-02-03_at_10.31.18_AM.png?fit=max&auto=format&n=1FU_hDyg-ytA7ilg&q=85&s=3dd1e86efa52aa0274a392f55fcc96a9" alt="" width="1244" height="558" data-path="docs/images/ja-jp/cdy7uua7fh8z/1Xx4UbgZR0FIuLC1KVvhKG/bde4469d770c2ff8d37c19895a0c8e66/Screenshot_2025-02-03_at_10.31.18_AM.png" />
</Frame>

アクションIDはManagement APIで取得することもできます。まず、APIを使用するために[Management APIトークンを取得](/docs/ja-jp/secure/tokens/access-tokens/management-api-access-tokens#get-management-api-tokens)します。そして、以下のGET要求を`/actions`エンドポイントに送信します。

```bash lines theme={null}
curl --location 'https://{{YOUR _TENANT}}/api/v2/actions/actions?actionName={{ACTION_NAME}}' \
--header 'Authorization: Bearer {{MANAGEMENT_API_TOKEN}}' \
```

応答本文の`actions[0].id`内にアクションIDが含まれているはずです。アクションIDはカスタムトークン交換プロファイルの作成に必要です。

#### カスタムトークン交換プロファイルを作成する

カスタムトークン交換プロファイルを作成するには、Management APIを使用して、POST要求に以下のパラメーターを含めて`/token-exchange-profiles`エンドポイントに送信します。

```bash lines theme={null}
curl --location 'https://{{YOUR _TENANT}}/api/v2/token-exchange-profiles' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{MANAGEMENT_API_TOKEN}}' \
--data '{
    "name": "{{PROFILE_NAME}}",
    "subject_token_type": "{{UNIQUE_PROFILE_TOKEN_TYPE_URI}}",
    "action_id": "{{ACTION_ID}}",
    "type": "custom_authentication"
}'
```

| パラメーター               | 説明                                                                                                                                                                                                                                                                             |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `subject_token_type` | 特有なプロファイルのトークンタイプの`https://`または`urn`で始まるURIです。<br /><br />以下の名前空間は予約されているため使用できません。<br /><br /><ul><li>`http://auth0.com`</li><li>`https://auth0.com`</li><li>`http://okta.com`</li><li>`https://okta.com`</li><li>`urn:ietf`</li><li>`urn:auth0`</li><li>`urn:okta`</li></ul> |
| `action_id`          | カスタムトークンプロファイルに関連付けられているアクションのアクションIDです。                                                                                                                                                                                                                                       |
| `type`               | `custom_authentication`に設定します。                                                                                                                                                                                                                                                 |

カスタムトークン交換プロファイルが正常に作成されると、以下の応答を受け取ります。

```json lines theme={null}
{
  "id":"tep_9xqewuejpa2RTltf",
  "name":"{{PROFILE_NAME}}",
  "type":"custom_authentication",
  "subject_token_type":"{{UNIQUE_PROFILE_TOKEN_TYPE_URI}}",
  "action_id":"{{ACTION_ID}}",
  "created_at":"2025-01-30T13:19:00.616Z",
  "updated_at":"2025-01-30T13:19:00.616Z"
}
```

これで、ユースケースに実装するカスタムトークン交換のコード作成とテストの準備が整いました。

### カスタムトークン交換プロファイルを管理する

カスタムトークン交換プロファイルを管理するには、Management APIを使用して`/token-exchange-profiles`エンドポイントに要求を送信します。

カスタムトークン交換プロファイルを取得するには、以下の要求を行います。複数のプロファイルがある場合のために、このエンドポイントは[チェックポイントページネーション](/docs/ja-jp/api/management/v2/introduction#checkpoint-based-pagination)に対応しています。

```bash lines theme={null}
curl --location 'https://{{YOUR _TENANT}}/api/v2/token-exchange-profiles' \
--header 'Authorization: Bearer {{MANAGEMENT_API_TOKEN}}' \
```

カスタムトークン交換プロファイルの`name`または`subject_token_type`を更新するには、以下のPATCH要求を行います。アクションIDは変更できませんが、実行されるカスタムコードはActionsエディターで変更できます。

```bash lines theme={null}
curl --location --request PATCH 'https://{{YOUR _TENANT}}/api/v2/token-exchange-profiles/{{PROFILE_ID}}' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{MANAGEMENT_API_TOKEN}}' \
--data '{
    "name": "external-idp-migration",
    "subject_token_type": "urn:partner0:external-idp-migration"
}'
```

カスタムトークン交換プロファイルを削除するには、以下のDELETE要求を行います。

```bash lines theme={null}
curl --location --request DELETE 'https://{{YOUR _TENANT}}/api/v2/token-exchange-profiles/{{PROFILE_ID}}' \
--header 'Authorization: Bearer {{MANAGEMENT_API_TOKEN}}' \
--data ''
```

## Actions API

### カスタムトークン交換とログイン後アクション

カスタムトークン交換アクションは早期アクセス版のカスタムトークン交換の一部として提供され、「[Actions APIを使用する](#use-the-actions-api)」に記載の新しいAPIメソッドが使用できます。

アクセストークンにカスタムクレームを追加することなどが必要な場合は、トランザクションに設定済みのユーザーに対してカスタムトークン交換アクションを実行したた後に、[ログイン後アクショントリガー](/docs/ja-jp/customize/actions/explore-triggers/signup-and-login-triggers/login-trigger)を実行すると、ログインフローと同じ機能性が実現できます。

トークン交換の付与タイプを使用するトランザクションを識別するには、[ログイン後アクション](/docs/ja-jp/customize/actions/explore-triggers/signup-and-login-triggers/login-trigger)の`oauth2-token-exchange`と同じ値を持つ`event.transaction.protocol`を探します。トークン交換の付与タイプはカスタムトークン交換とネイティブソーシャルログインの両方のトランザクションで使用され、`subject_token_type`はいずれかのカスタムトークン交換プロファイルに対応しているため、`subject_token_type`値を使用すれば、それらの2つを区別できます。

<Callout icon="file-lines" color="#0EA5E9" iconType="regular">
  早期アクセス版のカスタムトークン交換は[多要素認証](/docs/ja-jp/secure/multi-factor-authentication)に対応していません。MFAをテナントポリシーとして有効化することや、`api.multifactor.enable()`、`api.authentication.challengeWith()`、`api.authentication.enrollWith()`の使用はカスタムトークン交換に未対応であるため、ログイン後アクショントリガー内においてトランザクションが回復不可能なエラーで失敗します。`subject_token_type`値に応じて`event.transaction.protocol==oauth2-token-exchange` の場合にはMFAの有効化を必ずスキップしてください。

  MFA対応は次回のカスタムトークン交換のEAに追加される予定です。
</Callout>

### Actions APIを使用する

トークン交換アクションで使用できるように、Auth0は数々のAPIメソッドを提供しています。`subject_token_type`に基づいてサブジェクトトークンの復号化と検証を行うアクションを実装してください。そうすることで、トランザクションのためにユーザー情報を入手できます。また、この情報を使用して、コードがトランザクションに必要な認可ポリシーを適用するようにします。トランザクションが続行できることを確認したら、対応するユーザーを設定してトランザクションを確定できます。そうすると、Auth0がアクセストークン、IDトークンとリフレッシュトークンをユーザーに発行します。これはユーザーを認証する1つの方法だと考えることができます。

カスタムトークン交換トランザクションはそれぞれ1つのテナントイベントログを生成します。トランザクションが成功すると種類が`secte`のイベントログが生成され、トランザクションが失敗すると種類が`fecte`のイベントログが生成されます。これらの種類は受け取るかもしれないエラーを理解するのに役立ちます。`/oauth/token`エンドポイントからのエラーには詳細があまり含まれません。

<Callout icon="file-lines" color="#0EA5E9" iconType="regular">
  カスタムトークン交換は、トランザクションでユーザーを識別するサブジェクトトークンの安全な検証を追加で担うことにより、トランザクションのユーザー設定に優れた柔軟性を提供します。

  カスタムトークン交換に使用するサブジェクトトークンは、必要なアクションコードが解釈できるのであれば、どのような形式や種類のトークンでも構いません。**受信して受け入れるトークンに対して強固な検証を実装しなければなりません。** 検証に不備があった場合、なりすましやリプレイ攻撃などのあらゆる攻撃にさらされることになり、悪意のある行為者が他者のユーザーIDを使用して認証できるようになります。

  サブジェクトトークンの安全な検証を実装する各種のオプションについては、「[ユースケースの例とサンプルコード](#example-use-cases-and-code-samples)」に記載の推奨を読んだ上で適用してください。また、[攻撃防御](#attack-protection)機能の適用も検討してください。
</Callout>

#### api.authentication.setUserById(user\_id)

任意の接続タイプに対して、指定のユーザーIDを基にユーザー属性を設定します。これにより、プロファイルを更新することなく、既存のユーザーを指定できます。このメソッドはユーザーが存在しないか、ブロックされていると失敗します。

| パラメーター    | 説明                                          |
| --------- | ------------------------------------------- |
| `user_id` | ユーザーIDです。例：`auth0&amp;#124;55562040asf0aef` |

```javascript lines theme={null}
exports.onExecuteCustomTokenExchange = async (event, api) => {

  // 1. Validate subject_token
  const subject_token = await validateToken(event.transaction.subject_token, jwksUri);

  // 2.  Apply your authorization policy on the user
  const isAuthorized = await authorizeAccess(subject_token.sub);
  if (!isAuthorized) {
    api.access.deny('Unauthorized_login', 'User cannot login due to reason: X');
  }

  // 3. Set the user for the transaction
  api.authentication.setUserById(subject_token.sub);

  return;
};
```

#### api.authentication.setUserByConnection(connection\_name, user\_profile, options)

ユーザーとそのユーザーに関するプロファイル属性を指定の接続内で設定します。これは、ユーザーがこの接続にログインし、指定されたユーザープロファイルをフェデレーション<Tooltip data-tooltip-id="react-containers-DefinitionTooltip-2" href="/docs/ja-jp/glossary?term=idp" tip="IDプロバイダー（IdP）: デジタルIDを保存および管理するサービス。" cta="用語集の表示">IdP</Tooltip>が返すことと同等です。ユーザーが存在しない場合にこの操作がユーザーを作成するか、そして、提供されたユーザープロファイル属性を使ってプロファイルを更新するかを構成できます。

ユーザーが`setUserByConnection()`を通してログインするたびに、ログイン回数が加算されます。このメソッドはユーザーがブロックされていると失敗します。

<Callout icon="file-lines" color="#0EA5E9" iconType="regular">
  カスタムトークン交換のEA版は現在、エンタープライズ接続とソーシャル接続に加えて、Auth0のデータベース接続に対応しています。今後のカスタムトークン交換のEA版には、インポートモードが`OFF`のカスタムデータベース対応が追加される予定です。
</Callout>

| パラメーター            | 説明                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `connection_name` | ユーザープロファイルが設定される接続の名前です。512文字が上限です。                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| `user_profile`    | 設定するユーザープロファイル属性を含むオブジェクトです。プロパティは24が上限です。                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `options`         | 更新と作成の動作を指定するオブジェクトです。<br /><br />`{updateBehavior:'replace' \| 'none',creationBehavior:'create_if_not_exists' \| 'none',}`<br /><br />ユーザーが存在する場合は、`updateBahaviour`が以下を行います。<br /><ul><li> `replace`：提供された接続のユーザー属性および`user_id`を置換します（既存のユーザー属性の中で提供されないものは、ユーザーから削除されます。部分的な更新には対応していません。）</li><li>`none`：ユーザーが存在する場合は、プロファイルを更新しません。ユーザーが存在しない場合は、`creationBehavior`の構成に応じて、提供されたプロファイル属性でプロファイルを作成します。</li><li>ユーザーが存在しない場合は、`creationBehavior`が以下を行います。</li><ul><li>`create_if_not_exists`：ユーザーを作成します</li><li>`none`：ユーザーを作成することなく、エラーを返します</li></ul></ul> |

```javascript lines theme={null}
exports.onExecuteCustomTokenExchange = async (event, api) => {

  // 1. Validate subject_token
  const subject_token = await validateToken(event.transaction.subject_token, jwksUri);

  // 2.  Apply your authorization policy on the user
  const isAuthorized = await authorizeAccess(subject_token.sub);
  if (!isAuthorized) {
    api.access.deny('Unauthorized_login', 'User cannot login due to reason: X');
  }

  // 3. Set the user for the transaction
  api.authentication.setUserByConnection(
    'My Connection',
    {
      user_id: subject_token.sub,
      email: subject_token.email,
      email_verified: subject_token.email_verified,
      phone_number: subject_token.phone_number,
      phone_verified: subject_token.phone_number_verified,
      username: subject_token.preferred_username,
      name: subject_token.name,
      given_name: subject_token.given_name,
      family_name: subject_token.family_name,
      nickname: subject_token.nickname,
      verify_email: false
    },
    {
      creationBehavior: 'create_if_not_exists',
      updateBehavior: 'none'
    }
  );

  return;
};
```

##### 対応されているユーザープロファイル属性

`setUserByConnection()`メソッドでは、[ユーザー更新](https://auth0.com/docs/api/management/v2/users/patch-users-by-id)エンドポイントが対応しているプロファイル属性を設定できます。

* `user_id` （必須）：この接続またはプロバイダーでユーザーに一意の識別子です。通常は、この接続について外部のIDプロバイダーが提供するユーザーIDです。このパラメーターは`creationBehaviour`と`updateBehaviour`の両方が`none`に設定されている場合にのみ必須です。
* `email`
* `email_verified`。デフォルトは`false`です。
* `username`
* `phone_number`
* `phone_verified`。デフォルトは`false`です。
* `name`
* `given_name`
* `family_name`
* `nickname`
* `picture`

上のリストにはない属性の設定が必要な場合は、メタデータフィールドを使用してください。

##### 対応されている接続ストラテジー

現在のバージョンは以下の接続ストラテジーに対応しています。これ以外のストラテジーでは、`setUserByConnection()`メソッドが失敗します。他のストラテジーへの対応をご希望の場合には、Auth0サポートまでお問い合わせください。

エンタープライズ接続：

* [Active Directory](/docs/ja-jp/authenticate/identity-providers/enterprise-identity-providers/active-directory-ldap)
* [SAML IdP](/docs/ja-jp/authenticate/identity-providers/enterprise-identity-providers/saml)
* [OIDC IdP](/docs/ja-jp/authenticate/identity-providers/enterprise-identity-providers/oidc)
* [Okta](/docs/ja-jp/authenticate/identity-providers/enterprise-identity-providers/okta)
* [ADFS](/docs/ja-jp/authenticate/identity-providers/enterprise-identity-providers/adfs)

ソーシャル接続：

* カスタムソーシャル接続
* Google
* Apple
* Facebook
* Github
* Windowslive

##### 作成の動作

ユーザーは`creationBehavior`が`create_if_not_exists`に設定されている場合にのみ動的に作成されます。

ユーザーの作成には以下が必要です。

* 使用している接続に構成されている識別子を提供しなければなりません。メールはデフォルトで必須です。
* [柔軟な識別子と属性](/docs/ja-jp/authenticate/database-connections/flexible-identifiers-and-attributes)を使用する接続については、該当する属性が接続に有効化されていれば、ユーザー名や電話番号を提供できます。
* 柔軟な識別子と属性を使用しない接続については以下を行います。

  * 接続に\*\*［Require Username（ユーザー名を必須にする）］\*\* が`true`に設定されている場合は、ユーザー名を提供できます。詳細については、「[データベース接続にユーザー名を追加する](/docs/ja-jp/authenticate/database-connections/require-username)」をお読みください。
  * `phone_number`は提供できません。
* `email_verified`や`phone_verified`を指定しても構いません。

Auth0のデータベース接続では、ユーザーのためにランダムなパスワードが動的に生成されます。ユーザーの作成後に必要に応じて[パスワードのリセットフロー](/docs/ja-jp/authenticate/database-connections/password-change)をトリガーするには、別のオプションがあります。

<Callout icon="file-lines" color="#0EA5E9" iconType="regular">
  ユーザーが接続に存在していない場合に、ユーザーを作成することなくログインさせるには、`creationBehavior`を`none`に設定します。

  今後のカスタムトークン交換では、接続の構成に応じてメール属性が任意になる予定です。
</Callout>

##### 更新の動作

ユーザープロファイルは`updateBehavior`が`replace`に設定されている場合にのみ更新されます。

以下の属性は編集できません。値を変更しようとすると、Auth0からエラーが返されます。

* `email`
* `username`
* `phone_number`
* `email_verified`
* `phone_verified`

<Callout icon="file-lines" color="#0EA5E9" iconType="regular">
  `setUserByConnection()`を使用して、すでに`email`、`username`、または`phone_number`の属性を含むユーザープロファイルを更新にするには、それらの属性に既存の値を指定して渡す必要があります。そうしないと、メソッドがエラーを返します。
</Callout>

<Callout icon="file-lines" color="#0EA5E9" iconType="regular">
  ユーザーが接続にすでに存在する場合に、プロファイル属性を変更することなくログインさせるには、`updateBehavior`を`none`に設定します。
</Callout>

##### メール検証

`email_verified=false`でユーザーが作成されると、Auth0は自動的に確認メールを送信します。この動作をオーバーライドするには、`verify_email=false`をユーザープロファイル属性として指定します。ユーザープロファイルの一部として保管はされません。

```javascript lines theme={null}
exports.onExecuteCustomTokenExchange = async (event, api) => {

  // Validate subject_token
  const subject_token = await validateToken(event.transaction.subject_token, jwksUri);

  // Create a user but don't verify email
  api.authentication.setUserByConnection(
    'My Connection',
    {
      user_id: subject_token.sub,
      email: subject_token.email,
      email_verified: false,
      verify_email: false
    },
    {
      creationBehavior: 'create_if_not_exists',
      updateBehavior: 'none'
    }
  );

  return;
};
```

[歓迎メールテンプレートを構成して有効化](/docs/ja-jp/customize/email/email-templates)すると、確認メールが送信されない場合に、Auth0は新規作成されたユーザーに対して自動的に歓迎メールを送信します。

##### メタデータを設定する

[ユーザー更新](https://auth0.com/docs/api/management/v2/users/patch-users-by-id)エンドポイントとは異なり、`setUserByConnection()`メソッドではユーザーやアプリケーションのメタデータを設定できません。その場合は`api.user.setAppMetadata`を使用できます。ユーザーメタデータの正しい使い方については、「[ユーザープロファイルでのメタデータの仕組み](/docs/ja-jp/manage-users/user-accounts/metadata)」をお読みください。メタデータのベストプラクティスについては、「[ログイン後トリガーでユーザーメタデータを管理する方法](/docs/ja-jp/manage-users/user-accounts/metadata/manage-user-metadata#best-practices)」をお読みください。

#### api.user.setAppMetadata(name, value)

ログインを試行するユーザーのアプリケーションメタデータを設定します。

このメソッドはマージ動作に従うため、既存の属性に影響を与えることなく、追加する属性や更新する属性を指定できます。属性を削除するには、値を`null`に設定します。

| パラメーター  | 説明                            |
| ------- | ----------------------------- |
| `name`  | 文字列。メタデータプロパティの名前です。          |
| `value` | 文字列、オブジェクト、配列。メタデータプロパティの値です。 |

```javascript lines theme={null}
exports.onExecuteCustomTokenExchange = async (event, api) => {
  // Validate subject_token
  const subject_token = await validateToken(event.transaction.subject_token, jwksUri);

  // set the user for the transaction
  api.authentication.setUserById(subject_token.id);

  // set user group based on info contaiened in subject_token
  api.user.setAppMetadata('group', subject_token.group);

  return;
};
```

#### api.user.setUserMetadata(name, value)

ログインを試行するユーザーの一般的なメタデータを設定します。

このメソッドはマージ動作に従うため、既存の属性に影響を与えることなく、追加する属性や更新する属性を指定できます。属性を削除するには、値を`null`に設定します。

| パラメーター  | 説明                            |
| ------- | ----------------------------- |
| `name`  | 文字列。メタデータプロパティの名前です。          |
| `value` | 文字列、オブジェクト、配列。メタデータプロパティの値です。 |

```javascript lines theme={null}
exports.onExecuteCustomTokenExchange = async (event, api) => {
  // Validate subject_token
  const subject_token = await validateToken(event.transaction.subject_token, jwksUri);

  // set the user for the transaction
  api.authentication.setUserById(subject_token.id);

  // set user preferred_locale based on info contaiened in subject_token
  api.user.setUserMetadata('preferred_locale', subject_token.locale);

  return;
};
```

#### api.access.deny(code, reason)

ログイントランザクションを拒否して、呼び出し元にエラーを返します。

| パラメーター   | 説明                                                                                                                                                                                                           |
| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `code`   | 応答でプロパティに含めて返される文字列です。<br /><br />以下の2つの標準エラーコードを使用できます。<ul><li>`invalid_request`：`400`番台のステータスコードを返します</li><li>`server_error`：`500`番台のステータスコードを返します</li></ul><br />独自のエラーコードを使用する場合は、`400`番台のステータスコードを返します。 |
| `reason` | 応答の`error_description`プロパティに含めて返される文字列です。                                                                                                                                                                    |

```javascript lines theme={null}
exports.onExecuteCustomTokenExchange = async (event, api) => {

  // 1. Validate subject_token
  const subject_token = await validateToken(event.transaction.subject_token, jwksUri);

  // 2.  Apply your authorization policy on the user
  const isAuthorized = await authorizeAccess(subject_token.sub);
  if (!isAuthorized) {
    api.access.deny('Unauthorized_login', 'User cannot login due to reason: X');
  }

  // if user is authorized, go on as indicated here

};
```

#### api.access.rejectInvalidSubjectToken(reason)

トランザクションを拒否して、要求元の外部IPアドレスについて試行の失敗数を加算します。カスタムトークン交換は要求を`invalid_request`のエラーコードを使用した`400 Bad Request`エラー応答で拒否します。

試行の失敗が最大数に達すると、該当するIPアドレスからのすべてのカスタムトークン交換要求について、Auth0はトラフィックを`too_many_attempts`のエラーコードを使用した`429 Too Many Requests`エラー応答でブロックします。詳細については、「[攻撃防御](#attack-protection)」をお読みください。

このメソッドは、署名や暗号化が不適切、または有効期限切れのサブジェクトトークンを含むカスタムトークン交換要求を受け取った場合には必ず使用してください。また、なりすましやリプレイ攻撃など、不正使用が疑われる状況でも必ず使用してください。そうすることで、Auth0は構成に応じて、不審なIPのスロットリングを適用できるようになります。

不審なIPのスロットリングはデフォルトで最大10回まで、1時間あたりに6回の試行を許容します。詳細については、「[攻撃防御](#attack-protection)」をお読みください。

| パラメーター   | 説明                                        |
| -------- | ----------------------------------------- |
| `reason` | 応答で`error_description`プロパティに含めて返された文字列です。 |

```javascript lines theme={null}
exports.onExecuteCustomTokenExchange = async (event, api) => {

  try {
    // Validate subject_token
    const subject_token = await validateToken(event.transaction.subject_token, jwksUri);
    // set the user for the transaction
    api.authentication.setUserById(subject_token.id);

  } catch (error) {
    if (error.message === 'Invalid Token') {
      // If specifically the problem is the subject_token is invalid
      console.error('Invalid Token error');
      api.access.rejectInvalidSubjectToken('Invalid subject_token');
    } else {
      // if there is any other unexpected error, throw a server error
      throw error;
    }
  }

};
```

#### api.cache

実行間で維持されるデータの保管と取得を行います。

これらのメソッドはサブジェクトトークンを検証するために、署名検証の公開鍵などのデータをキャッシュする場合に役立ちます。`jwks-uri`からキーを取得する際のパフォーマンスを向上させることができます。

##### api.cache.delete(key)

提供された`key`にキャッシュ済みの値が存在する場合は、それを記述したレコードを削除します。

値がキャッシュから削除されると、`CacheWriteResult`オブジェクトに`type: "success"`を含めて返します。操作に失敗すると、`type: "error"`を返します。エラーの場合には、返すオブジェクトに`code`プロパティを含めて、失敗の詳細を示します。

| パラメーター | 説明                          |
| ------ | --------------------------- |
| `key`  | 文字列。キャッシュに保管されているレコードのキーです。 |

##### api.cache.get(key)

提供された`key`にキャッシュ済みの値が存在する場合は、それを記述したレコードを取得します。レコードが見つかった場合には、返されたオブジェクトの`value`プロパティにキャッシュ済みの値があります。

提供された`key`にキャッシュが見つかった場合には、キャッシュレコードを返します。キャッシュレコードは`value`プロパティを含むオブジェクトで、このプロパティにはキャッシュ済みの値の他にも`expires_at`プロパティが含まれ、レコードの最大有効期間をUNIXエポックからの経過ミリ秒数で示します。

**重要：** このキャッシュは、短命で一時的なデータ向けに設計されています。項目が所定のライフタイム内であったとしても、後のトランザクションでは利用できないかもしれません。

| パラメーター | 説明                          |
| ------ | --------------------------- |
| `key`  | 文字列。キャッシュに保管されているレコードのキーです。 |

##### api.cache.set(key, value, \[options])

指定されたkeyのキャッシュに文字列値を保管または更新します。

このキャッシュに保管された値は、それを設定するトリガーにスコープが限定されます。これは[アクションのキャッシュ制限](/docs/ja-jp/customize/actions/limitations)の対象になります。

このように保管された値には、指定された`ttl`または`expires_at`値までのライフタイムがあります。ライフタイムが指定されない場合には、デフォルトのライフタイムである15分が使用されます。ライフタイムは[アクションのキャッシュ制限](/docs/ja-jp/customize/actions/limitations)が定める最大値を超過してはいけません。

値が正常に保管されると、`CacheWriteSuccess`を返します。それ以外の場合は`CacheWriteError`を返します。

| パラメーター               | 説明                                                                                                                                                                                            |
| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `key`                | 文字列。キャッシュに保管されているレコードのキーです。                                                                                                                                                                   |
| `value`              | 文字列。保管するレコードの値です。                                                                                                                                                                             |
| `options`            | 任意のオブジェクト。キャッシュの動作を調整するオプションです。                                                                                                                                                               |
| `options.expires_at` | 任意の数値。UNIXエポックからのミリ秒単位で指定した絶対有効期限です。キャッシュ済みのレコードは早期に削除されることはあっても、`expires_at`で指定された時点を過ぎて存続することはありません。<br /><br />\_\_注意：\_\_この値がttlで指定されている場合は指定するべきではありません。両方で指定された場合、2つの中で早い方の有効期限が適用されます。 |
| `options.ttl`        | 任意の数値。このキャッシュエントリの存続時間をミリ秒単位で指定します。キャッシュ済みの値は早期に削除されることはあっても、`ttl`で指定された時点を過ぎて存続することはありません。<br /><br />\_\_注意：\_\_この値がexpires\_atで指定されている場合は指定するべきではありません。両方で指定された場合、2つの中で早い方の有効期限が適用されます。    |

## アクションイベント

新しいActions APIメソッドに加えて、アクションイベントのデータを使用して、サブジェクトトークン、IPアドレス、クライアントなど、トークン交換要求のコンテキストについて知ることができます。

| プロパティ                | タイプ    | 例                                  |
| -------------------- | ------ | ---------------------------------- |
| *client*             |        |                                    |
| `client_id`          | 文字列    | `HOVc2PDFTH7eahimN4yNCo8mOtjfNjLV` |
| `name`               | 文字列    | `My Web App`                       |
| `metadata`           | オブジェクト | `{“foo”: “bar” }`                  |
| *tenant*             |        |                                    |
| `id`                 | 文字列    | `dev_1234`                         |
| request              |        |                                    |
| `geoip`              | オブジェクト | `{ … geoip object}`                |
| `hostname`           | 文字列    | `dev_1234.us.auth0.com`            |
| `ip`                 | 文字列    | `123.42.42.34`                     |
| `user_agent`         | 文字列    | `Mozilla/5.0`                      |
| `language`           | 文字列    | `en`                               |
| `body`               | オブジェクト | `{ // raw req.body }`              |
| `method`             | 文字列    | `POST`                             |
| *transaction*        |        |                                    |
| `subject_token_type` | 文字列    | `urn://cic-migration-token`        |
| `subject_token`      | 文字列    | `41598922a1745f7af70`              |
| `requested_scopes`   | 文字列\[] | `[“openid”, “email”]`              |
| *resource\_server*   |        |                                    |
| `id`                 | 文字列    | `http://acme-api/v1/profile`       |

## アクションをデプロイする

上記のAPIとイベントオブジェクトでトークン交換アクションを作成したら、ページの上部にある **［Deploy（デプロイ）］** をクリックして変更内容をデプロイします。

<Frame>
  <img src="https://mintcdn.com/docs-dev-docs-event-stream-action-templates/V-g8sIA_dMysRiDH/docs/images/ja-jp/cdy7uua7fh8z/61fgdh8VJEmfiYn0l7Tem0/3ceac5de5413373662e8b800d8f80210/Screenshot_2025-02-03_at_9.30.50_PM.png?fit=max&auto=format&n=V-g8sIA_dMysRiDH&q=85&s=769e4f0441f666edcb61ca541ffe74da" alt="" width="500" height="160" data-path="docs/images/ja-jp/cdy7uua7fh8z/61fgdh8VJEmfiYn0l7Tem0/3ceac5de5413373662e8b800d8f80210/Screenshot_2025-02-03_at_9.30.50_PM.png" />
</Frame>

## トークン交換を呼び出す

カスタムトークン交換を使用するには、以下のパラメーターを指定して`POST`要求を`/oauth/token`エンドポイントに対して行います。以下に留意してください。

* カスタムトークン交換に使用する`subject_tokens`は、アクションコードが解釈できるのであれば、どのような形式や種類のトークンでも構いません。
* `subject_token_type`はそれぞれ特定のカスタムトークン交換プロファイルにマッピングされ、そのトランザクションを制御する特定のアクションに関連付けられます。

| パラメーター               | 説明                                                                                                                                                                                                                                                                                                                                                             |
| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `grant_type`         | カスタムトークン交換には `urn:ietf:params:oauth:grant-type:token-exchange` を使用します。                                                                                                                                                                                                                                                                                         |
| `subject_token_type` | サブジェクトトークンの種類です。カスタムトークン交換では、独自に所有するURIでスコープが限定された任意の値です。たとえば、`http://acme.com/legacy-token`や`urn:acme:legacy-token`.<br /><br />です。以下の名前空間は予約されているため使用できません。<ul><li>`http://auth0.com`</li><li>`https://auth0.com`</li><li>`http://okta.com`</li><li>`https://okta.com`</li><li>`urn:ietf`</li><li>`urn:auth0`</li><li>`urn:okta`</li></ul>                   |
| `subject_token`      | アクションが検証とユーザーの識別に使用するべきサブジェクトトークンです。                                                                                                                                                                                                                                                                                                                           |
| `client_id`          | トークン交換に使用しているアプリケーションのクライアントIDです。他の付与タイプについても、HTTP Basic認証でクライアントIDをAuthorizationヘッダーに含めて渡すことができます。                                                                                                                                                                                                                                                            |
| `client_secret`      | トークン交換に使用しているアプリケーションのクライアントシークレットです。他の付与タイプについても、HTTP Basic認証でクライアントシークレットをAuthorizationヘッダーに含めて渡すことができます。<br /><br />他にも方法はありますが、詳細については[Auth0 Authentication APIのリファレンスドキュメント](https://auth0.com/docs/api/authentication#authentication-methods)を参照してください。<br /><br />公開アプリケーションがカスタムトークン交換を使用できることに注意してください。利用する場合は必ず「[攻撃防御](#attack-protection)」をお読みください。 |
| `audience`           | Auth0で定義されているAPI識別子です。                                                                                                                                                                                                                                                                                                                                         |
| `scope`              | OAuth2スコープのパラメーターです。                                                                                                                                                                                                                                                                                                                                           |

その他の拡張パラメーターは、対応するアクションの`event.request.body`に含まれていますが、無視されます。

<Callout icon="file-lines" color="#0EA5E9" iconType="regular">
  Organizationsはカスタムトークン交換の早期アクセスではまだ対応されていません。organizationパラメーターを使用すると、要求が拒否されます。Organizations対応は次のバージョンのカスタムトークン交換に追加する予定です。
</Callout>

### 要求例

```bash lines theme={null}
curl --location 'https://{{YOUR_TENANT}}/oauth/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=urn:ietf:params:oauth:grant-type:token-exchange' \
--data-urlencode 'audience=https://api.acme.com' \
--data-urlencode 'scope=openid offline_access acme-scope1 acme-scope2' \
--data-urlencode 'subject_token_type=urn:acme:external-idp-migration' \
--data-urlencode 'subject_token=t8e7S2D9trQm73e .... iqBR3GjxDtbDVjpfQU' \
--data-urlencode 'client_id={{CLIENT_ID}}' \
--data-urlencode 'client_secret={{CLIENT_SECRET}}'
```

## 攻撃防御

なりすましやリプレイ攻撃で悪意のある攻撃者がサブジェクトトークンを推測または再利用しようとするのを防ぐために、カスタムトークン交換は[不審なIPのスロットリング](/docs/ja-jp/secure/attack-protection/suspicious-ip-throttling)に対応しています。これにより、[サブジェクトトークンが無効](#api-access-rejectinvalidsubjecttoken-reason-)な場合に、アクション内のコードから信号を特異的に送信できるため、Auth0は外部IPからの試行の失敗をカウントできます。

1つのIPアドレスからの試行の失敗数が事前構成のしきい値に達すると、該当するIPアドレスからのカスタムトークン交換要求について、Auth0は以下のエラー応答でトラフィックをブロックします。

```json lines theme={null}
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
{
    "error": "too_many_attempts",
    "error_description": "We have detected suspicious login behavior and further attempts will be blocked. Please contact the administrator."
}
```

構成済みの期間が過ぎると、IPアドレスは要求を再び行えるようになります。

すべての事例で推奨されることですが、**不審なIPのスロットリングを正しく構成してアクティブ化することは、カスタムトークン交換をネイティブアプリケーションやシングルページアプリケーションで使用する場合には特に重要です** 。ネイティブアプリケーションやSPAなどの非機密アプリケーションは、自身を認証するためにシークレットを安全に保管できません。攻撃者は簡単にサブジェクトトークンを推測したり、窃盗や漏洩したものを使用したりできます。

不審なIPのスロットリングを正しく使用するためには、受け取ったサブジェクトトークンが厳密に検証されない場合は必ず、アクションのコードに`api.access.rejectInvalidSubjectToken`を使用してください。

不審なIPのスロットリングはAuth0テナントにはデフォルトでアクティブ化されます。アクティブ化や構成の方法については、「[不審なIPのスロットリング](/docs/ja-jp/secure/attack-protection/suspicious-ip-throttling)」をお読みください。アクティブ化すると、カスタムトークン交換のデフォルト設定が適用されます。

* しきい値：10。1つのIPアドレスが失敗した試行の最大数です。
* スロットリングレート：1時間あたり6回。しきい値を超えない範囲で10分ごとに1回の試行が補充されます。

<Frame>
  <img src="https://mintcdn.com/docs-dev-docs-event-stream-action-templates/f9tcsxrYvRYBs4lY/docs/images/ja-jp/cdy7uua7fh8z/47PB3OAci9fotSHFrCNBVn/1bafbaacbeb22a4d94eb78506ab89bb8/Screenshot_2025-02-03_at_4.44.29_PM.png?fit=max&auto=format&n=f9tcsxrYvRYBs4lY&q=85&s=6db5b66de9b543f5e2379b0b2a3ab250" alt="" width="1244" height="966" data-path="docs/images/ja-jp/cdy7uua7fh8z/47PB3OAci9fotSHFrCNBVn/1bafbaacbeb22a4d94eb78506ab89bb8/Screenshot_2025-02-03_at_4.44.29_PM.png" />
</Frame>

カスタムトークン交換には、Management APIを使用してカスタムのしきい値やスロットリングレートを構成できます。

まず、APIを使用するために[Management APIトークンを取得](/docs/ja-jp/secure/tokens/access-tokens/management-api-access-tokens#get-management-api-tokens)します。そして、以下のGET要求を[不審なIPのスロットリング設定取得エンドポイント](https://auth0.com/docs/api/management/v2/attack-protection/get-suspicious-ip-throttling)に対して行います。

```bash lines theme={null}
curl --location 'https://{{YOUR _TENANT}}/api/v2/attack-protection/suspicious-ip-throttling' \
--header 'Authorization: Bearer {{MANAGEMENT_API_TOKEN}}' \
```

以下のような応答を受け取ります。

```json lines theme={null}
{
  "enabled": true,
  "shields": [
    "admin_notification",
    "block"
  ],
  "allowlist": [],
  "stage": {
    "pre-login": {
      "max_attempts": 100,
      "rate": 864000
    },
    "pre-user-registration": {
      "max_attempts": 50,
      "rate": 1200
    },
    "pre-custom-token-exchange": {
      "max_attempts": 10,
      "rate": 600000
    }
  }
}
```

以下の`PATCH`要求では`pre-custom-token-exchange`ステージを必要な値で更新できます。レートは新たに試行が許可されるまでをミリ秒単位の間隔で表していることに注意してください。

```bash lines theme={null}
curl --location --request PATCH 'https://{{YOUR _TENANT}}/api/v2//attack-protection/suspicious-ip-throttling' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{MANAGEMENT_API_TOKEN}}' \
--data '{"stage":{"pre-custom-token-exchange":{"max_attempts":10,"rate":600000}}}'
```

## ユースケースの例とサンプルコード

技術的な制約やユーザーエクスペリエンスの観点から、エンドユーザーのリダイレクトに基づく標準的なフェデレーションログインストラテジーが適用できない高度な統合シナリオには、カスタムトークン交換をソリューションとして使用できます。ユースケースに提供するコードは不完全なものであり、対処するコードを論理的に進められるように説明することのみを意図しています。より詳しいサンプルコードについては、「[サンプルコード](#code-samples)」を参照してください。

<Callout icon="file-lines" color="#0EA5E9" iconType="regular">
  できる限り、そのままで使用できる通常のフェデレーションログインの使用をお勧めします。カスタムトークン交換はトランザクションにユーザーを設定可能にすることで、トランザクションの安全な検証と処理を強化し、柔軟性を向上します。
</Callout>

このセクションでは、ユースケースの例と該当するシナリオの実装に推奨されるサンプルコードを紹介します。ユースケースを説明するために、GearUpという架空のレンタカー会社を想定します。

### ユースケース：Auth0へシームレスに移行する

GearUpには何百万人もが使用するモバイルアプリがあり、アイデンティティソリューションの近代化が必要なため、Auth0を使用することに決めました。ただし、レガシーIDプロバイダー（IdP）からの移行に関してユーザーエクスペリエンスでの摩擦を懸念して、ユーザーに再認証を強制することは避けたいと考えています。

これを解消し、リスクを制限するために、GearUpは段階的に移行しています。それぞれのユーザーについて、レガシーIdPからのリフレッシュトークンをAuth0のアクセストークン、リフレッシュトークン、IDトークンのセットに交換することを希望しています。そうすることで、アプリが速やかにAuth0をIdPとして使い始め、Auth0発行のトークンを用いてGearUp APIを使用できるようになります。すべてのユーザーに交換が完了したら、エンドユーザーとGearUpのビジネスに影響することなく、アプリは完全に移行され、古いIdPが切断されます。

<Frame>
  <img src="https://mintcdn.com/docs-dev-docs-event-stream-action-templates/5l8GTXHD40NsjgmL/docs/images/ja-jp/cdy7uua7fh8z/2Ke6p3yZl06KT4HHqtaVu9/5d9c5feb98d614d6d793fb01ccc03e92/Screenshot_2025-02-03_at_5.00.32_PM.png?fit=max&auto=format&n=5l8GTXHD40NsjgmL&q=85&s=db623b6d76f8db81ce64c524ccb13982" alt="" width="1222" height="720" data-path="docs/images/ja-jp/cdy7uua7fh8z/2Ke6p3yZl06KT4HHqtaVu9/5d9c5feb98d614d6d793fb01ccc03e92/Screenshot_2025-02-03_at_5.00.32_PM.png" />
</Frame>

前提条件として、GearUpはAuth0テナントに[ユーザーの一括インポート](/docs/ja-jp/manage-users/user-migration/bulk-user-imports)を行い、モバイルアプリには移行するユーザーの有効なレガシーリフレッシュトークンがあります。

1. モバイルアプリがAuth0に対してレガシーのリフレッシュトークンの交換を要求します。その際にはレガシーのリフレッシュトークンをサブジェクトトークンとして設定します。
2. 該当するカスタムトークン交換プロファイルのアクションが実行されます。リフレッシュトークンをレガシーIdPで照会して、ユーザープロファイルから外部のユーザーIDを取得します。そして、必要な認可ポリシーを適用し、最終にユーザーを設定します。
3. Auth0がAuth0のアクセストークン、IDトークン、リフレッシュトークンを含めて応答します。
4. これで、ユーザーが再認証することなく、モバイルアプリがAuth0のトークンを使って顧客のAPIを使用できるようになりました。

以下のサンプルコードは、カスタムトークン交換アクションでこれを実装する方法を示しています。このユースケースではユーザープロファイルがすでにAuth0データベース接続にインポートされているため、以下が当てはまります。

* Auth0はユーザーを作成しません。
* Auth0はユーザープロファイルを更新しません。

Auth0は外部IdPのユーザーIDを使用して、該当する接続でユーザーを設定します。

```javascript lines theme={null}
/**
* Handler to be executed while executing a custom token exchange request
* @param {Event} event - Details about the incoming token exchange request.
* @param {CustomTokenExchangeAPI} api - Methods and utilities to define token exchange process.
*/
exports.onExecuteCustomTokenExchange = async (event, api) => {

 // 1. VALIDATE the refresh_token received in the subject_token by using it to get
 // the UserProfile from the external IdP
 const { isValid, user } = await getUserProfile(
   event.transaction.subject_token,
   event.secrets.CLIENT_SECRET,
 );

 if (!isValid) {
   // Mark the subject token as invalid and fail the transaction.
   api.access.rejectInvalidSubjectToken("Invalid subject_token");
 } else {
   // 2. Apply your AUTHORIZATION POLICY as required to determine if the request is valid.
   // Use api.access.deny() to reject the transaction in those cases.

   // 3. When we have the profile, we SET THE USER in the target connection
   api.authentication.setUserByConnection(
     connectionName,
     {
       // only the user_id in the connection is needed, as we are not
       // creating nor updating the user
       user_id: user.sub,
     },
     {
       creationBehavior: "none",
       updateBehavior: "none",
     },
   );
 }
};

/**
* Exchange the refresh token and load the user profile from the legacy IdP
* @param {string} refreshToken
* @param {string} clientSecret
* @returns {Promise<{ isValid: boolean, user?: object }>} If the refresh token was exchanged successfully, returns the user profile
*/
async function getUserProfile(refreshToken, clientSecret) {
 // Add your code here. REFER TO CODE SAMPLES FOR DETAILED EXAMPLES
}
```

不透明なリフレッシュトークンをレガシーIdPで検証する方法の詳しい例については、「[サンプルコード](#code-samples)」をお読みください。

### ユースケース：外部の認証プロバイダーを再利用する

別のユースケースでは、GearUpがAir0という大手旅行会社と提携し、Air0のシングルページアプリケーション内でレンタカーサービスを直接提供します。GearUpは自社APIの使用をカプセル化したJavaScriptライブラリーを提供します。そうすることで、レンタカーサービスを提供するAir0のWebサイトがGearUpのAPIを手軽に使用できるようになります。

今回も、GearUpへの再認証を避けて、ソリューションがエンドユーザーから見えないようにする必要があります。これを実現するために、GearUpのJavaScriptライブラリーは外部のAir0 IDトークンを入力として使用し、トークン交換を処理できます。その結果、Auth0のアクセストークンが生成され、メールアドレスを基に所定のGearUpユーザーと関連付けられます。GearUpライブラリーがアクセストークンを取得したら、Air0のWebサイトで直接レンタカーサービスを提供するために、GearUpのAPIを使い始めることができます。

<Frame>
  <img src="https://mintcdn.com/docs-dev-docs-event-stream-action-templates/itywf6vBRFUC2Nea/docs/images/ja-jp/cdy7uua7fh8z/34AVzwyYARK6fn2IEnLsQn/409082d736d8495b637626406977fb1f/Screenshot_2025-02-03_at_5.08.47_PM.png?fit=max&auto=format&n=itywf6vBRFUC2Nea&q=85&s=a69ab95f035723069c6901b5e66ca25e" alt="" width="1260" height="730" data-path="docs/images/ja-jp/cdy7uua7fh8z/34AVzwyYARK6fn2IEnLsQn/409082d736d8495b637626406977fb1f/Screenshot_2025-02-03_at_5.08.47_PM.png" />
</Frame>

前提条件として、GearUpはAir0 IdPをフェデレーションのエンタープライズ接続またはソーシャル接続としてセットアップし、ユーザー認証をフェデレーションログインで行うか、以下のようにカスタムトークン交換で行えるようにします。

1. ユーザーを認証したら、シングルページアプリが外部IdPからIDトークンを取得します。
2. シングルページアプリがIDトークンをサブジェクトトークンとして設定し、トークンの交換を要求します。
3. 該当するカスタムトークン交換プロファイルのアクションが実行されます。IDトークンを検証し、トークンからユーザーIDと他のプロファイル属性を取得します。そして、必要な認可ポリシーを適用し、最終にユーザーを設定します。
4. Auth0がAuth0のアクセストークン、IDトークン、リフレッシュトークンを含めて応答します。
5. これで、ユーザーが再認証することなく、SPAで実行中のJavascriptコードがAuth0のトークンを使って顧客のAPIを使用できるようになりました。

以下のサンプルコードは、カスタムトークン交換アクションでこれを実装する方法を示しています。このユースケースでは以下が当てはまります。

* Auth0は外部IdPのユーザーIDを使用して、該当する接続でユーザーを設定します。
* ユーザーが存在しない場合にはAuth0が作成します。
* ユーザーがすでに存在する場合のために、より完全な属性のセットがフェデレーションログインで取得できるのであれば、Auth0はユーザープロファイルを置換しません。
* Auth0はユーザーの作成時にメールを検証しません。

```javascript lines theme={null}
const jwksUri = "https://example.com/.well-known/jwks.json";

/**
 * Handler to be executed while executing a custom token exchange request
 * @param {Event} event - Details about the incoming token exchange request.
 * @param {CustomTokenExchangeAPI} api - Methods and utilities to define token exchange process.
 */
exports.onExecuteCustomTokenExchange = async (event, api) => {

  // 1. VALIDATE the id_token received in the subject_token
  const { isValid, payload } = await validateToken(
    event.transaction.subject_token,
  );

  if (!isValid) {
    // Mark the subject token as invalid and fail the transaction.
    api.access.rejectInvalidSubjectToken("Invalid subject_token");
  } else {
    // 2. Apply your AUTHORIZATION POLICY as required to determine if the request is valid.
    // Use api.access.deny() to reject the transaction in those cases.

    // 3. SET THE USER in the target connection.
    // We don't want to verify emails when users are created
    // This example assumes subject_token (id_token) contains standard OIDC claims. Other custom mappings
    // are also possible.
    api.authentication.setUserByConnection(
      'Enterprise-OIDC',
      {
          user_id: formattedUserId,
          email: subject_token.email,
          email_verified: subject_token.email_verified,
          phone_number: subject_token.phone_number,
          phone_verified: subject_token.phone_number_verified,
          username: subject_token.preferred_username,
          name: subject_token.name,
          given_name: subject_token.given_name,
          family_name: subject_token.family_name,
          nickname: subject_token.nickname,
          verify_email: false
      },
      {
          creationBehavior: 'create_if_not_exists',
          updateBehavior: 'none'
      }
    );
  }

  /**
   * Validate the subject token
   * @param {string} subjectToken
   * @returns {Promise<{ isValid: boolean, payload?: object }>} Payload of the token
   */
  async function validateToken(subjectToken) {
    // Add your code here. REFER TO CODE SAMPLES FOR DETAILED EXAMPLES
  }
};
```

<Tooltip data-tooltip-id="react-containers-DefinitionTooltip-3" href="/docs/ja-jp/glossary?term=json-web-token" tip="JSON Web Token（JWT）: 二者間のクレームを安全に表現するために使用される標準IDトークン形式（および多くの場合、アクセストークン形式）。" cta="用語集の表示">JWT</Tooltip>を安全に検証する方法の詳しい例については、「[サンプルコード](#code-samples)」をお読みください。

### ユースケース：別のオーディエンスのAuth0トークンを取得する

GearUpはAPI要求を処理するために、内部のマイクロサービス間で呼び出しの認可方法を改善したいと考えています。それぞれのサービスに使用可能なリソースを制御するために、ポリシーの一元管理を希望しています。これもトークン交換を使用して実現できます。

まず、API要求がサービスAに到達すると、受け取ったアクセストークンを、新しいオーディエンスとしてサービスBの使用を許可する新しいアクセストークンに交換します。トークン交換を管理する認可ポリシーが許す場合、サービスAが新しいトークンを取得して、サービスBが使用できるようになります。ユーザーIDは新しいトークンで変わらずに維持されるため、ユーザーの正しいコンテキストがプロセス全体で維持されます。

<Frame>
  <img src="https://mintcdn.com/docs-dev-docs-event-stream-action-templates/V-g8sIA_dMysRiDH/docs/images/ja-jp/cdy7uua7fh8z/5Zw7yaJGct9eHAl4rdf72D/42274a5896851a16bea402ac52037f52/Screenshot_2025-02-03_at_5.17.14_PM.png?fit=max&auto=format&n=V-g8sIA_dMysRiDH&q=85&s=ccad8efdd5ff3b6cdca02b77af5d07b5" alt="" width="1240" height="694" data-path="docs/images/ja-jp/cdy7uua7fh8z/5Zw7yaJGct9eHAl4rdf72D/42274a5896851a16bea402ac52037f52/Screenshot_2025-02-03_at_5.17.14_PM.png" />
</Frame>

GearUpアプリケーションは当初、API Aをユーザーに代わって使用するために、アクセストークンを取得します。

1. アプリが要求に当初のアクセストークンを含めてAPI Aに送信します。
2. API Aのバックエンドサービスがアクセストークンを検証し、それをサブジェクトトークンとして設定して、API Bを使用するための新しいアクセストークンとの交換を要求します。
3. 該当するカスタムトークン交換プロファイルのアクションが実行されます。IDトークンを検証し、トークンからAuth0ユーザーIDを取得します。そして、必要な認可ポリシーを適用し、最終にユーザーを設定します。
4. Auth0がAPI Bオーディエンスを使用するためのAuth0のアクセストークンを含めて応答します。
5. API Aのバックエンドサービスが新しいアクセストークンを使用してAPI Bを呼び出します。このアクセストークンは引き続き同じユーザーに関連付けられています。

以下のサンプルコードは、カスタムトークン交換アクションでこれを実装する方法を示しています。このユースケースでは以下が当てはまります。

* Auth0がAuth0のユーザーIDを使用してユーザーを設定するため、接続のスコープにこれを設定する必要はありません。
* Auth0はユーザーの作成や更新を行いません。

このユースケースに関する詳しいサンプルコードについては、「[非対称鍵で署名されたJWTを検証する](#validate-jwts-signed-with-asymmetric-keys)」を参照してください。

```javascript lines theme={null}
const jwksUri = "https://example.com/.well-known/jwks.json";

/**
 * Handler to be executed while executing a custom token exchange request
 * @param {Event} event - Details about the incoming token exchange request.
 * @param {CustomTokenExchangeAPI} api - Methods and utilities to define token exchange process.
 */
exports.onExecuteCustomTokenExchange = async (event, api) => {
  // 1. VALIDATE the access_token received in the subject_token
  const { isValid, payload } = await validateToken(
    event.transaction.subject_token,
  );

  if (!isValid) {
    // Mark the subject token as invalid and fail the transaction.
    api.access.rejectInvalidSubjectToken("Invalid subject_token");
  } else {
    // 2. Apply your AUTHORIZATION POLICY as required to determine if the request is valid.
    // Use api.access.deny() to reject the transaction in those cases.

    // 3. SET THE USER
    api.authentication.setUserById(payload.sub);
  }

  /**
   * Validate the subject token
   * @param {string} subjectToken
   * @returns {Promise<{ isValid: boolean, payload?: object }>} Payload of the token
   */
  async function validateToken(subjectToken) {
    // Add your code here. REFER TO CODE SAMPLES FOR DETAILED EXAMPLES
  }
};
```

JWTを安全に検証する方法の詳しい例については、「[サンプルコード](#code-samples)」をお読みください。

### サンプルコード

以下のサンプルコードは、受け取ったサブジェクトトークンを安全で効率よく検証するために、シナリオ共通のベストプラクティスを示しています。

Auth0とシークレットを共有する必要がない場合は、できる限り非対称のアルゴリズムと鍵を使用します。これは、適用できるk公開鍵を公表するためにJWKS URIエンドポイントを公開する場合などで、鍵のローテーションも簡素化します。

<Callout icon="file-lines" color="#0EA5E9" iconType="regular">
  強力なアルゴリズムと十分な高エントロピーのキーやシークレットで確実にサブジェクトトークンを保護することは、お客様の責任であることに注意してください。
</Callout>

#### 非対称鍵で署名されたJWTを検証する

以下の推奨事項を検討してください。

* Actionsの[api.cache](#api-cache)メソッドを使用し、トランザクションごとに署名鍵を取得しないようにします。
* [RFC8725](https://www.rfc-editor.org/rfc/rfc8725.txt)のベストプラクティスに従います。
* RS\*、PS\*、ES\*、またはEd25519のアルゴリズムを使用します。
* noneアルゴリズムを使用したり、受け入れたりしてはいけません。
* 最小長2048ビットのRSAを使用します。

```javascript lines theme={null}
const { jwtVerify } = require("jose");

const jwksUri = "https://example.com/.well-known/jwks.json";
const fetchTimeout = 5000; // 5 seconds

const validIssuer = "urn:my-issuer"; // Replace with your issuer

/**
 * Handler to be executed while executing a custom token exchange request
 * @param {Event} event - Details about the incoming token exchange request.
 * @param {CustomTokenExchangeAPI} api - Methods and utilities to define token exchange process.
 */
exports.onExecuteCustomTokenExchange = async (event, api) => {
  const { isValid, payload } = await validateToken(
    event.transaction.subject_token,
  );

  // Apply your authorization policy as required to determine if the request is valid.
  // Use api.access.deny() to reject the transaction in those cases.

  if (!isValid) {
    // Mark the subject token as invalid and fail the transaction.
    api.access.rejectInvalidSubjectToken("Invalid subject_token");
  } else {
    // Set the user in the current request as authenticated, using the user ID from the subject token.
    api.authentication.setUserById(payload.sub);
  }

  /**
   * Validate the subject token
   * @param {string} subjectToken
   * @returns {Promise<{ isValid: boolean, payload?: object }>} Payload of the token
   */
  async function validateToken(subjectToken) {
    try {
      const { payload, protectedHeader } = await jwtVerify(
        subjectToken,
        async (header) => await getPublicKey(header.kid),
        {
          issuer: validIssuer,
        },
      );

      // Perform additional validation on the token payload as required

      return { isValid: true, payload };
    } catch (/** @type {any} */ error) {
      if (error.message === "Error fetching JWKS") {
        throw new Error("Internal error - retry later");
      } else {
        console.log("Token validation failed:", error.message);
        return { isValid: false };
      }
    }
  }

  /**
   * Get the public key to use for key verification. Load from the actions cache if available, otherwise
   * fetch the key from the JWKS endpoint and store in the cache.
   * @param {string} kid - kid (Key ID) of the key to be used for verification
   * @returns {Promise<Object>}
   */
  async function getPublicKey(kid) {
    const cachedKey = api.cache.get(kid);
    if (!cachedKey) {
      console.log(`Key ${kid} not found in cache`);
      const key = await fetchKeyFromJWKS(kid);
      api.cache.set(kid, JSON.stringify(key), { ttl: 600000 });
      return key;
    } else {
      return JSON.parse(cachedKey.value);
    }
  }

  /**
   * Fetch public signing key from the provided JWKS endpoint, to use for token verification
   * @param {string} kid - kid (Key ID) of the key to be used for verification
   * @returns {Promise<object>}
   */
  async function fetchKeyFromJWKS(kid) {
    const controller = new AbortController();
    setTimeout(() => controller.abort(), fetchTimeout);

    /** @type {any} */
    const response = await fetch(jwksUri);

    if (!response.ok) {
      console.log(`Error fetching JWKS. Response status: ${response.status}`);
      throw new Error("Error fetching JWKS");
    }
    const jwks = await response.json();
    const key = jwks.keys.find((key) => key.kid === kid);
    if (!key) {
      throw new Error("Key not found in JWKS");
    }
    return key;
  }
};
```

#### 対称鍵で署名されたJWTを検証する

以下の推奨事項を検討してください。

* [Actionsシークレット](/docs/ja-jp/customize/actions/write-your-first-action#add-a-secret)を使用して、対称シークレットを安全に保管します。
* [RFC8725](https://www.rfc-editor.org/rfc/rfc8725.txt)のベストプラクティスに従います。
* HS256などのセキュリティ保護されたアルゴリズムと、高エントロピーでランダムなシークレット（256ビッドの最小長など）を使用します。

```javascript lines theme={null}
const { jwtVerify } = require("jose");

const validIssuer = "urn:my-issuer"; // Replace with your issuer

/**
 * Handler to be executed while executing a custom token exchange request
 * @param {Event} event - Details about the incoming token exchange request.
 * @param {CustomTokenExchangeAPI} api - Methods and utilities to define token exchange process.
 */
exports.onExecuteCustomTokenExchange = async (event, api) => {
  // Initialize the shared symmetric key from Actions Secrets
  const encoder = new TextEncoder();
  const symmetricKey = encoder.encode(event.secrets.SHARED_SECRET);

  const { isValid, payload } = await validateToken(
    event.transaction.subject_token,
    symmetricKey,
  );

  // Apply your authorization policy as required to determine if the request is valid.
  // Use api.access.deny() to reject the transaction in those cases.

  if (!isValid) {
    // Mark the subject token as invalid and fail the transaction.
    api.access.rejectInvalidSubjectToken("Invalid subject_token");
  } else {
    // Set the user in the current request as authenticated, using the user ID from the subject token.
    api.authentication.setUserById(payload.sub);
  }
};

/**
 * Validate the subject token
 * @param {string} subjectToken
 * @param {Uint8Array} symmetricKey
 * @returns {Promise<{ isValid: boolean, payload?: object }>} Payload of the token
 */
async function validateToken(subjectToken, symmetricKey) {
  try {
    // Validate token is correctly signed with the shared symmetric key
    // It also checks it is not expired as long as it includes an 'exp' attribute.
    const { payload, protectedHeader } = await jwtVerify(
      subjectToken,
      symmetricKey,
      {
        issuer: validIssuer,
      },
    );

    return { isValid: true, payload };
  } catch (/** @type {any} */ error) {
    console.log("Token validation failed:", error.message);
    return { isValid: false };
  }
}
```

#### 外部サービスで不透明なトークンを検証する

[Actionsシークレット](/docs/ja-jp/customize/actions/write-your-first-action#add-a-secret)を使用して、外部IdPのクライアントシークレットを安全に保管します。

```javascript lines theme={null}
const tokenEndpoint = "EXTERNAL_TOKEN_ ENDPOINT";
const userInfoEndpoint = "EXTERNAL_USER_INFO_ENDPOINT";
const clientId = "EXTERNAL_CLIENT_ID";
const connectionName = "YOUR_CONNECTION_NAME";
const fetchTimeout = 5000; // 5 seconds

/**
 * Handler to be executed while executing a custom token exchange request
 * @param {Event} event - Details about the incoming token exchange request.
 * @param {CustomTokenExchangeAPI} api - Methods and utilities to define token exchange process.
 */
exports.onExecuteCustomTokenExchange = async (event, api) => {
  const { isValid, user } = await getUserProfile(
    event.transaction.subject_token,
    event.secrets.CLIENT_SECRET,
  );

  if (!isValid) {
    // Mark the subject token as invalid and fail the transaction.
    api.access.rejectInvalidSubjectToken("Invalid subject_token");
    return;
  }

  // Apply your authorization policy as required to determine if the request is valid.
  // Use api.access.deny() to reject the transaction in those cases.

  // When we have the profile, we set the user in the target connection
  api.authentication.setUserByConnection(
    connectionName,
    {
      // only the user_id in the connection is needed, as we are not
      // creating nor updating the user
      user_id: user.sub,
    },
    {
      creationBehavior: "none",
      updateBehavior: "none",
    },
  );
};

/**
 * Exchange the refresh token and load the user profile from the legacy IdP
 * @param {string} refreshToken
 * @param {string} clientSecret
 * @returns {Promise<{ isValid: boolean, user?: object }>} If the refresh token was exchanged successfully, returns the user profile
 */
async function getUserProfile(refreshToken, clientSecret) {
  const { isValid, accessToken } = await refreshAccessToken(
    refreshToken,
    clientSecret,
  );
  if (!isValid) {
    return { isValid: false };
  }

  const controller = new AbortController();
  setTimeout(() => controller.abort(), fetchTimeout);

  /** @type {any} */
  const response = await fetch(userInfoEndpoint, {
    method: "GET",
    headers: {
      Authorization: `Bearer ${accessToken}`,
      "Content-Type": "application/json",
    },
  });

  if (!response.ok) {
    console.log(`Failed to fetch user info. Status: ${response.status}`);
    throw new Error("Error fetching user info");
  }

  const userProfile = await response.json();

  return { isValid: true, user: userProfile };
}

/**
 * Use the Refresh Token with the legacy IdP to validate it and get an access token
 * @param {string} refreshToken
 * @param {string} clientSecret
 * @returns {Promise<{ isValid: boolean, accessToken?: string }>} If the refresh token was exchanged successfully, returns the access token
 */
async function refreshAccessToken(refreshToken, clientSecret) {
  const controller = new AbortController();
  setTimeout(() => controller.abort(), fetchTimeout);

  /** @type {any} */
  let response;

  try {
    response = await fetch(tokenEndpoint, {
      method: "POST",
      headers: {
        "Content-Type": "application/x-www-form-urlencoded",
      },
      body: new URLSearchParams({
        grant_type: "refresh_token",
        refresh_token: refreshToken,
        client_id: clientId,
        client_secret: clientSecret,
      }).toString(),
    });
  } catch (error) {
    console.error("Error refreshing token");
    throw error;
  }

  if (!response.ok) {
    const errorBody = await response.json();
    console.error("Error refreshing token:", errorBody.error);

    // If we receive an error indicating the refresh token is invalid (for example, an invalid_grant error),
    // then we should explicitly indicate an invalid token using api.access.rejectInvalidSubjectToken
    // to prevent against brute force attacks on the refresh token by activating Suspicious IP Throttling.
    // For other errors which indicate a generic error making the request to the IdP, we should throw
    // an error to indicate a transient failure.
    if (errorBody.error === "invalid_grant") {
      return { isValid: false };
    } else {
      throw new Error("Error refreshing token");
    }
  }

  // Parse the response, in the form { access_token: "...", expires_in: ..., }
  const data = await response.json();
  console.log("Successfully exchanged refresh token");
  return { isValid: true, accessToken: data.access_token };
}
```

## 制限事項

これは早期アクセス機能であるため、いくつかの制限があり、他のAuth0機能との互換性はありません。

早期アクセス版のカスタムトークン交換は以下の機能には対応していません（または正しく動作しません）。

* Organizations
* <Tooltip data-tooltip-id="react-containers-DefinitionTooltip-0" href="/docs/ja-jp/glossary?term=multifactor-authentication" tip="多要素認証（MFA）: ユーザー名とパスワードに加えて、SMS経由のコードなどの要素を使用するユーザー認証プロセス。" cta="用語集の表示">MFA</Tooltip>：ログイン後アクションの`api.authentication.challengeWith()`および`api.multifactor.enable()`コマンドはカスタムトークン交換に未対応であるため、トランザクションが回復不可能なエラーで失敗します。同様に、テナントのポリシーとしてMFAが構成されている場合にも、トランザクションが失敗します。
* カスタムデータベース接続
* 特有のなりすまし対応（アクタートークンやアクタークレームなど）
* サードパーティやOIDC非準拠のクライアント

## レート制限

`/oauth/token`エンドポイントに対するカスタムトークン交換要求には、該当するパフォーマンスレベルにおいて、Authentication APIのグローバルレート制限の10%というレート制限が適用されます。

| パフォーマンスレベル              | グローバルのAuthentication API制限（RPS） | カスタムトークン交換制限（RPS） |
| ----------------------- | ------------------------------- | ----------------- |
| Enterprise              | 100                             | 10                |
| プライベートクラウドベーシック（1x）     | 100                             | 10                |
| プライベートクラウドパフォーマンス（5x）   | 500                             | 50                |
| プライベートクラウドパフォーマンス（15x）  | 1500                            | 150               |
| プライベートクラウドパフォーマンス（30x）  | 3000                            | 300               |
| プライベートクラウドパフォーマンス（60x）  | 6000                            | 600               |
| プライベートクラウドパフォーマンス（100x） | 10000                           | 1000              |

`api/v2/token-exchange-profiles`エンドポイントでの読み出し要求にも以下のレート制限が適用されます。

| パフォーマンスレベル              | カスタムトークン交換の制限（RPS） | カスタムトークン交換の制限（RPM） |
| ----------------------- | ------------------ | ------------------ |
| エンタープライズ                | 20                 | 200                |
| プライベートクラウドベーシック（1x）     | 20                 | 200                |
| プライベートクラウドパフォーマンス（5x）   | 100                | 300                |
| プライベートクラウドパフォーマンス（15x）  | 300                | 3000               |
| プライベートクラウドパフォーマンス（30x）  | 600                | 6000               |
| プライベートクラウドパフォーマンス（60x）  | 1200               | 12000              |
| プライベートクラウドパフォーマンス（100x） | 2000               | 20000              |

## エンティティ制限

テナントごとに最大100のカスタムトークン交換プロファイルを作成できます。

アクションの総数もAuth0プランに応じて制限されます。詳細については、「[Auth0の価格設定](https://auth0.com/pricing)」ページを参照してください。

## トラブルシューティング

### 「同意が必要」応答

`/oauth/token`エンドポイントを呼び出したときに、`consent_required`のエラー説明を含む`invalid_request`エラーを受け取ることがあります。

この問題を解消するには、Auth0 Dashboardを使用してAPIに\*\*［Allow Skipping User Consent（ユーザー同意のスキップを許可する）］\*\* オプションを有効にします。

<Frame>
  <img src="https://mintcdn.com/docs-dev-docs-event-stream-action-templates/oCq_HmOgBHEv-wbr/docs/images/ja-jp/cdy7uua7fh8z/4eKWTJtCQwSHUZyKU81mWm/33c1966367d2b7c157b414fa1f04c026/Screenshot_2025-02-03_at_5.36.38_PM.png?fit=max&auto=format&n=oCq_HmOgBHEv-wbr&q=85&s=6a27209cd9ee08e72b54810f87a2b32b" alt="" width="1094" height="260" data-path="docs/images/ja-jp/cdy7uua7fh8z/4eKWTJtCQwSHUZyKU81mWm/33c1966367d2b7c157b414fa1f04c026/Screenshot_2025-02-03_at_5.36.38_PM.png" />
</Frame>
