次の方法で共有


チュートリアル: Node.js デーモン アプリケーションから Web API を呼び出す

このチュートリアルは、Open Authorization (OAuth) 2.0 クライアント資格情報の許可フローを使って Node.js デーモン クライアント アプリを準備してから、Web API を呼び出すためのアクセス トークンを取得するようにそれを構成する方法を見ていくシリーズの最後の部分です。 このシリーズのパート 1 では、Microsoft Entra 管理センターで Web API とデーモン アプリを登録して、アクセス許可を付与しました。 この最後のステップでは、Node 用 Microsoft 認証ライブラリ (MSAL) を使って Node.js アプリケーションを構築して、アプリに認可を簡単に追加できるようにする方法を見ていきます。

このチュートリアルでは、

  • Visual Studio Code で Node.js アプリを作成してから、依存関係をインストールします。
  • Node.js アプリを有効にして、Web API を呼び出すためのアクセス トークンを取得します。

前提条件

Node.js デーモン プロジェクトを作成する

Node.js デーモン アプリケーションをホストするフォルダーを作成します (例: ciam-call-api-node-daemon)。

  1. ターミナルで、ノード デーモン アプリ フォルダー (cd ciam-call-api-node-daemon など) にディレクトリを変更し、npm init -y を実行します。 このコマンドを実行すると、Node.js プロジェクト用に既定の package.json ファイルが作成されます。 このコマンドは、Node.js プロジェクトのデフォルトの package.json ファイルを作成します。

  2. 追加のフォルダーとファイルを作成して、次のプロジェクト構造を完成させます。

        ciam-call-api-node-daemon/
        ├── auth.js
        └── authConfig.js
        └── fetch.js
        └── index.js 
        └── package.json
    

アプリの依存関係をインストールする

ターミナルで、次のコマンドを実行して、axiosyargs@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 を、前の手順でコピーしたクライアント デーモン アプリ シークレット値に置き換えます。

  • Enter_the_Web_Api_Application_Id_Here を、前の手順でコピーした Web API アプリのアプリケーション (クライアント) ID に置き換えます。

protectedResources 変数の scopes プロパティは、前の手順で登録した 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 をテストする準備ができました。

  1. ASP.NET Web API をセキュリティで保護する」のチュートリアルで学習した手順を使用して、Web API を開始します。 これで、Web API でクライアント要求を処理する準備が整いました。 authConfig.js ファイルで指定されているポート 44351 で Web API を実行しない場合は、忘れずに Web API の正しいポート番号を使用するように authConfig.js ファイルを更新してください。

  2. ターミナルで、ciam-call-api-node-daemon などのデーモン Node.js アプリを含むプロジェクト フォルダーに移動し、そこで次のコマンドを実行します。

    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'
}

次のステップ