我正在使用 NestJS 开发一个 Azure Web 应用程序服务。它被称为电子邮件服务,它应该与在 Office365 Exchange Online 中创建的邮箱进行通信。 Web 应用程序应该从收件箱检索消息并将消息发送出去。我需要获取访问令牌才能启用所需的功能。我将把它作为身份验证服务的一部分来处理。 这是文件夹结构: 电子邮件服务
• |---auth 服务文件夹包含 auth.service.ts
• |---检索端点包括检索服务和控制器,...
• |--- 通知端点包括通知服务和控制器
• |---main.ts
• |---module.ts
我正在使用 MSAL、ConfidentialClientApplication 和 acquireTokenByClientCredential。我已在 Azure 中注册了该应用程序,并且拥有正确的权限集。 当我尝试获取令牌作为 AuthService 类的一部分时,它会失败,但如果我在 auth.service.ts 中的 AuthService 类之外实现相同的逻辑,则可以。这是代码。顺便说一句,在这两种情况下,我都会在启动应用程序之前获取令牌。
//main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { FastifyAdapter, NestFastifyApplication } from '@nestjs/platform-fastify';
import { AuthService } from './auth/auth.service';
import * as msal from '@azure/msal-node';
async function bootstrap() {
const app = await NestFactory.create<NestFastifyApplication>(AppModule, new FastifyAdapter());
// TODO: Understand what the following line do
app.enableShutdownHooks();
await app.get(AuthService).acquireToken();// Acquire access token before starting the app
console.log(app.get(AuthService).getAccessToken()); // Display access token
await app.listen(3000, '0.0.0.0');
}
bootstrap();
当获取令牌发生在身份验证类之外时,身份验证服务。成功了。
// auth.service.ts
// TODO: Add documentation using TSDoc
import { Injectable } from '@nestjs/common';
import * as msal from '@azure/msal-node';
const clientId = 'myId';
const clientSecret = 'mySecret';
const tenantId = 'myTenant';
const scopes = ['https://graph.microsoft.com/.default'];
const azureActiveDirectoryEndPoint = 'https://login.microsoftonline.com/';
const authClient = new msal.ConfidentialClientApplication({
auth: {
clientId,
clientSecret,
authority: `${azureActiveDirectoryEndPoint}${tenantId}`,
}
});
function getToken(): Promise<string> {
return authClient.acquireTokenByClientCredential({
scopes,
}).then(authResult =>{
if (!authResult) {
throw new Error('Authentication failed!')
}
return authResult.accessToken;
})
}
@Injectable()
export class AuthService {
private timeStamp: Date | null = null;
private accessToken: string | null = null;
async acquireToken(): Promise<void> {
this.accessToken = await getToken();
}
// TODO: Should this be async?
getAccessToken() {
return this.accessToken;
}
}
获取令牌时的身份验证服务发生在身份验证类内。它会抛出错误。
// auth.service.ts
// TODO: Add documentation using TSDoc
import { Injectable } from '@nestjs/common';
import * as msal from '@azure/msal-node';
@Injectable()
export class AuthService {
private timeStamp: Date | null = null;
private accessToken: string | null = null;
private msalClient: msal.ConfidentialClientApplication;
constructor() {
let clientId = 'myId';
let clientSecret ='nySecret';
let tenantId ='myTenant';
let azureActiveDirectoryEndPoint = 'https://login.microsoftonline.com';
this.msalClient = new msal.ConfidentialClientApplication({
auth: {
clientId,
clientSecret,
authority: `${azureActiveDirectoryEndPoint}${tenantId}`,
}
});
}
async acquireToken(): Promise<void> {
try {
const tokenRequest: msal.ClientCredentialRequest = {
scopes: ['https://graph.microsoft.com/.default'],
};
const response = await this.msalClient.acquireTokenByClientCredential(tokenRequest);
this.accessToken = response.accessToken;
console.log('Access token acquired.');
} catch (error) {
console.error('Error acquiring access token:', error);
}
}
getAccessToken() {
return this.accessToken;
}
}
这是错误
Error acquiring access token: ClientAuthError: endpoints_resolution_error: Endpoints cannot be resolved
at createClientAuthError (C:\code\emailservice\node_modules\@azure\msal-common\src\error\ClientAuthError.ts:340:12)
at Object.createDiscoveredInstance (C:\code\emailservice\node_modules\@azure\msal-common\src\authority\AuthorityFactory.ts:70:15)
at processTicksAndRejections (node:internal/process/task_queues:95:5)
at ConfidentialClientApplication.buildOauthClientConfiguration (C:\code\emailservice\node_modules\@azure\msal-node\src\client\ClientApplication.ts:415:37)
at ConfidentialClientApplication.acquireTokenByClientCredential (C:\code\emailservice\node_modules\@azure\msal-node\src\client\ConfidentialClientApplication.ts:146:17)
at AuthService.acquireToken (C:\code\emailservice\src\auth\auth.service.ts:67:28)
at bootstrap (C:\code\emailservice\src\main.ts:45:3) {
errorCode: 'endpoints_resolution_error',
errorMessage: 'Endpoints cannot be resolved',
subError: '',
correlationId: 'a2359270-c25d-4bca-8d0b-f0bc33b548e5'
}
这个错误是什么?我错过了什么。
AuthService
中的访问令牌。一种方法是在 msal.ConfidentialClientApplication
的构造函数中实例化 AuthService
,而另一种方法是在类外部实例化它,然后在 AuthService
的方法中使用它。请按照以下代码操作。
代码:
import { Controller, Get, Post, Query, Req, Res } from '@nestjs/common';
import axios from 'axios';
import * as querystring from 'querystring';
@Controller()
export class AppController {
private readonly authUrl = 'https://login.microsoftonline.com/<tenant_ID>/oauth2/v2.0/authorize';
private readonly tokenUrl = 'https://login.microsoftonline.com/<tenant_ID>/oauth2/v2.0/token';
private readonly clientId = '<client_ID>';
private readonly clientSecret = '<client_secret>';
private readonly redirectUri = 'http://localhost:3000/xbox-oauth-callback';
@Get()
getAuthorizationUrl(@Res() res): void {
const scope = encodeURIComponent(this.clientId + '/.default');
const authorizationUrl = `${this.authUrl}?client_id=${this.clientId}&redirect_uri=${this.redirectUri}&response_type=code&scope=${scope}`;
res.send(`Authorization URL: <a href="${authorizationUrl}">${authorizationUrl}</a>`);
}
@Get('xbox-oauth-callback')
async handleCallback(@Query('code') code: string, @Res() res): Promise<void> {
const tokenParams = {
grant_type: 'authorization_code',
client_id: this.clientId,
client_secret: this.clientSecret,
code: code,
redirect_uri: this.redirectUri
};
try {
const tokenResponse = await axios.post(this.tokenUrl, querystring.stringify(tokenParams), {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
});
const accessToken = tokenResponse.data.access_token;
res.send(`Access Token: ${accessToken}`);
} catch (error) {
console.error('Error exchanging authorization code for access token:', error);
res.status(500).send('Error exchanging authorization code for access token');
}
}
}
GET /
对应根路由并提供授权URL。GET /xbox-oauth-callback
对应于我们用授权代码交换访问令牌的回调路由。