Node.js APIの実装 (SPA+API)
本ドキュメントはSPA + APIアーキテクチャーシナリオの一部で、APIをNode.jsで実装する方法を説明します。実装したソリューションについての情報は、シナリオを参照してください。
Node.jsでのAPI実装で使用する全ソースコードは、こちらのGitHubリポジトリでご覧いただけます。
1.APIエンドポイントの定義
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"
}
Was this helpful?
依存関係をインストールします
次に、依存関係を設定する必要があります。以下のモジュールを使用します。
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
で公開します。詳細および代替については、「body-parser GitHubリポジトリ」を参照してください。
これらの依存関係をインストールするには、次を実行します。
npm install express cors express-jwt jwks-rsa body-parser express-jwt-authz --save
Was this helpful?
エンドポイントを実装する
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);
Was this helpful?
node server
を使用してAPIサーバーを起動し、localhost:8080/timesheets
にHTTP POST要求を行います。This is the POST /timesheets endpoint
というメッセージを含むJSON応答が表示されます。
これでエンドポイントが入手できましたが、それは誰でも呼び出すことができます。次の段落に進んで、この問題を解決する方法を確認します。
2.APIエンドポイントを保護する
トークンを検証するには、express-jwtミドルウェアが提供するjwt
関数とjwks-rsa
を使用してシークレットを取得します。ライブラリーは以下を行います。
express-jwt
はトークンをデコードし、要求、ヘッダー、ペイロードをjwksRsa.expressJwtSecret
に渡します。その後
jwks-rsa
はJWKSエンドポイントからすべての署名鍵をダウンロードし、署名鍵の1つがJWTヘッダーのkid
と一致するかどうかを確認します。どの署名鍵も受け取ったkid
に一致しない場合、エラーがスローされます。一致するものがあれば、express-jwt
に正しい署名鍵を渡します。express-jwt
はトークンの署名、有効期限、オーディエンス
、発行者
を検証する独自のロジックを継続します。
コードでは次のステップに従います。
アクセストークンを検証するミドルウェア関数を作成する。
ルートでミドルウェアを使用できるようにする。
また、実際にタイムシートをデータベースに保存するコードを書くこともできます。以下はサンプル実装を示します(簡潔にするために一部のコードは省略しています)。
// 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
Was this helpful?
ここでサーバーを起動してlocalhost:8080/timesheets
にHTTP POSTを実行すると、エラーメッセージMissing or invalid token
が表示されるはずです(要求でアクセストークンを送信しなかったためこれは正確です)。
また、作業シナリオをテストするためには、次を実行する必要があります。
アクセストークンを取得します。詳しい取得方法については、「アクセストークンを取得する」を参照してください。
APIを呼び出し、
Bearer ACCESS_TOKEN
値を指定してAuthorization
ヘッダーを要求に追加します(ACCESS_TOKEN
は最初のステップで取得したトークンの値)。
3.アプリケーションの権限を確認する
このステップでは、アプリケーションに権限(またはスコープ
)があるかを確認する機能を実装に追加して、タイムシートを作成するためにエンドポイントを使用します。特にトークンのスコープが正しく、batch:upload
であることを確認することが目的です。
そのためには、express-jwt-authz
Node.jsパッケージを利用するので、それをプロジェクトに追加します。
npm install express-jwt-authz --save
Was this helpful?
ここで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
Was this helpful?
このスコープを含まないトークンを使用してAPIを呼び出すHTTPステータスコード403
の「Forbidden(禁止)」というエラーメッセージが表示されます。APIからこのスコープを削除と、これをテストすることができます。
4.ユーザーIDの決定
JWTの検証に使われるexpress-jwt
ミドルウェアは、JWTに含まれる情報をreq.auth
に設定します。sub
クレームを使用してユーザーを一意に識別したい場合は、req.auth.sub
を使用するだけです。
ただし、タイムシートアプリケーションの場合は、ユーザーの電子メールアドレスを一意の識別子として使用します。
最初に行う必要があるのは、ユーザーの電子メールアドレスをアクセストークンに追加するルールを作成することです。Dashboardの[Rules section(ルールセクション)]に移動し、[Create 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);
}
Was this helpful?
[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;
// 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);
});
Was this helpful?