> ## 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) + "*****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 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 と issuer を検証
  audience: '{YOUR_API_IDENTIFIER}', // Dashboard > APIs で確認できる API の audience に置き換えてください
  issuer: 'https://{yourDomain}/',
  algorithms: [ 'RS256' ]
});

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

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

  // timesheet をデータベースに保存...

  // レスポンスを送信
  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エンドポイントを定義する">
    この実装では、Node.js API の構築に [Express Webアプリケーションフレームワーク](http://expressjs.com/) を使用します。

    ##### 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 application framework](https://expressjs.com/) を追加します。
    * **cors**: このモジュールは、[CORS](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing) を有効にするためのサポートを追加します。API は、Web ブラウザー内の別ドメインで動作するシングルページアプリケーションから呼び出されるため、これが必要です。
    * **jwks-rsa**: このライブラリは、JWKS (JSON Web Key Set) endpoint から RSA 署名鍵を取得します。`expressJwtSecret` を使用すると、JWT ヘッダー内の `kid` に基づいて、`express-jwt` に適切な署名鍵を提供するシークレットプロバイダーを生成できます。詳しくは、[node-jwks-rsa GitHub repository](https://github.com/auth0/node-jwks-rsa) を参照してください。
    * **express-jwt**: このモジュールは、Node.js アプリケーションで JWT トークンを使って HTTP リクエストを認証します。JWT をより簡単に扱えるようにするための複数の関数を提供します。詳しくは、[express-jwt GitHub repository](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` と一致するものがあるかを確認します。一致する署名鍵がなければエラーがスローされます。一致するものがあれば、正しい署名鍵を `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](/docs/ja-jp/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(...)` の呼び出しを追加します。

    依存関係をもう 1 つ追加します。**express-jwt-authz** ライブラリは express-jwt と組み合わせて使用され、[JWT](/docs/ja-jp/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` claim を使いたい場合は、`req.user.sub` を使用できます。timesheets アプリケーションでは、一意の識別子としてユーザーのメールアドレスを使用します。

    ##### Action を作成する

    まず、ユーザーのメールアドレスを access token に追加する[新しい Action を作成](/docs/ja-jp/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` は、claim 名が標準の OIDC claim や内部サービスと競合しないよう、一意にするために使用されます。名前空間付きおよび名前空間なしの claim に関する制限やガイドラインについて詳しくは、[Create Custom Claims](/docs/ja-jp/secure/tokens/json-web-tokens/create-custom-claims) を参照してください。
           </Callout>
    4. Actions Code Editor のサイドバーで Test (再生アイコン) を選択し、**Run** を選択して[コードをテスト](/docs/ja-jp/customize/actions/test-actions)します。
    5. Action を公開する準備ができたら、**Deploy** を選択します。

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

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

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

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

    ```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>
