メインコンテンツへスキップ

Node.js API 実装 (SPAs + API)

このドキュメントは SPA + API アーキテクチャシナリオ の一部であり、Node.js で API を実装する方法について説明します。実装するソリューションについては、このシナリオを参照してください。 Node.js API 実装の完全なソースコードは、こちらの GitHub リポジトリ にあります。

ステップ 1. API エンドポイントを定義する

Node.js API の構築には、Express web application framework を使用します。

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": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/auth0-samples/auth0-pnp-timesheets.git"
  },
  "author": "Auth0",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/auth0-samples/auth0-pnp-timesheets/issues"
  },
  "homepage": "https://github.com/auth0-samples/auth0-pnp-timesheets#readme"
}

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

次に、依存関係を設定します。使用するモジュールは次のとおりです。
  • express: このモジュールは、Express web application framework を追加します。
  • cors: このモジュールは、CORS を有効にするためのサポートを追加します。これは、Web ブラウザー内の別のドメインで実行されるシングルページアプリケーションから API が呼び出されるため、必要です。
  • jwks-rsa: このライブラリは、JWKS (JSON Web Key Set) エンドポイントから RSA 署名鍵を取得します。expressJwtSecret を使用すると、 ヘッダー内の kid に基づいて、適切な署名鍵を express-jwt に提供するシークレットプロバイダーを生成できます。詳しくは、node-jwks-rsa GitHub repository を参照してください。
  • express-jwt: このモジュールを使用すると、Node.js アプリケーションで JWT を使って HTTP リクエストを認証できます。JWT を扱いやすくするための複数の関数も提供されています。詳しくは、express-jwt GitHub repository を参照してください。
  • body-parser: これは Node.js のリクエストボディを解析する middleware です。受信したリクエストストリームのボディ全体を抽出し、扱いやすい形で req.body として公開します。詳しい情報といくつかの代替手段については、body-parser GitHub repository を参照してください。
これらの依存関係をインストールするには、次を実行します。
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"});
})

// APIサーバーをlocalhost:8080で起動する
app.listen(8080);
node serverで API サーバーを起動し、localhost:8080/timesheets に HTTP POST リクエストを送信します。This is the POST /timesheets endpoint というメッセージを含む JSON レスポンスが返されるはずです。 これでエンドポイントは用意できましたが、現時点では誰でも呼び出せてしまいます。これをどのように解決するかは、次の段落で確認してください。

ステップ 2. API エンドポイントを保護する

トークンを検証するために、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 は最初の手順で取得したトークンの値です) 。

ステップ 3. アプリケーションの権限を確認する

このステップでは、タイムシートを作成するために、アプリケーションがエンドポイントを使用するための権限 (またはscope) を持っているかどうかを確認する機能を実装に追加します。特に、トークンに正しいスコープ (batch:upload) が含まれていることを確認します。 これを行うには、express-jwt-authz Node.js パッケージを使用するため、プロジェクトに追加してください。
npm install express-jwt-authz --save
これで、特定のエンドポイントにアクセスするために必要なスコープが JWT に含まれていることを確認するには、ミドルウェアに jwtAuthz(...) の呼び出しを追加するだけです。 追加の依存関係を 1 つ追加します。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);
})

// APIサーバーをlocalhost:8080で起動 - コードは省略
このスコープを含まないトークンで API を呼び出すと、HTTP ステータスコード 403 のエラーメッセージ Forbidden が返されるはずです。これを確認するには、API からこのスコープを削除してください。

ステップ 4. ユーザーの識別情報を特定する

JWT の検証に使用する express-jwt ミドルウェアは、JWT に含まれる情報を req.auth にも設定します。ユーザーを一意に識別するために sub クレームを使用する場合は、req.auth.sub をそのまま使用できます。 ただし、このタイムシートアプリケーションでは、一意の識別子としてユーザーのメールアドレスを使用します。 まず、ユーザーのメールアドレスをアクセストークンに追加する Rule を作成する必要があります。Dashboard の Rules section に移動し、Create Rule ボタンをクリックします。 Rule には、たとえば Add email to Access Token のようなわかりやすい名前を付け、次のコードを使用します。
function (user, context, callback) {
  const namespace = 'https://api.exampleco.com/';
  context.accessToken[namespace + 'email'] = user.email;
  callback(null, user, context);
}
namespace は、クレームに一意の名前を付け、標準の OIDC クレーム名と競合しないようにするために使用されます。ただし、Auth0 は名前空間付きと名前空間なしの両方のカスタムクレームをサポートしています。カスタムクレームの詳細については、Create Custom Claims を参照してください。 次に、API 内で req.auth からクレームの値を取得し、それをタイムシートのエントリに関連付ける一意のユーザー ID として使用できます。
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);
});