APIのステップアップ認証を構成する
ステップアップ認証を使用すると、さまざまな種類のリソースへのアクセスを許可するアプリケーションは、機密情報にアクセスしたり、特定のトランザクションを実行したりするために、より強力なメカニズムを使用してユーザーに認証を要求することができます。
たとえば、銀行アプリのユーザーは、多要素認証(MFA)を使用してIDを確認した後にのみ、口座間で送金することができます。
対象がAPIの場合、スコープ、アクセストークン、およびアクションを使用して、Auth0でステップアップ認証を実装できます。アプリケーションがAPIの保護されたリソースにアクセスする場合、アクセストークンを提供する必要があります。アクセスできるリソースは、アクセストークンに含まれる権限によって異なります。これらの権限はスコープとして定義されます。
MFAのアクセストークンを検証する
スコープの確認に加えて、APIはアクセストークンを検証して次の操作を行う必要があります。
トークンの署名を検証します。これは、トークンの送信者が本人であることを確認し、メッセージが途中で変更されていないことを確認するために使用されます。
標準クレームを検証します。
クレーム 説明 exp
トークンの有効期限 iss
トークンの発行者 aud
トークンの意図された受信者
シナリオ:プッシュ通知による銀行取引
次のシナリオでは、アプリケーションがユーザー名とパスワードでユーザーを認証し、口座残高を要求します。口座残高情報を取得する前に、ユーザーはGuardianプッシュファクターで認証する必要があります。
銀行APIは、口座残高の表示(スコープview:balance
)または資金の振替(スコープtransfer:funds
)という2つの異なるレベルの承認を受け入れることができます。アプリケーションがAPIにユーザーの残高の取得を要求する場合、アクセストークンにはview:balance
スコープが含まれている必要があります。別の口座に送金するには、アクセストークンにtransfer:funds
スコープが含まれている必要があります。
ワークフロー
ユーザーは、ユーザー名とパスワードの認証を使用してアプリケーションにログインします。標準ログインにより、このユーザーはAPIと対話して残高を取得できます。つまり、ユーザーが認証した後にアプリが受け取るアクセストークンには、
view:balance
スコープが含まれます。アプリケーションは、アクセストークンを資格情報として使用して、残高を取得する要求をAPIに送信します。
APIはトークンを検証し、残高情報をアプリケーションに送信して、ユーザーがそれを表示できるようにします。
ユーザーは、あるアカウントから別のアカウントに資金を移動したいと考えています。これは、
transfer:funds
スコープを必要とする高額トランザクションと見なされます。アプリケーションは同じアクセストークンを使用してAPIに要求を送信します。APIはトークンを検証し、トークンに必要な
transfer:funds
スコープがないため、アクセスを拒否します。アプリケーションはAuth0にリダイレクトします。そこで、高額スコープが要求されたため、アクションを使用してユーザーにMFAによる認証を要求します。ユーザーがMFAで正常に認証されると、正しいスコープを含む新しいアクセストークンが生成され、応答の一部としてアプリケーションに送信されます。
アプリケーションは、新しいアクセストークンを使用して別の資金振替要求を送信します。このトークンには、今回は
transfer:funds
スコープが含まれています。APIはトークンを検証し、破棄して操作を続行します。
前提条件
このシナリオでは、ダッシュボードで次の項目を構成する必要があります。
アクションを作成する
transfer:funds
スコープが要求されたときに、ユーザーにMFAによる認証を要求するアクションを作成します。[Dashbord]>[Actions(アクション)]>[Flows(フロー)]に移動し、次のコンテンツを含むアクションを作成します。
exports.onExecutePostLogin = async (event, api) => {
const CLIENTS_WITH_MFA = ['REPLACE_WITH_YOUR_CLIENT_ID'];
// run only for the specified clients
if (CLIENTS_WITH_MFA.includes(event.client.client_id)) {
// ask for MFA only if scope transfer:funds was requested
if (event.transaction.requested_scopes.indexOf('transfer:funds') > -1)
api.multifactor.enable('any', { allowRememberBrowser: false });
}
}
}
Was this helpful?
CLIENTS_WITH_MFA
変数には、このアクションを適用するアプリケーションのクライアントIDが含まれます。必要ない場合は、クライアントID(および後続のif
条件)を削除できます。event.transaction.requested_scopes
プロパティには、認証要求が要求したすべてのスコープが含まれます。値transfer:funds
が含まれている場合は、context.multifactor
プロパティを適切な値に設定してMFAを要求します。この場合、プッシュを使用してMFAを要求しています。
アプリを構成
ユーザーが資金を送金するという高額取引を実行しようとしているかどうかに応じて、適切な認証要求をAPIに送信するようにアプリを構成します。2つの認証要求(MFAの有無)の唯一の違いはスコープであることに注意してください。
MFAあり:
https://{yourDomain}/authorize? audience=https://my-banking-api& scope=openid%20view:balance%20transfer:funds& response_type=id_token%20token& client_id={yourClientId}& redirect_uri={https://yourApp/callback}& nonce=NONCE& state=OPAQUE_VALUE
Was this helpful?
/MFAなし:
https://{yourDomain}/authorize? audience=https://my-banking-api& scope=openid%20view:balance& response_type=id_token%20token& client_id={yourClientId}& redirect_uri={https://yourApp/callback}& nonce=NONCE& state=OPAQUE_VALUE
Was this helpful?
/
パラメーター | 設定 |
---|---|
audience |
APIの**Identifier(識別子)**に設定する(」API設定」を参照)。当方ではhttps://my-banking-api に設定しています。 |
response_type |
id_token token に設定して、応答でIDトークンとアクセストークンを両方取得するようにします。 |
client_id |
アプリケーションのクライアントIDに設定します(「アプリケーション設定」を参照)。 |
redirect_uri |
認証後にAuth0がリダイレクトで戻すアプリケーション内のURLに設定します(アプリケーション設定を参照)。 |
nonce |
Auth0からの応答に含まれる安全な文字列値に設定します。これはトークンリプレイ攻撃の防止に使用され、response_type=id_token token には必須です。 |
state |
アプリケーションにリダイレクトで戻すときにAuth0が含める不透明な値に設定します。アプリケーションはCSRF攻撃を防ぐためにこの値を使用する必要があります。 |
APIを構成する
受信トークンを検証し、承認された権限を確認するようにAPIを構成します。
次のAPIの2つのエンドポイントを構成します。
GET /balance
: 現在の残高を取得しますPOST /transfer
: 資金を送金しますNode.js
と次の複数のモジュールを使用します。Express: Express Webアプリケーションフレームワークを追加します。
jwks-rsa: JWKS(JSON Web Key Set)エンドポイントからRSA署名キーを取得します。
expressJwtSecret
を使用すると、JWTヘッダーのkid
に基づいてexpress-jwt
に適切な署名キーを発行するシークレットプロバイダーを生成できます。express-jwt: Node.jsアプリケーションでJWTトークンを使用してHTTP要求を認証できます。JWTを使用した作業を容易にする、いくつかの機能が提供されています。
express-jwt-authz: アクセストークンに特定のスコープが含まれているかどうかを確認します。
次の依存関係をインストールします。
npm install express express-jwt jwks-rsa express-jwt-authz --save
APIエンドポイントを定義し、アクセストークンを検証するミドルウェア関数を作成し、そのミドルウェアを使用してエンドポイントを保護します。
server.js
ファイル内のコードは、次のサンプルスクリプトのようになります。APIが要求を受信するたびに、次の処理が行われます。// set dependencies const express = require('express'); const app = express(); const jwt = require('express-jwt'); const jwksRsa = require('jwks-rsa'); const jwtAuthz = require('express-jwt-authz'); // 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: 'https://my-banking-api', // replace with your API's audience, available at Dashboard > APIs issuer: 'https://{yourDomain}/', algorithms: [ 'RS256' ] // we are using RS256 to sign our tokens }); // create retrieve balance endpoint app.get('/balance', checkJwt, jwtAuthz(['view:balance']), function (req, res) { // code that retrieves the user's balance and sends it back to the calling app res.status(201).send({message: "This is the GET /balance endpoint"}); }); // create transfer funds endpoint app.post('/transfer', checkJwt, jwtAuthz(['transfer:funds']), function (req, res) { // code that transfers funds from one account to another res.status(201).send({message: "This is the POST /transfer endpoint"}); }); // launch the API Server at localhost:8080 app.listen(8080); console.log('Listening on http://localhost:8080');
Was this helpful?
/エンドポイントは
checkJwt
ミドルウェアを呼び出します。express-jwt
はトークンをデコードし、要求、ヘッダー、ペイロードをjwksRsa.expressJwtSecret
に渡します。jwks-rsa
はJWKSエンドポイントからすべての署名キーをダウンロードし、署名キーの1つがアクセストークンのヘッダー内のkid
と一致するかどうかを確認します。署名キーのいずれも受信kid
と一致しない場合は、エラーがスローされます。一致する場合は、正しい署名キーをexpress-jwt
に渡します。express-jwt
は独自のロジックを続行して、トークンの署名、有効期限、対象ユーザー、発行者を検証します。jwtAuthz
は、エンドポイントに必要なスコープがアクセストークンの一部であるかどうかを確認します。指定されたスコープがアクセストークンにない場合、要求は403エラーメッセージで拒否されます。