在 NodeJS 开发的 Azure Web 应用服务中获取访问令牌

问题描述 投票:0回答:1

我正在使用 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'
}

这个错误是什么?我错过了什么。

node.js nestjs azure-web-app-service access-token azure-ad-msal
1个回答
0
投票
  • 您有两种不同的方法来获取
    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
    对应于我们用授权代码交换访问令牌的回调路由。

已添加网址:

认证网址:

访问令牌:

© www.soinside.com 2019 - 2024. All rights reserved.