共用方式為


教學課程:從您的 Node.js 精靈應用程式呼叫Web API

本教學課程示範如何使用 Open Authorization (OAuth) 2.0 用戶端認證授與流程來準備您的 Node.js 精靈用戶端應用程式,然後將它設定為取得呼叫 Web API 的存取令牌。 您將使用 node Microsoft Authentication Library (MSAL) 建置 Node.js 應用程式,以簡化將授權新增至您的應用程式。

在本教學中,

  • 設定 Web API 的應用程式角色
  • 將權限授與精靈應用程式
  • 在 Visual Studio Code 中建立 Node.js 應用程式,然後安裝相依性。
  • 讓 Node.js 應用程式取得呼叫 Web API 的存取令牌。

先決條件

  • Microsoft Entra 系統管理中心註冊新的用戶端應用程式,針對 任何組織目錄中的帳戶和個人Microsoft帳戶進行設定。 如需詳細資訊 ,請參閱註冊應用程式 。 從應用程式 [概 ] 頁面記錄下列值,以供稍後使用:
    • 應用程式 (用戶端) 識別碼
    • 目錄(租戶)識別碼
    • 目錄(租使用者)功能變數名稱(例如 ,contoso.onmicrosoft.comcontoso.com)。
  • 將客戶端密碼新增至用戶端應用程式註冊。 請勿 在生產應用程式中使用客戶端密碼。 請改用憑證或同盟認證。 如需詳細資訊,請參閱 將認證新增至您的應用程式
  • 受保護的 Web API,正在執行並準備好接受要求。 請確定您的 Web API 會透過 HTTPS 公開下列端點:
    • GET /api/todolist 取得所有待辦事項。
    • POST /api/todolist 新增一個 TODO。
  • Node.js。
  • 雖然可以使用任何支援 React 應用程式的整合式開發環境 (IDE),但本教學課程會使用 Visual Studio Code

設定應用程式角色

API 需要為應用程式發佈至少一個應用程式角色,也稱為 應用程式許可權,用戶端應用程式才能取得存取令牌。 應用程式許可權是 API 想要讓用戶端應用程式以自己身分成功驗證,而不需要登入使用者時,應該發佈的許可權類型。 若要發佈應用程式許可權,請遵循下列步驟:

  1. 從 [應用程式註冊] 頁面中,選取您建立的應用程式 (例如 ciam-ToDoList-api),以開啟其 [概觀] 頁面。

  2. 在 [管理] 下方,選取 [應用程式角色]

  3. 選取 [建立應用程式角色],並輸入下列值,然後選取 [套用] 以儲存您的變更:

    財產 價值
    顯示名稱 ToDoList.Read.All
    允許的成員類型 應用程式
    價值 ToDoList.Read.All
    說明 允許應用程式使用 'TodoListApi' 讀取每位使用者的待辦事項清單
    您要啟用此應用程式角色嗎? 保持檢查
  4. 再次選取 [建立應用程式角色],並為第二個應用程式角色輸入下列值,然後選取 [套用] 以儲存您的變更:

    財產 價值
    顯示名稱 ToDoList.ReadWrite.All
    允許的成員類型 應用程式
    價值 ToDoList.ReadWrite.All
    說明 允許應用程式使用 'ToDoListApi' 讀取和寫入每位使用者的待辦事項清單
    您要啟用此應用程式角色嗎? 保持檢查

設定 idtyp 權杖的宣告屬性

您可以新增 idtyp 選擇性宣告,以協助 Web API 判斷令牌是 應用程式 令牌還是 應用程式 + 使用者 令牌。 雖然您可以將 scproles 宣告組合使用於相同用途,但使用 idtyp 宣告是區分應用程式權杖和應用程式 + 使用者權杖的最簡單方式。 例如,當權杖是應用程式專用權杖時,此宣告值為 app

將 API 許可權授與精靈應用程式

  1. 從 [應用程式註冊] 頁面中,選取您建立的應用程式 (例如 ciam-client-app)。

  2. 「管理」下方選取 [API 權限]

  3. 在 [已設定的權限] 底下,選取 [新增權限]

  4. 選取 [我的組織使用的 API] 索引標籤。

  5. 在 API 清單中,選取 API,例如 ciam-ToDoList-api

  6. 選取 [應用程式權限] 選項。 我們會選取此選項,因為應用程式會以本身身分登入,但不代表使用者登入。

  7. 從權限清單中,選取 [TodoList.Read.All]、[ToDoList.ReadWrite.All] (如有必要,請使用搜尋方塊)。

  8. 選取 [新增權限] 按鈕。

  9. 此時,您已正確地分配了權限。 不過,由於精靈應用程式不允許使用者與其互動,因此使用者本身無法同意這些許可權。 若要解決此問題,身為管理員的您必須代表租用戶中的所有使用者同意這些權限:

    1. 選取對 <您的租戶名稱> 授予系統管理員同意,然後選取
    2. 選取「重新整理」,然後驗證「已授與 您的租用戶名稱 是否出現在這兩個權限的「狀態」下方。

建立 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 您稍早註冊之用戶端精靈應用程式的應用程式(用戶端)標識碼。

  • Enter_the_Tenant_Subdomain_Here,並將它取代為 Directory (tenant) 子域。 例如,如果您的租戶主要網域為 contoso.onmicrosoft.com,則請使用 contoso。 如果您沒有租用戶名稱,請了解如何讀取租用戶詳細資料

  • Enter_the_Client_Secret_Here 配上您稍早複製的客戶端進程應用程式的秘密值。

  • Enter_the_Web_Api_Application_Id_Here,其中包含您稍早複製之 Web API 應用程式的應用程式(用戶端)識別碼。

請注意, scopes 變數中的 protectedResources 屬性是您註冊為必要條件一部分之 Web API 的資源識別碼(應用程式識別碼 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,
};

在程式碼中:

  • 準備 tokenRequestapiConfig 物件。 tokenRequest 包含您要求存取令牌的範圍。 範圍看起來像 api://Enter_the_Web_Api_Application_Id_Here/.defaultapiConfig 物件包含 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();

此程式碼是您應用程式的進入點。 您可以使用 yargs JavaScript 命令行自變數剖析連結庫,讓 Node.js 應用程式以互動方式擷取存取令牌,然後呼叫 API。 您可以使用您稍早定義的 getTokencallApi 函式:

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 現在已準備好提供用戶端要求。 如果您未在 44351 檔案中指定的埠 上執行 Web API,請務必更新 authConfig.js 檔案,以使用正確的 Web API 埠號碼。

  2. 在終端機中,請確定您位於包含精靈 Node.js 應用程式的項目資料夾中,例如 ciam-call-api-node-daemon,然後執行下列命令:

    node . --op getToDos
    

如果您的精靈應用程式和 Web API 順利執行,您應該會在主控台視窗中找到 Web API 端點所傳回的數據 todos 變數,類似於下列 JSON 陣列:

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

後續步驟

在您的 Node.js 機密應用程式中使用用戶端憑證,而非用密鑰進行驗證。