本教學課程示範如何使用 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.com 或 contoso.com)。
- 將客戶端密碼新增至用戶端應用程式註冊。 請勿 在生產應用程式中使用客戶端密碼。 請改用憑證或同盟認證。 如需詳細資訊,請參閱 將認證新增至您的應用程式。
- 受保護的 Web API,正在執行並準備好接受要求。 請確定您的 Web API 會透過 HTTPS 公開下列端點:
-
GET /api/todolist
取得所有待辦事項。 -
POST /api/todolist
新增一個 TODO。
-
- Node.js。
- 雖然可以使用任何支援 React 應用程式的整合式開發環境 (IDE),但本教學課程會使用 Visual Studio Code。
設定應用程式角色
API 需要為應用程式發佈至少一個應用程式角色,也稱為 應用程式許可權,用戶端應用程式才能取得存取令牌。 應用程式許可權是 API 想要讓用戶端應用程式以自己身分成功驗證,而不需要登入使用者時,應該發佈的許可權類型。 若要發佈應用程式許可權,請遵循下列步驟:
從 [應用程式註冊] 頁面中,選取您建立的應用程式 (例如 ciam-ToDoList-api),以開啟其 [概觀] 頁面。
在 [管理] 下方,選取 [應用程式角色]。
選取 [建立應用程式角色],並輸入下列值,然後選取 [套用] 以儲存您的變更:
財產 價值 顯示名稱 ToDoList.Read.All 允許的成員類型 應用程式 價值 ToDoList.Read.All 說明 允許應用程式使用 'TodoListApi' 讀取每位使用者的待辦事項清單 您要啟用此應用程式角色嗎? 保持檢查 再次選取 [建立應用程式角色],並為第二個應用程式角色輸入下列值,然後選取 [套用] 以儲存您的變更:
財產 價值 顯示名稱 ToDoList.ReadWrite.All 允許的成員類型 應用程式 價值 ToDoList.ReadWrite.All 說明 允許應用程式使用 'ToDoListApi' 讀取和寫入每位使用者的待辦事項清單 您要啟用此應用程式角色嗎? 保持檢查
設定 idtyp 權杖的宣告屬性
您可以新增 idtyp 選擇性宣告,以協助 Web API 判斷令牌是 應用程式 令牌還是 應用程式 + 使用者 令牌。 雖然您可以將 scp 和 roles 宣告組合使用於相同用途,但使用 idtyp 宣告是區分應用程式權杖和應用程式 + 使用者權杖的最簡單方式。 例如,當權杖是應用程式專用權杖時,此宣告值為 app。
將 API 許可權授與精靈應用程式
從 [應用程式註冊] 頁面中,選取您建立的應用程式 (例如 ciam-client-app)。
在「管理」下方選取 [API 權限]。
在 [已設定的權限] 底下,選取 [新增權限]。
選取 [我的組織使用的 API] 索引標籤。
在 API 清單中,選取 API,例如 ciam-ToDoList-api。
選取 [應用程式權限] 選項。 我們會選取此選項,因為應用程式會以本身身分登入,但不代表使用者登入。
從權限清單中,選取 [TodoList.Read.All]、[ToDoList.ReadWrite.All] (如有必要,請使用搜尋方塊)。
選取 [新增權限] 按鈕。
此時,您已正確地分配了權限。 不過,由於精靈應用程式不允許使用者與其互動,因此使用者本身無法同意這些許可權。 若要解決此問題,身為管理員的您必須代表租用戶中的所有使用者同意這些權限:
- 選取對 <您的租戶名稱> 授予系統管理員同意,然後選取是。
- 選取「重新整理」
,然後驗證「已授與 您的租用戶名稱 」 是否出現在這兩個權限的「狀態」 下方。
建立 Node.js 精靈專案
建立資料夾來裝載 Node.js 精靈應用程式,例如 ciam-call-api-node-daemon
:
在終端機中,將目錄變更為節點精靈應用程式資料夾,例如
cd ciam-call-api-node-daemon
,然後執行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
您稍早註冊之用戶端精靈應用程式的應用程式(用戶端)標識碼。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,
};
在程式碼中:
準備
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();
此程式碼是您應用程式的進入點。 您可以使用 yargs JavaScript 命令行自變數剖析連結庫,讓 Node.js 應用程式以互動方式擷取存取令牌,然後呼叫 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,請務必更新 authConfig.js 檔案,以使用正確的 Web API 埠號碼。在終端機中,請確定您位於包含精靈 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 機密應用程式中使用用戶端憑證,而非用密鑰進行驗證。