メインコンテンツへスキップ
本ドキュメントはモバイル + APIアーキテクチャーシナリオの一部で、APIをNode.jsで実装する方法を説明します。Node.jsでのAPI実装で使用する全ソースコードは、こちらのGitHubリポジトリでご覧いただけます。 実装したソリューションについての情報は、シナリオを参照してください。 accordion.expand_all/accordion.collapse_all
この実装では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": {
    "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アプリケーションフレームワークを追加します。
  • cors :このモジュールはCORSを有効にするためのサポートを追加します。これは、APIがWebブラウザー内の別のドメインで実行されるシングルページアプリケーションから呼び出されるために必要です。
  • 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');

// Enable CORS
app.use(cors());

// Enable the use of request body parsing middleware
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
  extended: true
}));

// Create timesheets API endpoint
app.post('/timesheets', function(req, res){
  res.status(201).send({message: "This is the POST /timesheets endpoint"});
})

// Launch the API Server at localhost:8080
app.listen(8080);
node serverを使用してAPIサーバーを起動し、localhost:8080/timesheetsにHTTP POST要求を行います。This is the POST /timesheets endpointというメッセージを含むJSON応答が表示されます。これでエンドポイントが入手できましたが、それは誰でも呼び出すことができます。次のステップに進んで、この問題を解決する方法を確認します。
トークンを検証するには、express-jwtミドルウェアが提供するjwt関数とjwks-rsaを使用してシークレットを取得します。ライブラリーは以下を行います。
  1. express-jwtはトークンをデコードし、要求、ヘッダー、ペイロードをjwksRsa.expressJwtSecretに渡します。
  2. jwks-rsaはJWKSエンドポイントからすべての署名鍵をダウンロードし、署名鍵の1つがJWTヘッダーのkidと一致するかどうかを確認します。どの署名鍵も受け取ったkidに一致しない場合、エラーがスローされます。一致するものがあれば、express-jwtに正しい署名鍵を渡します。
  3. express-jwtはトークンの署名、有効期限、audienceissuerを検証する独自のロジックを継続します。
コードでは次の手順に従います。
  • アクセストークンを検証するミドルウェア関数を作成する。
  • ルートでミドルウェアを使用できるようにする。
また、実際にタイムシートをデータベースに保存するコードを書くこともできます。以下はサンプル実装を示します(簡潔にするために一部のコードは省略しています)。
// set dependencies - code omitted

// Enable CORS - code omitted

// 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: '{YOUR_API_IDENTIFIER}', //replace with your API's audience, available at Dashboard > APIs
  issuer: 'https://{yourDomain}/',
  algorithms: [ 'RS256' ]
});

// Enable the use of request body parsing middleware - code omitted

// create timesheets API endpoint - code omitted
app.post('/timesheets', checkJwt, function(req, res){
  var timesheet = req.body;

  // Save the timesheet to the database...

  //send the response
  res.status(201).send(timesheet);
});
// launch the API Server at localhost:8080 - code omitted
ここでサーバーを起動してlocalhost:8080/timesheetsにHTTP POSTを実行すると、エラーメッセージMissing or invalid tokenが表示されるはずです(要求でアクセストークンを送信しなかったためこれは正確です)。また、作業シナリオをテストするためには、次を実行する必要があります。
  • アクセストークンを取得する。詳しい取得方法については、「アクセストークンを取得する」を参照してください。
  • APIを呼び出し、値Bearer ACCESS_TOKENを指定してAuthorizationヘッダーを要求に追加します(ACCESS_TOKENは最初のステップで取得したトークンの値)。
このステップでは、アプリケーションに権限(またはスコープ)があるかを確認する機能を追加して、タイムシートを作成するためにエンドポイントを使用します。特に、トークンのスコープが正しくbatch:uploadであることを確認することが目的です。そのためには、express-jwt-authz Node.jsパッケージを利用するので、それをプロジェクトに追加します。
npm install express-jwt-authz --save
ここでjwtAuthz (...)への呼び出しをミドルウェアに追加して、特定のエンドポイントを実行するために、特定のスコープがJWTに含まれていることを確認します。依存関係を追加します。express-jwt-authz ライブラリーはexpress-jwtと組み合わせて使用され、JWTを検証し、目的のエンドポイントを呼び出すための正しい権限を持っていることを保証します。詳細については、「express-jwt-authz GitHubリポジトリ」を参照してください。以下はサンプル実装を示します(簡潔にするために一部のコードは省略しています)。
// set dependencies - some code omitted
const jwtAuthz = require('express-jwt-authz');

// Enable CORS - code omitted

// Create middleware for checking the JWT - code omitted

// Enable the use of request body parsing middleware - code omitted

// create timesheets API endpoint
app.post('/timesheets', checkJwt, jwtAuthz(['create:timesheets'], { customUserKey: 'auth' }), function(req, res){
  var timesheet = req.body;

  // Save the timesheet to the database...

  //send the response
  res.status(201).send(timesheet);
})

// launch the API Server at localhost:8080 - code omitted
このスコープを含まないトークンを使用してAPIを呼び出すと、HTTPステータスコード403の「Forbidden(禁止)」というエラーメッセージが表示されます。APIからこのスコープを削除と、これをテストすることができます。
JWTの検証に使われるexpress-jwtミドルウェアは、JWTに含まれる情報をreq.userに設定します。subクレームを使用してユーザーを一意に識別したい場合は、req.user.subを使用できます。タイムシートアプリケーションでは、ユーザーのメールアドレスを一意の識別子として使用することが目的です。
アクションを作成する
まず、アクセストークンにユーザーのメールアドレスを追加するアクションを新規作成します。
  1. [Auth0 Dashboard] > [Actions(アクション)] > [Library(ライブラリー)]に移動し、[Build Custom(カスタム作成)] を選択します。
  2. アクションの説明的な [Name(名前)] を入力し(たとえば、Add email to access token)、[Login / Post-Login(ログイン/ログイン後)] トリガー、[Create(作成)] の順に選択します。
  3. Actionsコードエディターを見つけて、次の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クレームや内部サービスと衝突しない、一意のものになります。名前空間のあるクレームと名前空間のないクレームの制約やガイドラインについては、「カスタムクレームを作成する」をお読みください。
  4. Actionsコードエディタのサイドバーから[Test(テスト)](再生アイコン)を選択し、[Run(実行)] を選択してコードをテストします。
  5. アクションを公開する準備ができたら、[Deploy(デプロイ)] を選択します。
ログインフローにアクションを追加する
次に、作成したアクションをログインフローに追加します。フローにアクションをアタッチする方法の詳細については、「初めてのアクションを作成する」の「アクションをフローにアタッチする」セクションをお読みください。
一意の識別子を取得する
最後に、APIの内部から、req.authからクレームの値を取得します。その値を、タイムシートエントリーに関連付ける一意のユーザー識別子として使用します。
app.get('/timesheets', checkJwt, jwtAuthz(['read:timesheets'], { customUserKey: 'auth' }), function(req, res) {
  var timesheet = req.body;

  // Associate the timesheet entry with the current user
  var userId = req.auth['https://api.exampleco.com/email'];
  timesheet.user_id = userId;

  // Save the timesheet to the database...

  //send the response
  res.status(201).send(timesheet);
});
I