このチュートリアルでは、 Open Authorization (OAuth) 2.0 クライアント資格情報付与フローを使用して Node.js デーモン クライアント アプリを準備し、Web API を呼び出すためのアクセス トークンを取得するように構成する方法について説明します。 Microsoft Authentication Library (MSAL) for Node を使用して Node.js アプリケーションを構築し、アプリへの承認の追加を簡略化します。
このチュートリアルでは、
- Web API のアプリ ロールを構成する
- デーモン アプリにアクセス許可を付与する
- Visual Studio Code で Node.js アプリを作成し、依存関係をインストールします。
- Node.js アプリが Web API を呼び出すためのアクセス トークンを取得できるようにします。
前提条件
- 組織のディレクトリと個人用の Microsoft アカウントのアカウント用に構成された、Microsoft Entra 管理センターに新しいクライアント アプリを登録します。 詳細については、「 アプリケーションの登録 」を参照してください。 後で使用するために、アプリケーション の [概要 ] ページから次の値を記録します。
- アプリケーション (クライアント) ID
- ディレクトリ (テナント) ID
- ディレクトリ (テナント) ドメイン名 ( contoso.onmicrosoft.com や contoso.com など)。
- クライアント アプリの登録にクライアント シークレットを追加します。 運用アプリではクライアント シークレットを使用しないでください。 代わりに、証明書またはフェデレーション資格情報を使用してください。 詳細については、「 アプリケーションに資格情報を追加する」を参照してください。
- 実行中で要求を受け入れる準備ができている保護された Web API。 Web API が HTTPS 経由で次のエンドポイントを公開していることを確認します。
GET /api/todolist
: すべての todo を取得します。POST /api/todolist
としてTODOを追加。
- Node.js。
- React アプリケーションをサポートする統合開発環境 (IDE) であればどれでも使用できますが、このチュートリアルでは Visual Studio Code を使用します。
アプリ ロールを構成する
API は、クライアント アプリがアクセス トークンをそれ自体として取得するために、アプリケーションに対して少なくとも 1 つのアプリ ロール ( アプリケーションアクセス許可とも呼ばれます) を発行する必要があります。 アプリケーションのアクセス許可は、クライアント アプリケーションが自身として正常に認証できるようにして、ユーザーをサインインさせる必要がないようにする場合に、API が発行するアクセス許可の種類です。 アプリケーションのアクセス許可を発行するには、次の手順に従います。
[アプリの登録] ページから、作成したアプリケーション (ciam-ToDoList-api など) を選択して、その [概要] ページを開きます。
[管理] で、[アプリ ロール] を選択します。
[アプリ ロールの作成] を選択し、次の値を入力し、[適用] を選択して変更を保存します:
プロパティ 価値 表示名 ToDoList.Read.All Allowed member types (許可されるメンバーの種類) アプリケーション 価値 ToDoList.Read.All 説明 'ToDoListApi' を使用して、アプリがすべてのユーザーの ToDo リストを読むことができるようにする このアプリ ロールを有効にしますか? オンのままにする もう一度 [アプリ ロールの作成] を選択し、2 番目のアプリ ロールに次の値を入力し、[適用] を選択して変更を保存します:
プロパティ 価値 表示名 ToDoList.ReadWrite.All Allowed member types (許可されるメンバーの種類) アプリケーション 価値 ToDoList.ReadWrite.All 説明 'ToDoListApi' を使用して、アプリがすべてのユーザーの ToDo リストを読み書きできるようにする このアプリ ロールを有効にしますか? オンのままにする
idtyp トークン クレームを構成する
idtyp の省略可能な要求を追加すると、Web API がトークンが アプリ トークンなのか アプリ + ユーザー トークンなのかを判断するのに役立ちます。 scp とロール要求の組み合わせを同じ目的で使用できますが、idtyp 要求を使用すると、アプリ トークンとアプリ + ユーザー トークンを区別する最も簡単な方法です。 たとえば、トークンがアプリ専用トークンの場合、この要求の値は app です。
デーモン アプリに API のアクセス許可を付与する
[アプリの登録] ページから、作成したアプリケーション (ciam-client-app など) を選択します。
[管理] で API 許可を選択します。
[構成されたアクセス許可] の下で [アクセス許可の追加] を選択します。
[所属する組織で使用している API] タブを選択します。
API の一覧で、API (ciam-ToDoList-api など) を選択します。
[アプリケーションのアクセス許可] オプションを選択します。 このアプリは、ユーザーの代理としてではなくそれ自体がサインインするものであるため、このオプションを選びます。
アクセス許可の一覧から、TodoList.Read.All と ToDoList.ReadWrite.All を選択します (必要に応じて検索ボックスを使用してください)。
[アクセス許可の追加] ボタンを選択します。
この時点で、アクセス許可が正しく割り当てられました。 ただし、デーモン アプリはユーザーが対話することを許可しないため、ユーザー自身がこれらのアクセス許可に同意することはできません。 この問題に対処するには、管理者が次のように、テナント内のすべてのユーザーに代わってこれらのアクセス許可に同意する必要があります。
- [<テナント名> に管理者の同意を与えます] を選択してから、[はい] を選択します。
- [最新の情報に更新] を選択し、両方のアクセス許可の < に ">テナント名 に付与されました" と表示されていることを確認します。
Node.js デーモン プロジェクトを作成する
ciam-call-api-node-daemon
など、Node.js デーモン アプリケーションをホストするフォルダーを作成します。
ターミナルで、
cd ciam-call-api-node-daemon
などの Node デーモン アプリ フォルダーにディレクトリを変更し、npm init -y
実行します。 このコマンドは、Node.js プロジェクトの既定の package.json ファイルを作成します。 このコマンドは、Node.js プロジェクトの既定のpackage.json
ファイルを作成します。次のプロジェクト構造を実現するために、追加のフォルダーとファイルを作成します。
ciam-call-api-node-daemon/ ├── auth.js └── authConfig.js └── fetch.js └── index.js └── package.json
アプリの依存関係をインストールする
ターミナルで、次のコマンドを実行して、axios
、yargs
、@azure/msal-node
パッケージをインストールします。
npm install axios yargs @azure/msal-node
MSAL 構成オブジェクトを作成する
コード エディターでファイル authConfig.js 開き、次のコードを追加します。
require('dotenv').config();
/**
* Configuration object to be passed to MSAL instance on creation.
* For a full list of MSAL Node configuration parameters, visit:
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-node/docs/configuration.md
*/
const msalConfig = {
auth: {
clientId: process.env.CLIENT_ID || 'Enter_the_Application_Id_Here', // 'Application (client) ID' of app registration in Azure portal - this value is a GUID
authority: process.env.AUTHORITY || 'https://Enter_the_Tenant_Subdomain_Here.ciamlogin.com/', // Replace "Enter_the_Tenant_Subdomain_Here" with your tenant subdomain
clientSecret: process.env.CLIENT_SECRET || 'Enter_the_Client_Secret_Here', // Client secret generated from the app
},
system: {
loggerOptions: {
loggerCallback(loglevel, message, containsPii) {
console.log(message);
},
piiLoggingEnabled: false,
logLevel: 'Info',
},
},
};
const protectedResources = {
apiToDoList: {
endpoint: process.env.API_ENDPOINT || 'https://localhost:44351/api/todolist',
scopes: [process.env.SCOPES || 'api://Enter_the_Web_Api_Application_Id_Here'],
},
};
module.exports = {
msalConfig,
protectedResources,
};
msalConfig
オブジェクトには、承認フローの動作をカスタマイズするために使用する一連の構成オプションが含まれています。
authConfig.js ファイルで、次の値を置き換えます。
Enter_the_Application_Id_Here
を、前の手順で登録したクライアント デーモン アプリのアプリケーション (クライアント) ID に置き換えます。Enter_the_Tenant_Subdomain_Here
をディレクトリ (テナント) サブドメインに置き換えます。 たとえば、テナントのプライマリ ドメインがcontoso.onmicrosoft.com
の場合は、contoso
を使用します。 テナント名がない場合は、テナントの詳細を読み取る方法を確認してください。前にコピーしたクライアント デーモン アプリのシークレット値を使用して
Enter_the_Client_Secret_Here
します。前にコピーした Web API アプリのアプリケーション (クライアント) ID に
Enter_the_Web_Api_Application_Id_Here
を置き換えます。
scopes
変数のprotectedResources
プロパティは、前提条件の一部として登録した Web API のリソース識別子 (アプリケーション ID URI) であることに注意してください。 完全なスコープ URI は、api://Enter_the_Web_Api_Application_Id_Here/.default
のようになります。
アクセス トークンを取得する
コード エディターでファイル auth.js 開き、次のコードを追加します。
const msal = require('@azure/msal-node');
const { msalConfig, protectedResources } = require('./authConfig');
/**
* With client credentials flows permissions need to be granted in the portal by a tenant administrator.
* The scope is always in the format '<resource-appId-uri>/.default'. For more, visit:
* https://docs.microsoft.com/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow
*/
const tokenRequest = {
scopes: [`${protectedResources.apiToDoList.scopes}/.default`],
};
const apiConfig = {
uri: protectedResources.apiToDoList.endpoint,
};
/**
* Initialize a confidential client application. For more info, visit:
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-node/docs/initialize-confidential-client-application.md
*/
const cca = new msal.ConfidentialClientApplication(msalConfig);
/**
* Acquires token with client credentials.
* @param {object} tokenRequest
*/
async function getToken(tokenRequest) {
return await cca.acquireTokenByClientCredential(tokenRequest);
}
module.exports = {
apiConfig: apiConfig,
tokenRequest: tokenRequest,
getToken: getToken,
};
コード内:
tokenRequest
とapiConfig
オブジェクトを準備します。tokenRequest
には、アクセス トークンを要求するスコープが含まれています。 スコープはapi://Enter_the_Web_Api_Application_Id_Here/.default
に似ています。apiConfig
オブジェクトには、Web API へのエンドポイントが含まれています。 OAuth 2.0 クライアント資格情報フロー の詳細を確認します。msalConfig
オブジェクトを ConfidentialClientApplication クラスのコンストラクターに渡すことによって、機密クライアント インスタンスを作成します。const cca = new msal.ConfidentialClientApplication(msalConfig);
次に、acquireTokenByClientCredential 関数を使用してアクセス トークンを取得します。 このロジックは、
getToken
関数に実装します。cca.acquireTokenByClientCredential(tokenRequest);
アクセス トークンを取得したら、API の呼び出しに進むことができます。
API を呼び出す
コード エディターでファイル fetch.js 開き、次のコードを追加します。
const axios = require('axios');
/**
* Calls the endpoint with authorization bearer token.
* @param {string} endpoint
* @param {string} accessToken
*/
async function callApi(endpoint, accessToken) {
const options = {
headers: {
Authorization: `Bearer ${accessToken}`
}
};
console.log('request made to web API at: ' + new Date().toString());
try {
const response = await axios.get(endpoint, options);
return response.data;
} catch (error) {
console.log(error)
return error;
}
};
module.exports = {
callApi: callApi
};
このコードでは、アクセス トークンをベアラー トークンとして要求 Authorization
ヘッダーに渡すことによって、Web API を呼び出します。
Authorization: `Bearer ${accessToken}`
前の手順「アクセス トークンを取得する」で取得したアクセス トークンを使用します。
Web API は要求を受け取ると評価し、それがアプリケーション要求であると判断します。 アクセス トークンが有効な場合、Web API は要求されたデータを返します。 それ以外の場合、API は 401 Unauthorized
HTTP エラーを返します。
デーモン アプリの最終処理
コード エディターでファイル index.js 開き、次のコードを追加します。
#!/usr/bin/env node
// read in env settings
require('dotenv').config();
const yargs = require('yargs');
const fetch = require('./fetch');
const auth = require('./auth');
const options = yargs
.usage('Usage: --op <operation_name>')
.option('op', { alias: 'operation', describe: 'operation name', type: 'string', demandOption: true })
.argv;
async function main() {
console.log(`You have selected: ${options.op}`);
switch (yargs.argv['op']) {
case 'getToDos':
try {
const authResponse = await auth.getToken(auth.tokenRequest);
const todos = await fetch.callApi(auth.apiConfig.uri, authResponse.accessToken);
} catch (error) {
console.log(error);
}
break;
default:
console.log('Select an operation first');
break;
}
};
main();
このコードは、アプリへのエントリ ポイントです。 Node.js アプリの yargs JavaScript コマンドライン引数解析ライブラリを使用して、アクセス トークンを対話形式でフェッチしてから API を呼び出します。 前に定義した getToken
関数と callApi
関数を使用します。
const authResponse = await auth.getToken(auth.tokenRequest);
const todos = await fetch.callApi(auth.apiConfig.uri, authResponse.accessToken);
デーモン アプリと API の実行とテスト
この時点で、クライアント デーモン アプリと Web API をテストする準備ができました。
ASP.NET Web API のセキュリティ保護に関するチュートリアルで学習した手順を使用して、Web API を開始します。 これで、Web API でクライアント要求を処理する準備ができました。
44351
ファイルで指定されているポート で Web API を実行しない場合は、正しい Web API のポート番号を使用するように authConfig.js ファイルを更新してください。ターミナルで、デーモン Node.js アプリ (
ciam-call-api-node-daemon
など) が含まれているプロジェクト フォルダーにいることを確認し、次のコマンドを実行します。node . --op getToDos
デーモン アプリと Web API が正常に実行された場合は、次の JSON 配列と同様に、Web API エンドポイント todos
変数によって返されるデータがコンソール ウィンドウに表示されます。
{
id: 1,
owner: '3e8....-db63-43a2-a767-5d7db...',
description: 'Pick up grocery'
},
{
id: 2,
owner: 'c3cc....-c4ec-4531-a197-cb919ed.....',
description: 'Finish invoice report'
},
{
id: 3,
owner: 'a35e....-3b8a-4632-8c4f-ffb840d.....',
description: 'Water plants'
}