教程:从 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 守护程序应用程序。
- 受保护的 Web API,它正在运行且已准备好接受请求。 如果尚未创建,请参阅创建受保护的 Web API 教程。 确保此 Web API 使用在准备租户教程中创建的应用注册详细信息。 确保 Web API 通过 HTTPS 公开以下终结点:
GET /api/todolist
以获取所有待办事项。POST /api/todolist
,用于添加待办事项。
- Node.js。
- 本教程使用了 Visual Studio Code,但可以使用任何支持 React 应用程序的集成开发环境 (IDE)。
- 在准备租户教程中创建的 Node.js 守护程序应用和 Web API 的注册详细信息。
创建 Node.js 守护程序项目
创建一个用于托管 Node.js 守护程序应用程序的文件夹,例如 ciam-call-api-node-daemon
:
在终端中,将目录切换到 Node 守护程序应用文件夹(例如
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
替换为之前注册的客户端守护程序应用的应用程序(客户端)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:
使用在保护 ASP.NET Web API 教程中学到的步骤启动 Web API。 Web API 现已准备好处理客户端请求。 如果未在 authConfig.js 文件中指定的
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'
}