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

NestJS中如何实现Azure AD Passport身份验证?

嗨,我之前在项目里刚好折腾过这个!确实NestJS官方在Azure AD Passport身份验证这块的文档有点缺失,而且实际实现过程中确实会踩不少坑。我把完整的实现步骤和避坑指南整理出来了,你可以参考下:

在NestJS中实现Azure AD Passport身份验证完整指南

一、先搞定Azure AD应用配置(这步很关键,错了后面全白搭)

  • 登录Azure门户,找到Azure Active Directory → 应用注册 → 点击「新建注册」
  • 填写应用名称,选择支持的账户类型(比如单租户/多租户,根据你的业务需求)
  • 注册完成后,记下这三个关键信息:租户ID(目录ID)、客户端ID
  • 转到「证书和密码」,点击「新建客户端密码」,设置过期时间后,复制生成的密码值(这就是客户端密钥,只显示一次,一定要存好)
  • 转到「API权限」,添加Microsoft Graph的User.Read委托权限,然后点击「授予管理员同意」(如果是多租户应用,可能需要租户管理员操作)

二、安装依赖包

打开终端,在你的NestJS项目里安装所需依赖:

npm install @nestjs/passport passport-azure-ad passport @nestjs/config
# 如果需要处理自定义JWT令牌,还可以装以下包
npm install @nestjs/jwt passport-jwt

三、配置环境变量

用@nestjs/config管理配置,新建.env文件,把Azure的配置填进去:

AZURE_AD_TENANT_ID=你的租户ID
AZURE_AD_CLIENT_ID=你的客户端ID
AZURE_AD_CLIENT_SECRET=你的客户端密钥
# 如果用授权码流(前端跳转登录),需要填重定向URI
AZURE_AD_REDIRECT_URI=http://localhost:3000/auth/callback

然后在app.module.ts里导入ConfigModule:

import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { AuthModule } from './auth/auth.module';

@Module({
  imports: [
    ConfigModule.forRoot({ isGlobal: true }), // 全局可用
    AuthModule,
  ],
})
export class AppModule {}

四、创建Passport策略(分两种常见场景)

场景1:后端API用Bearer令牌验证(最常见的API保护场景)

新建auth/azure-ad-bearer.strategy.ts:

import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { BearerStrategy } from 'passport-azure-ad';
import { ConfigService } from '@nestjs/config';

@Injectable()
export class AzureAdBearerStrategy extends PassportStrategy(BearerStrategy, 'azure-ad') {
  constructor(private readonly configService: ConfigService) {
    super({
      // Azure AD的身份元数据地址
      identityMetadata: `https://login.microsoftonline.com/${configService.get('AZURE_AD_TENANT_ID')}/v2.0/.well-known/openid-configuration`,
      clientID: configService.get('AZURE_AD_CLIENT_ID'),
      validateIssuer: true, // 验证令牌签发者
      issuer: `https://login.microsoftonline.com/${configService.get('AZURE_AD_TENANT_ID')}/v2.0`,
      audience: configService.get('AZURE_AD_CLIENT_ID'), // 令牌受众,填客户端ID或者应用ID URI
      loggingLevel: 'info',
      passReqToCallback: false,
    });
  }

  // 自定义验证逻辑,这里可以对接你的数据库,检查用户是否存在
  async validate(payload: any) {
    return {
      userId: payload.oid, // Azure AD里的用户唯一ID
      email: payload.emails?.[0],
      name: payload.name,
    };
  }
}

场景2:Web应用用授权码流(前端跳转Azure登录页)

如果是前后端分离或者服务端渲染的应用,需要用OIDC策略处理登录跳转和回调:
新建auth/azure-ad-oidc.strategy.ts:

import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { OIDCStrategy } from 'passport-azure-ad';
import { ConfigService } from '@nestjs/config';

@Injectable()
export class AzureAdOidcStrategy extends PassportStrategy(OIDCStrategy, 'azure-ad-oidc') {
  constructor(private readonly configService: ConfigService) {
    super({
      identityMetadata: `https://login.microsoftonline.com/${configService.get('AZURE_AD_TENANT_ID')}/v2.0/.well-known/openid-configuration`,
      clientID: configService.get('AZURE_AD_CLIENT_ID'),
      clientSecret: configService.get('AZURE_AD_CLIENT_SECRET'),
      redirectUrl: configService.get('AZURE_AD_REDIRECT_URI'),
      responseType: 'code id_token', // 同时获取授权码和ID令牌
      responseMode: 'form_post',
      scope: ['openid', 'profile', 'email', 'offline_access'], // 请求的权限
      loggingLevel: 'info',
      passReqToCallback: false,
    });
  }

  async validate(iss: string, sub: string, profile: any, accessToken: string, refreshToken: string, done: Function) {
    // 这里可以把用户信息存入你的数据库,或者生成自己的JWT令牌
    const user = {
      userId: sub,
      email: profile.emails?.[0].value,
      name: profile.displayName,
      accessToken,
      refreshToken,
    };
    done(null, user); // 将用户信息挂载到req.user上
  }
}

五、创建AuthModule和控制器

1. 配置AuthModule

新建auth/auth.module.ts:

import { Module } from '@nestjs/common';
import { PassportModule } from '@nestjs/passport';
import { AzureAdBearerStrategy } from './azure-ad-bearer.strategy';
// 如果用授权码流,还要导入AzureAdOidcStrategy
import { AzureAdOidcStrategy } from './azure-ad-oidc.strategy';
import { AuthController } from './auth.controller';

@Module({
  imports: [PassportModule],
  providers: [AzureAdBearerStrategy, AzureAdOidcStrategy], // 根据场景添加策略
  controllers: [AuthController],
})
export class AuthModule {}

2. 编写AuthController(授权码流用)

新建auth/auth.controller.ts:

import { Controller, Get, Post, UseGuards, Request, Response } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

@Controller('auth')
export class AuthController {
  // 跳转Azure登录页面
  @Get('login')
  @UseGuards(AuthGuard('azure-ad-oidc'))
  async login() {
    // 这个方法会自动触发跳转到Azure的登录页,无需额外代码
  }

  // 登录回调处理
  @Post('callback')
  @UseGuards(AuthGuard('azure-ad-oidc'))
  async callback(@Request() req, @Response() res) {
    // 登录成功后,这里可以生成自己的JWT令牌返回给前端,或者跳转到前端页面
    // 比如:
    // const jwtToken = this.authService.generateJwt(req.user);
    // res.cookie('token', jwtToken, { httpOnly: true });
    res.redirect('/dashboard'); // 跳转到你的前端首页
  }
}

六、保护你的API路由

在需要验证的控制器方法上,使用@UseGuards(AuthGuard('azure-ad')):

import { Controller, Get, UseGuards, Request } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

@Controller('users')
export class UsersController {
  @Get('profile')
  @UseGuards(AuthGuard('azure-ad'))
  getProfile(@Request() req) {
    // req.user就是我们在validate方法里返回的用户信息
    return req.user;
  }
}

七、常见问题及解决方案

我在实现过程中踩过这些坑,给你列出来:

  • 问题1:令牌验证失败,提示「audience不匹配」
    解决:如果是保护API,需要在Azure应用注册里设置「应用ID URI」,然后把策略里的audience改成这个URI;如果是客户端应用,audience填客户端ID即可。
  • 问题2:授权码流回调后获取不到用户邮箱/名称
    解决:检查scope是否包含profile和email,并且在Azure门户里已经授予了对应的权限;另外,确保responseType包含id_token(因为用户信息在ID令牌里)。
  • 问题3:passport-azure-ad版本兼容性问题
    解决:目前passport-azure-ad@4.x和NestJS v10.x兼容良好,避免使用低于v4的版本,安装时可以指定版本:npm install passport-azure-ad@4.x。
  • 问题4:本地开发时HTTPS报错
    解决:Azure AD允许localhost使用HTTP,所以本地开发时重定向URI填http://localhost:xxx即可;如果是部署到服务器,必须用HTTPS。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 01:39:06