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

# APIのステップアップ認証を構成する

> APIがアクセストークンを調べて、ユーザーが多要素認証を使用してログインしているかどうかを確認する方法について説明します。

export const AuthCodeBlock = ({filename, icon, language, highlight, children}) => {
  const [displayText, setDisplayText] = useState(children);
  const [copyText, setCopyText] = useState(children);
  const wrapperRef = React.useRef(null);
  useEffect(() => {
    let unsubscribe = null;
    function init() {
      if (!window.autorun || !window.rootStore) {
        return;
      }
      unsubscribe = window.autorun(() => {
        let processedChildrenForDisplay = children;
        let processedChildrenForCopy = children;
        for (const [key, value] of window.rootStore.variableStore.values.entries()) {
          const escapedKey = key.replaceAll(/[.*+?^${}()|[\]\\]/g, (String.raw)`\$&`);
          let displayValue = value;
          if (key === "{yourClientSecret}" && value !== "{yourClientSecret}") {
            displayValue = value.substring(0, 3) + "*****MASKED*****";
          }
          processedChildrenForDisplay = processedChildrenForDisplay.replaceAll(new RegExp(escapedKey, "g"), displayValue);
          processedChildrenForCopy = processedChildrenForCopy.replaceAll(new RegExp(escapedKey, "g"), value);
        }
        setDisplayText(processedChildrenForDisplay);
        setCopyText(processedChildrenForCopy);
      });
    }
    if (window.rootStore) {
      init();
    } else {
      window.addEventListener("adu:storeReady", init);
    }
    return () => {
      window.removeEventListener("adu:storeReady", init);
      unsubscribe?.();
    };
  }, [children]);
  useEffect(() => {
    if (!wrapperRef.current) return;
    const originalWriteText = navigator.clipboard.writeText.bind(navigator.clipboard);
    let isOverriding = false;
    const handleClick = e => {
      const button = e.target.closest('[data-testid="copy-code-button"]');
      if (!button || !wrapperRef.current.contains(button)) return;
      isOverriding = true;
      navigator.clipboard.writeText = text => {
        if (isOverriding) {
          isOverriding = false;
          navigator.clipboard.writeText = originalWriteText;
          return originalWriteText(copyText);
        }
        return originalWriteText(text);
      };
      setTimeout(() => {
        if (isOverriding) {
          isOverriding = false;
          navigator.clipboard.writeText = originalWriteText;
        }
      }, 100);
    };
    const wrapper = wrapperRef.current;
    wrapper.addEventListener('click', handleClick, true);
    return () => {
      wrapper.removeEventListener('click', handleClick, true);
      if (navigator.clipboard.writeText !== originalWriteText) {
        navigator.clipboard.writeText = originalWriteText;
      }
    };
  }, [copyText]);
  return <div ref={wrapperRef}>
      <CodeBlock filename={filename} icon={icon} language={language} lines highlight={highlight}>
        {displayText}
      </CodeBlock>
    </div>;
};

export const codeExample1 = `  https://{yourDomain}/authorize?
  audience=https://my-banking-api&
  scope=openid%20view:balance%20transfer:funds&
  response_type=id_token%20token&
  client_id={yourClientId}&
  redirect_uri={https://yourApp/callback}&
  nonce=NONCE&
  state=OPAQUE_VALUE
  `;

export const codeExample2 = `  https://{yourDomain}/authorize?
  audience=https://my-banking-api&
  scope=openid%20view:balance&
  response_type=id_token%20token&
  client_id={yourClientId}&
  redirect_uri={https://yourApp/callback}&
  nonce=NONCE&
  state=OPAQUE_VALUE
  `;

export const codeExample3 = `   // set dependencies
    const express = require('express');
    const app = express();
    const jwt = require('express-jwt');
    const jwksRsa = require('jwks-rsa');
    const jwtAuthz = require('express-jwt-authz');

    // Create middleware for checking the JWT
    const checkJwt = jwt({
      // Dynamically provide a signing key based on the kid in the header and the signing keys provided by the JWKS endpoint
      secret: jwksRsa.expressJwtSecret({
        cache: true,
        rateLimit: true,
        jwksRequestsPerMinute: 5,
        jwksUri: \`https://{yourDomain}/.well-known/jwks.json\`
      }),

      // Validate the audience and the issuer
      audience: 'https://my-banking-api', // replace with your API's audience, available at Dashboard > APIs
      issuer: 'https://{yourDomain}/',
      algorithms: [ 'RS256' ] // we are using RS256 to sign our tokens
    });

    // create retrieve balance endpoint
    app.get('/balance', checkJwt, jwtAuthz(['view:balance']), function (req, res) {
      // code that retrieves the user's balance and sends it back to the calling app
      res.status(201).send({message: "This is the GET /balance endpoint"});
    });

    // create transfer funds endpoint
    app.post('/transfer', checkJwt, jwtAuthz(['transfer:funds']), function (req, res) {
      // code that transfers funds from one account to another
      res.status(201).send({message: "This is the POST /transfer endpoint"});
    });

    // launch the API Server at localhost:8080
    app.listen(8080);
    console.log('Listening on http://localhost:8080');
`;

ステップアップ認証を使用すると、さまざまな種類のリソースへのアクセスを許可するアプリケーションは、機密情報にアクセスしたり、特定のトランザクションを実行したりするために、より強力なメカニズムを使用してユーザーに認証を要求することができます。

たとえば、銀行アプリのユーザーは、多要素認証（<Tooltip data-tooltip-id="react-containers-DefinitionTooltip-0" href="/docs/ja-jp/glossary?term=multifactor-authentication" tip="多要素認証（MFA）: ユーザー名とパスワードに加えて、SMS経由のコードなどの要素を使用するユーザー認証プロセス。" cta="用語集の表示">MFA</Tooltip>）を使用してIDを確認した後にのみ、口座間で送金することができます。

対象がAPIの場合、スコープ、アクセストークン、および[アクション](/docs/ja-jp/customize/actions)を使用して、Auth0でステップアップ認証を実装できます。アプリケーションがAPIの保護されたリソースにアクセスする場合、アクセストークンを提供する必要があります。アクセスできるリソースは、アクセストークンに含まれる権限によって異なります。これらの権限は[スコープ](/docs/ja-jp/get-started/apis/scopes/api-scopes)として定義されます。

## MFAのアクセストークンを検証する

スコープの確認に加えて、APIは[アクセストークンを検証](/docs/ja-jp/secure/tokens/access-tokens/validate-access-tokens)して次の操作を行う必要があります。

* トークンの署名を検証します。これは、トークンの送信者が本人であることを確認し、メッセージが途中で変更されていないことを確認するために使用されます。
* 標準クレームを検証します。

  \| クレーム | 説明 |
  \| --- | --- |
  \| `exp` | トークンの有効期限 |
  \| `iss` | トークンの発行者 |
  \| `aud` | トークンの意図された受信者 |

## シナリオ：プッシュ通知による銀行取引

次のシナリオでは、アプリケーションがユーザー名とパスワードでユーザーを認証し、口座残高を要求します。口座残高情報を取得する前に、ユーザーはGuardianプッシュファクターで認証する必要があります。

銀行APIは、口座残高の表示（スコープ`view:balance`）または資金の振替（スコープ`transfer:funds`）という2つの異なるレベルの承認を受け入れることができます。アプリケーションがAPIにユーザーの残高の取得を要求する場合、アクセストークンには`view:balance`スコープが含まれている必要があります。別の口座に送金するには、アクセストークンに`transfer:funds`スコープが含まれている必要があります。

### ワークフロー

1. ユーザーは、ユーザー名とパスワードの認証を使用してアプリケーションにログインします。標準ログインにより、このユーザーはAPIと対話して残高を取得できます。つまり、ユーザーが認証した後にアプリが受け取るアクセストークンには、`view:balance`スコープが含まれます。
2. アプリケーションは、アクセストークンを資格情報として使用して、残高を取得する要求をAPIに送信します。
3. APIはトークンを検証し、残高情報をアプリケーションに送信して、ユーザーがそれを表示できるようにします。
4. ユーザーは、あるアカウントから別のアカウントに資金を移動したいと考えています。これは、`transfer:funds`スコープを必要とする高額トランザクションと見なされます。アプリケーションは同じアクセストークンを使用してAPIに要求を送信します。
5. APIはトークンを検証し、トークンに必要な`transfer:funds`スコープがないため、アクセスを拒否します。
6. アプリケーションはAuth0にリダイレクトします。そこで、高額スコープが要求されたため、アクションを使用してユーザーにMFAによる認証を要求します。ユーザーがMFAで正常に認証されると、正しいスコープを含む新しいアクセストークンが生成され、応答の一部としてアプリケーションに送信されます。
7. アプリケーションは、新しいアクセストークンを使用して別の資金振替要求を送信します。このトークンには、今回は`transfer:funds`スコープが含まれています。
8. APIはトークンを検証し、破棄して操作を続行します。

### 前提条件

このシナリオでは、ダッシュボードで次の項目を構成する必要があります。

* [シングルページWebアプリを登録します](/docs/ja-jp/get-started/auth0-overview/create-applications/single-page-web-apps)。
* [データベース接続を作成します](https://manage.auth0.com/#/connections/database)。
* [APIを登録します](/docs/ja-jp/get-started/auth0-overview/set-up-apis)。次の2つのスコープを作成します。`view:balance`と`transfer:funds`.
* プッシュ通知を使用するには、[MFAを有効化](/docs/ja-jp/secure/multi-factor-authentication/enable-mfa)します。

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

`transfer:funds`スコープが要求されたときに、ユーザーにMFAによる認証を要求するアクションを作成します。[［Dashbord］>［Actions（アクション）］>［Flows（フロー）］](https://manage.auth0.com/#/actions/flows)に移動し、次のコンテンツを含むアクションを作成します。

```javascript lines theme={null}
exports.onExecutePostLogin = async (event, api) => {
  const CLIENTS_WITH_MFA = ['REPLACE_WITH_{yourClientId}'];
  // run only for the specified clients
  if (CLIENTS_WITH_MFA.includes(event.client.client_id)) {
    // ask for MFA only if scope transfer:funds was requested
    if (event.transaction.requested_scopes.indexOf('transfer:funds') > -1)
      api.multifactor.enable('any', { allowRememberBrowser: false });
    }
  }
}
```

* `CLIENTS_WITH_MFA`変数には、このアクションを適用するアプリケーションのクライアントIDが含まれます。必要ない場合は、クライアントID（および後続の`if`条件）を削除できます。
* `event.transaction.requested_scopes`プロパティには、認証要求が要求したすべてのスコープが含まれます。値`transfer:funds`が含まれている場合は、`context.multifactor`プロパティを適切な値に設定してMFAを要求します。この場合、[プッシュ](/docs/ja-jp/secure/multi-factor-authentication/multi-factor-authentication-factors/configure-push-notifications-for-mfa)を使用してMFAを要求しています。

### アプリを構成

ユーザーが資金を送金するという高額取引を実行しようとしているかどうかに応じて、適切な認証要求をAPIに送信するようにアプリを構成します。2つの認証要求（MFAの有無）の唯一の違いはスコープであることに注意してください。

* MFAあり：

<AuthCodeBlock children={codeExample1} language="text" lines />

* MFAなし：

<AuthCodeBlock children={codeExample2} language="text" lines />

| パラメーター          | 設定                                                                                                                                                |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `audience`      | APIの **Identifier（識別子）** に設定する（」[API設定](https://manage.auth0.com/#/apis/)」を参照）。当方では`https://my-banking-api`に設定しています。                              |
| `response_type` | `id_token token`に設定して、応答でIDトークンとアクセストークンを両方取得するようにします。                                                                                            |
| `client_id`     | アプリケーションのクライアントIDに設定します（「[アプリケーション設定](https://manage.auth0.com/#/applications/\{yourClientId}/settings)」を参照）。                                     |
| `redirect_uri`  | 認証後にAuth0がリダイレクトで戻すアプリケーション内のURLに設定します（[アプリケーション設定](https://manage.auth0.com/#/applications/\{yourClientId}/settings)を参照）。                        |
| `nonce`         | Auth0からの応答に含まれる安全な文字列値に設定します。これは[トークンリプレイ攻撃の防止に使用](https://auth0.com/docs/ja-jp/api-auth/tutorials/nonce)され、`response_type=id_token token`には必須です。 |
| `state`         | アプリケーションにリダイレクトで戻すときにAuth0が含める不透明な値に設定します。アプリケーションはCSRF攻撃を防ぐためにこの値を使用する必要があります。                                                                   |

### APIを構成する

受信トークンを検証し、承認された権限を確認するようにAPIを構成します。

1. 次のAPIの2つのエンドポイントを構成します。
   `GET /balance`： 現在の残高を取得します
   `POST /transfer`： 資金を送金します
2. `Node.js`と次の複数のモジュールを使用します。

   1. [Express](https://expressjs.com/)： Express Webアプリケーションフレームワークを追加します。
   2. [jwks-rsa](https://github.com/auth0/node-jwks-rsa)： **JWKS** （JSON Web Key Set）エンドポイントからRSA署名キーを取得します。`expressJwtSecret`を使用すると、JWTヘッダーの`kid`に基づいて`express-jwt`に適切な署名キーを発行するシークレットプロバイダーを生成できます。
   3. [express-jwt](https://github.com/auth0/express-jwt): Node.jsアプリケーションでJWTトークンを使用してHTTP要求を認証できます。JWTを使用した作業を容易にする、いくつかの機能が提供されています。
   4. [express-jwt-authz](https://github.com/auth0/express-jwt-authz)： アクセストークンに特定のスコープが含まれているかどうかを確認します。
3. 次の依存関係をインストールします。
   `npm install express express-jwt jwks-rsa express-jwt-authz --save`
4. APIエンドポイントを定義し、アクセストークンを検証するミドルウェア関数を作成し、そのミドルウェアを使用してエンドポイントを保護します。`server.js`ファイル内のコードは、次のサンプルスクリプトのようになります。

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

APIが要求を受信するたびに、次の処理が行われます。

1. エンドポイントは`checkJwt`ミドルウェアを呼び出します。
2. `express-jwt`はトークンをデコードし、要求、ヘッダー、ペイロードを`jwksRsa.expressJwtSecret`に渡します。
3. `jwks-rsa`はJWKSエンドポイントからすべての署名キーをダウンロードし、署名キーの1つがアクセストークンのヘッダー内の`kid`と一致するかどうかを確認します。署名キーのいずれも受信`kid`と一致しない場合は、エラーがスローされます。一致する場合は、正しい署名キーを`express-jwt`に渡します。
4. `express-jwt`は独自のロジックを続行して、トークンの署名、有効期限、対象ユーザー、発行者を検証します。
5. `jwtAuthz`は、エンドポイントに必要なスコープがアクセストークンの一部であるかどうかを確認します。指定されたスコープがアクセストークンにない場合、要求は403エラーメッセージで拒否されます。

## もっと詳しく

* [アクセストークン](/docs/ja-jp/secure/tokens/access-tokens)
* [アクセストークンを検証する](/docs/ja-jp/secure/tokens/access-tokens/validate-access-tokens)
* [Actionのユースケース](/docs/ja-jp/customize/actions/use-cases)
* [APIスコープ](/docs/ja-jp/get-started/apis/scopes/api-scopes)
* [Webアプリにステップアップ認証を設定する](/docs/ja-jp/secure/multi-factor-authentication/step-up-authentication/configure-step-up-authentication-for-web-apps)
