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

> モバイルアプリ + API アーキテクチャシナリオ向けの API の Node.js 実装

# Node.js 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) + "*****マスク済み*****";
          }
          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 codeExample = `// 依存関係を設定 - コードは省略

// CORS を有効にする - コードは省略

// JWT を検証するミドルウェアを作成
const checkJwt = jwt({
  // ヘッダー内の kid と JWKS エンドポイントで提供される署名キーに基づいて、署名キーを動的に取得
  secret: jwksRsa.expressJwtSecret({
    cache: true,
    rateLimit: true,
    jwksRequestsPerMinute: 5,
    jwksUri: \`https://{yourDomain}/.well-known/jwks.json\`
  }),

  // オーディエンスと発行者を検証
  audience: '{YOUR_API_IDENTIFIER}', // Dashboard > APIs で確認できる API のオーディエンスに置き換えてください
  issuer: 'https://{yourDomain}/',
  algorithms: [ 'RS256' ]
});

// リクエストボディを解析するミドルウェアの使用を有効にする - コードは省略

// timesheets API エンドポイントを作成 - コードは省略
app.post('/timesheets', checkJwt, function(req, res){
  var timesheet = req.body;

  // タイムシートをデータベースに保存...

  // レスポンスを送信
  res.status(201).send(timesheet);
});
// localhost:8080 で API サーバーを起動 - コードは省略`;

このドキュメントは Mobile + API Architecture Scenario の一部であり、Node.js で API を実装する方法を説明します。Node.js API 実装の完全なソースコードは、[この GitHub リポジトリ](https://github.com/auth0-samples/auth0-pnp-exampleco-timesheets/tree/master/timesheets-api/node)にあります。

実装したソリューションの詳細については、このシナリオを参照してください。

<AccordionGroup>
  <Accordion title="1 API エンドポイントを定義する">
    この実装では、[Express Web アプリケーションフレームワーク](http://expressjs.com/) を使用して Node.js API を構築します。

    ##### package.json ファイルを作成する

    API 用のフォルダーを作成し、そのディレクトリに移動して `npm init` を実行します。これにより、`package.json` ファイルが作成されます。

    デフォルト設定のままでも、必要に応じて変更してもかまいません。

    サンプルの `package.json` は次のとおりです。

    ```json lines theme={null}
    {
      "name": "timesheets-api",
      "version": "1.0.0",
      "description": "API used to add timesheet entries for employees and contractors",
      "main": "index.js",
      "scripts": {
        "start": "node index.js",
        "test": "echo \"Error: no test specified\" && exit 1"
      },
      "dependencies": {
        "body-parser": "^1.20.0",
        "cors": "^2.8.5",
        "express": "^4.18.0",
        "express-oauth2-jwt-bearer": "^1.6.0"
      },
      "author": "Auth0",
      "license": "MIT"
    }
    ```

    ##### 依存関係をインストールする

    次に、以下のモジュールを依存関係として設定します。

    * **express**: このモジュールは、[Express Web アプリケーションフレームワーク](https://expressjs.com/)を追加します。
    * **cors**: このモジュールは、[CORS](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing) を有効にするためのサポートを追加します。これは、Web ブラウザー内の別のドメインで実行されるシングルページアプリケーションから API が呼び出されるため、必要です。
    * **jwks-rsa**: このライブラリは、JWKS (JSON Web Key Set) エンドポイントから RSA 署名鍵を取得します。`expressJwtSecret` を使用すると、JWT ヘッダー内の `kid` に基づいて適切な署名鍵を `express-jwt` に提供するシークレットプロバイダーを生成できます。詳しくは、[node-jwks-rsa GitHub リポジトリ](https://github.com/auth0/node-jwks-rsa)を参照してください。
    * **express-jwt**: このモジュールは、Node.js アプリケーションで JWT トークンを使用して HTTP リクエストを認証します。JWT を簡単に扱うための複数の関数を提供します。詳しくは、[express-jwt GitHub リポジトリ](https://github.com/auth0/express-jwt)を参照してください。
    * **body-parser**: これは Node.js のボディ解析ミドルウェアです。受信リクエストストリームの本文全体を抽出し、扱いやすい形で `req.body` として利用できるようにします。

    これらの依存関係をインストールするには、次を実行します。

    ```bash wrap lines theme={null}
    npm install express cors express-jwt jwks-rsa body-parser express-jwt-authz --save
    ```

    ##### エンドポイントを実装する

    API ディレクトリに移動し、`server.js` ファイルを作成します。コードでは次のことを行います。

    * 依存関係を追加する。
    * エンドポイントを実装する。
    * API サーバーを起動する。

    以下は実装例です。

    ```javascript lines theme={null}
    const express = require('express');
    const app = express();
    const { expressjwt: jwt } = require('express-jwt');
    const jwksRsa = require('jwks-rsa');
    const cors = require('cors');
    const bodyParser = require('body-parser');

    // CORSを有効にする
    app.use(cors());

    // リクエストボディ解析ミドルウェアを有効にする
    app.use(bodyParser.json());
    app.use(bodyParser.urlencoded({
      extended: true
    }));

    // タイムシートAPIエンドポイントを作成する
    app.post('/timesheets', function(req, res){
      res.status(201).send({message: "This is the POST /timesheets endpoint"});
    })

    // localhost:8080でAPIサーバーを起動する
    app.listen(8080);
    ```

    `node server` を使用して API サーバーを起動し、`localhost:8080/timesheets` に HTTP POST リクエストを送信します。`This is the POST /timesheets endpoint` というメッセージを含む JSON レスポンスが表示されるはずです。

    これでエンドポイントはできましたが、現時点では誰でも呼び出せます。これをどのように修正できるかを確認するには、次の手順に進んでください。
  </Accordion>

  <Accordion title="2 API エンドポイントを保護する">
    トークンを検証するには、[express-jwt middleware](https://github.com/auth0/express-jwt#usage) が提供する `jwt` 関数と、シークレットの取得に使用する `jwks-rsa` を利用します。これらのライブラリは次の処理を実行します。

    1. `express-jwt` はトークンをデコードし、リクエスト、ヘッダー、ペイロードを `jwksRsa.expressJwtSecret` に渡します。
    2. `jwks-rsa` は JWKS エンドポイントからすべての署名鍵をダウンロードし、その中に JWT ヘッダー内の `kid` と一致するものがあるかを確認します。受信した `kid` と一致する署名鍵がない場合は、エラーがスローされます。一致するものが見つかった場合は、適切な署名鍵を `express-jwt` に渡します。
    3. `express-jwt` は引き続き独自のロジックで、トークンの署名、有効期限、`audience`、`issuer` を検証します。

    コードでは、次の手順に従います。

    * アクセストークンを検証するミドルウェア関数を作成します。
    * ルートでこのミドルウェアを有効にします。

    実際にタイムシートをデータベースに保存するコードを追加することもできます。以下はサンプル実装です (一部のコードは簡潔さのため省略しています) 。

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

    ここでサーバーを起動し、`localhost:8080/timesheets` に HTTP POST を送信すると、エラーメッセージ `Missing or invalid token` が返されるはずです (リクエストでアクセストークンを送信していないため、これは正しい動作です) 。

    正常に動作するケースもテストするには、次の作業が必要です。

    * アクセストークンを取得します。取得方法の詳細については、[Get an Access Token](/ja/docs/secure/tokens/access-tokens/get-access-tokens) を参照してください。
    * リクエストに `Authorization` ヘッダーを追加し、その値を `Bearer ACCESS_TOKEN` に設定して API を呼び出します (`ACCESS_TOKEN` には、最初の手順で取得したトークンの値を指定します) 。
  </Accordion>

  <Accordion title="3 アプリの権限を確認する">
    このステップでは、アプリケーションに必要な権限 (またはスコープ) があるかどうかを確認し、エンドポイントを使ってタイムシートを作成できるようにします。特に、トークンに正しいスコープである `batch:upload` が含まれていることを確認します。

    これを行うには、`express-jwt-authz` Node.js パッケージを使用するので、これをプロジェクトに追加します。

    ```bash lines theme={null}
    npm install express-jwt-authz --save
    ```

    次に、特定のエンドポイントを呼び出す際に、JWT に特定のスコープが含まれていることを確認するため、ミドルウェアに `jwtAuthz(...)` の呼び出しを追加します。

    追加の依存関係が必要です。**express-jwt-authz** ライブラリは express-jwt と組み合わせて使用され、[JWT](/ja/docs/secure/tokens/json-web-tokens) を検証するとともに、目的のエンドポイントを呼び出すために必要な適切な権限が含まれていることを確認します。詳細については、[express-jwt-authz GitHub repository](https://github.com/auth0/express-jwt-authz) を参照してください。

    以下は実装例です (一部のコードは簡潔にするため省略しています) :

    ```javascript lines theme={null}
    // 依存関係を設定 - 一部のコードは省略
    const jwtAuthz = require('express-jwt-authz');

    // CORSを有効化 - コードは省略

    // JWTを検証するミドルウェアを作成 - コードは省略

    // リクエストボディ解析ミドルウェアの使用を有効化 - コードは省略

    // タイムシートAPIエンドポイントを作成
    app.post('/timesheets', checkJwt, jwtAuthz(['create:timesheets'], { customUserKey: 'auth' }), function(req, res){
      var timesheet = req.body;

      // タイムシートをデータベースに保存...

      // レスポンスを送信
      res.status(201).send(timesheet);
    })

    // localhost:8080 でAPIサーバーを起動 - コードは省略
    ```

    このスコープを含まないトークンで API を呼び出すと、HTTP ステータスコード `403` のエラーメッセージ Forbidden が返されるはずです。これは、API からこのスコープを削除して確認できます。
  </Accordion>

  <Accordion title="4 ユーザーの本人確認">
    JWT の検証に使用する `express-jwt` ミドルウェアでは、JWT に含まれる情報を `req.user` にも設定します。ユーザーを一意に識別するために `sub` クレームを使用する場合は、`req.user.sub` を利用できます。timesheets アプリケーションでは、一意の識別子としてユーザーのメールアドレスを使用します。

    ##### Action を作成する

    まず、ユーザーのメールアドレスをアクセストークンに追加する[新しい Action を作成](/ja/docs/customize/actions/write-your-first-action)します。

    1. [Auth0 Dashboard > Actions > Library](https://manage.auth0.com/#/actions/library) に移動し、**Build Custom** を選択します。
    2. Action の説明的な **Name** (例: `Add email to access token`) を入力し、**Login / Post Login** トリガーを選択して、**Create** を選択します。
    3. Actions Code Editor を開き、次の JavaScript コードをコピーして貼り付け、**Save Draft** を選択して変更を保存します。

       ```text lines theme={null}
       exports.onExecutePostLogin = async (event, api) => {
         const namespace = 'https://my-app.example.com';
         api.accessToken.setCustomClaim(`${namespace}/email`, event.user.email);
       }
       ```

           <Callout icon="file-lines" color="#0EA5E9" iconType="regular">
             `namespace` は、標準の OIDC クレームや内部サービスと競合しない一意のクレーム名にするために使用します。名前空間付きクレームおよび名前空間なしクレームの制限やガイドラインについて詳しくは、[Create Custom Claims](/ja/docs/secure/tokens/json-web-tokens/create-custom-claims) を参照してください。
           </Callout>
    4. Actions Code Editor のサイドバーで Test (再生アイコン) を選択し、次に **Run** を選択して[コードをテスト](/ja/docs/customize/actions/test-actions)します。
    5. Action を公開する準備ができたら、**Deploy** を選択します。

    ##### Action を Post-Login Trigger に追加する

    次に、作成した Action を [Post-Login Trigger](https://manage.auth0.com/#/actions/triggers/post-login/) に追加します。Actions を Trigger にアタッチする方法について詳しくは、[Write Your First Action](/ja/docs/customize/actions/write-your-first-action) を参照してください。

    ##### 一意の識別子を取得する

    最後に、API 内で `req.auth` からクレームの値を取得します。その値を、タイムシートのエントリに関連付ける一意のユーザー識別子として使用します。

    ```js lines theme={null}
    app.get('/timesheets', checkJwt, jwtAuthz(['read:timesheets'], { customUserKey: 'auth' }), function(req, res) {
      var timesheet = req.body;

      // タイムシートエントリを現在のユーザーに関連付ける
      var userId = req.auth['https://api.exampleco.com/email'];
      timesheet.user_id = userId;

      // タイムシートをデータベースに保存する...

      //レスポンスを送信する
      res.status(201).send(timesheet);
    });
    ```
  </Accordion>
</AccordionGroup>
