You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NestJS中Azure AD公共客户端PKCE配置异常求助

问题描述

在NestJS中配置Azure AD认证授权时,使用带PKCE配置的Public Client Application,却遇到以下问题:

  • 初始配置(平台为Web)时,用户完成认证后返回错误:

invalid_client: Error(s): 7000218 - Timestamp: 2024-10-21 02:49:19Z - Description: AADSTS7000218: 请求正文必须包含以下参数:'client_assertion' 或 'client_secret'。

Trace ID: e76ee3c8-d591-43f5-b939-1a5188441000

Correlation ID: 3711a68c-a1c7-4ddf-a524-0fbd0d68feb2

Timestamp: 2024-10-21 02:49:19Z

  • 将平台配置改为SPA后,错误变为:

AADSTS9002325: 跨域授权码兑换需要使用PKCE证明密钥。

相关代码如下:

PublicAuthService.ts

import { Injectable, Logger } from '@nestjs/common';
import { PublicClientApplication, AuthenticationResult } from '@azure/msal-node';
import * as crypto from 'crypto';

@Injectable()
export class PublicAuthService {
  public pca: PublicClientApplication;
  private readonly logger = new Logger(PublicAuthService.name);
  private codeVerifier: string;  // 临时存储code verifier

  constructor() {
    this.pca = new PublicClientApplication({
      auth: {
        clientId: process.env.CLIENT_ID,
        authority: `https://login.microsoftonline.com/${process.env.TENANT_ID}`,
      },
    });
  }

  // 生成code verifier的辅助函数
  private generateCodeVerifier(): string {
    return crypto.randomBytes(32).toString('base64url');
  }

  // 生成code challenge的辅助函数
  private generateCodeChallenge(verifier: string): string {
    return crypto.createHash('sha256').update(verifier).digest('base64url');
  }

  async getAuthUrl(): Promise<string> {
    this.codeVerifier = this.generateCodeVerifier();  // 存储code verifier

    const codeChallenge = this.generateCodeChallenge(this.codeVerifier);

    return this.pca.getAuthCodeUrl({
      scopes: ['User.Read'],
      redirectUri: process.env.REDIRECT_URI,
      codeChallenge: codeChallenge,
      codeChallengeMethod: 'S256',
    });
  }

  async acquireTokenByCode(code: string): Promise<AuthenticationResult> {
    return this.pca.acquireTokenByCode({
      code,
      scopes: ['User.Read'],
      redirectUri: process.env.REDIRECT_URI,
      codeVerifier: this.codeVerifier,  // 使用存储的code verifier
    });
  }
}

PublicAuthController.ts

import { Controller, Get, Query, Res, HttpStatus } from '@nestjs/common';
import { PublicAuthService } from './public_auth.service';

@Controller('public-auth')
export class PublicAuthController {
  constructor(private readonly publicAuthService: PublicAuthService) {}

  @Get('login')
  async login(@Res() res) {
    const authUrl = await this.publicAuthService.pca.getAuthCodeUrl({
      scopes: ['User.Read'],
      redirectUri: process.env.REDIRECT_URI,
    });
    res.redirect(authUrl);
  }

  @Get('redirect')
  async handleRedirect(@Query('code') code: string, @Res() res) {
    try {
      const result = await this.publicAuthService.acquireTokenByCode(code);
      res.status(HttpStatus.OK).json({
        accessToken: result.accessToken,
        user: result.account,
      });
    } catch (error) {
      res.status(HttpStatus.UNAUTHORIZED).json({ message: error.message });
    }
  }
}
解决方案

1. 修复登录接口的PKCE参数缺失

你的PublicAuthController中的login方法直接调用了pca.getAuthCodeUrl,但没有传入PKCE相关的codeChallenge和codeChallengeMethod参数,也没有调用PublicAuthService中已经实现的getAuthUrl方法——这会导致授权请求没有携带PKCE信息,Azure AD会将你的应用视为机密客户端,从而要求提供client_secret或client_assertion。

修改login方法如下:

@Get('login')
async login(@Res() res) {
  const authUrl = await this.publicAuthService.getAuthUrl();
  res.redirect(authUrl);
}

2. 配置正确的Azure AD应用平台类型

  • 如果你的NestJS服务是作为后端公开客户端(比如服务端发起认证请求),需要在Azure AD应用的"身份验证"设置中,添加移动和桌面应用平台,而不是Web或SPA。
  • SPA平台是为纯前端JavaScript应用设计的,会强制要求跨域场景下的PKCE验证,而后端客户端属于"移动和桌面"类别,更适配你的使用场景。

3. 验证PKCE流程的完整性

确保codeVerifier在授权请求生成时被正确存储,并且在调用acquireTokenByCode时准确传递。注意:codeVerifier必须和生成codeChallenge时使用的完全一致,否则会导致PKCE验证失败。

内容的提问来源于stack exchange,提问作者galih

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 23:41:17