メインコンテンツへスキップ
このドキュメントは Mobile + API Architecture Scenario の一部であり、Node.js で API を実装する方法を説明します。Node.js API 実装の完全なソースコードは、この GitHub リポジトリにあります。 実装したソリューションの詳細については、このシナリオを参照してください。
この実装では、Express Web アプリケーションフレームワーク を使用して Node.js API を構築します。
package.json ファイルを作成する
API 用のフォルダーを作成し、そのディレクトリに移動して npm init を実行します。これにより、package.json ファイルが作成されます。デフォルト設定のままでも、必要に応じて変更してもかまいません。サンプルの package.json は次のとおりです。
{
  "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 アプリケーションフレームワークを追加します。
  • cors: このモジュールは、CORS を有効にするためのサポートを追加します。これは、Web ブラウザー内の別のドメインで実行されるシングルページアプリケーションから API が呼び出されるため、必要です。
  • jwks-rsa: このライブラリは、JWKS (JSON Web Key Set) エンドポイントから RSA 署名鍵を取得します。expressJwtSecret を使用すると、JWT ヘッダー内の kid に基づいて適切な署名鍵を express-jwt に提供するシークレットプロバイダーを生成できます。詳しくは、node-jwks-rsa GitHub リポジトリを参照してください。
  • express-jwt: このモジュールは、Node.js アプリケーションで JWT トークンを使用して HTTP リクエストを認証します。JWT を簡単に扱うための複数の関数を提供します。詳しくは、express-jwt GitHub リポジトリを参照してください。
  • body-parser: これは Node.js のボディ解析ミドルウェアです。受信リクエストストリームの本文全体を抽出し、扱いやすい形で req.body として利用できるようにします。
これらの依存関係をインストールするには、次を実行します。
npm install express cors express-jwt jwks-rsa body-parser express-jwt-authz --save
エンドポイントを実装する
API ディレクトリに移動し、server.js ファイルを作成します。コードでは次のことを行います。
  • 依存関係を追加する。
  • エンドポイントを実装する。
  • API サーバーを起動する。
以下は実装例です。
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 レスポンスが表示されるはずです。これでエンドポイントはできましたが、現時点では誰でも呼び出せます。これをどのように修正できるかを確認するには、次の手順に進んでください。
トークンを検証するには、express-jwt middleware が提供する jwt 関数と、シークレットの取得に使用する jwks-rsa を利用します。これらのライブラリは次の処理を実行します。
  1. express-jwt はトークンをデコードし、リクエスト、ヘッダー、ペイロードを jwksRsa.expressJwtSecret に渡します。
  2. jwks-rsa は JWKS エンドポイントからすべての署名鍵をダウンロードし、その中に JWT ヘッダー内の kid と一致するものがあるかを確認します。受信した kid と一致する署名鍵がない場合は、エラーがスローされます。一致するものが見つかった場合は、適切な署名鍵を express-jwt に渡します。
  3. express-jwt は引き続き独自のロジックで、トークンの署名、有効期限、audienceissuer を検証します。
コードでは、次の手順に従います。
  • アクセストークンを検証するミドルウェア関数を作成します。
  • ルートでこのミドルウェアを有効にします。
実際にタイムシートをデータベースに保存するコードを追加することもできます。以下はサンプル実装です (一部のコードは簡潔さのため省略しています) 。ここでサーバーを起動し、localhost:8080/timesheets に HTTP POST を送信すると、エラーメッセージ Missing or invalid token が返されるはずです (リクエストでアクセストークンを送信していないため、これは正しい動作です) 。正常に動作するケースもテストするには、次の作業が必要です。
  • アクセストークンを取得します。取得方法の詳細については、Get an Access Token を参照してください。
  • リクエストに Authorization ヘッダーを追加し、その値を Bearer ACCESS_TOKEN に設定して API を呼び出します (ACCESS_TOKEN には、最初の手順で取得したトークンの値を指定します) 。
このステップでは、アプリケーションに必要な権限 (またはスコープ) があるかどうかを確認し、エンドポイントを使ってタイムシートを作成できるようにします。特に、トークンに正しいスコープである batch:upload が含まれていることを確認します。これを行うには、express-jwt-authz Node.js パッケージを使用するので、これをプロジェクトに追加します。
npm install express-jwt-authz --save
次に、特定のエンドポイントを呼び出す際に、JWT に特定のスコープが含まれていることを確認するため、ミドルウェアに jwtAuthz(...) の呼び出しを追加します。追加の依存関係が必要です。express-jwt-authz ライブラリは express-jwt と組み合わせて使用され、JWT を検証するとともに、目的のエンドポイントを呼び出すために必要な適切な権限が含まれていることを確認します。詳細については、express-jwt-authz GitHub repository を参照してください。以下は実装例です (一部のコードは簡潔にするため省略しています) :
// 依存関係を設定 - 一部のコードは省略
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 からこのスコープを削除して確認できます。
JWT の検証に使用する express-jwt ミドルウェアでは、JWT に含まれる情報を req.user にも設定します。ユーザーを一意に識別するために sub クレームを使用する場合は、req.user.sub を利用できます。timesheets アプリケーションでは、一意の識別子としてユーザーのメールアドレスを使用します。
Action を作成する
まず、ユーザーのメールアドレスをアクセストークンに追加する新しい Action を作成します。
  1. Auth0 Dashboard > Actions > Library に移動し、Build Custom を選択します。
  2. Action の説明的な Name (例: Add email to access token) を入力し、Login / Post Login トリガーを選択して、Create を選択します。
  3. Actions Code Editor を開き、次の JavaScript コードをコピーして貼り付け、Save Draft を選択して変更を保存します。
    exports.onExecutePostLogin = async (event, api) => {
      const namespace = 'https://my-app.example.com';
      api.accessToken.setCustomClaim(`${namespace}/email`, event.user.email);
    }
    
    namespace は、標準の OIDC クレームや内部サービスと競合しない一意のクレーム名にするために使用します。名前空間付きクレームおよび名前空間なしクレームの制限やガイドラインについて詳しくは、Create Custom Claims を参照してください。
  4. Actions Code Editor のサイドバーで Test (再生アイコン) を選択し、次に Run を選択してコードをテストします。
  5. Action を公開する準備ができたら、Deploy を選択します。
Action を Post-Login Trigger に追加する
次に、作成した Action を Post-Login Trigger に追加します。Actions を Trigger にアタッチする方法について詳しくは、Write Your First Action を参照してください。
一意の識別子を取得する
最後に、API 内で req.auth からクレームの値を取得します。その値を、タイムシートのエントリに関連付ける一意のユーザー識別子として使用します。
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);
});