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

Nodemailer对接Outlook 365报535 5.7.139认证失败问题咨询

方案实现步骤

你遇到的535报错是因为微软企业Office365租户默认已禁用用户名密码方式的SMTP AUTH认证,即便手动开启也会被多因素认证、条件访问等安全策略拦截,直接使用账号密码的方式已经无法正常使用。此外企业Office365租户默认绑定Azure AD,即便你不使用Azure云服务,要实现无交互服务端发信必须通过Azure AD做应用权限授权,这个是微软的强制要求,没有绕开的办法,以下是最简配置步骤,全程只需操作一次。

1. Azure AD 应用一次性配置

你需要使用企业Office365全局管理员账号完成以下配置:

  • 进入Azure Active Directory管理中心,左侧导航选择「应用注册」→「新注册」
  • 应用名称自定义,受支持的账户类型选择「仅我的组织使用」,重定向URI留空,点击「注册」
  • 记录生成的客户端ID、租户ID两个参数
  • 左侧导航选择「证书和密码」→「新建客户端密码」,选择最长有效期,创建后立即记录客户端密码值(关闭页面后无法再次查看)
  • 左侧导航选择「API权限」→「添加权限」→ 选择「Microsoft Graph」→「应用程序权限」,搜索Mail.Send权限并勾选,点击「授予<你的租户名>管理员同意」完成权限激活
  • (可选安全配置)如果需要限制该应用仅能使用指定共享邮箱B发信,可通过Exchange Online PowerShell执行以下命令,限制应用的发送范围:
New-ApplicationAccessPolicy -AppId <你刚才生成的客户端ID> -PolicyScopeGroupId <共享邮箱B的地址> -AccessRight RestrictAccess -Description "限制邮件应用仅能使用共享邮箱B发信"

2. Node.js 代码实现

推荐直接调用Microsoft Graph API发信,比SMTP协议更稳定,不受SMTP认证策略限制:

安装依赖

npm install @azure/msal-node @microsoft/microsoft-graph-client isomorphic-fetch

代码示例

import * as msal from "@azure/msal-node";
import { Client } from "@microsoft/microsoft-graph-client";
import "isomorphic-fetch";

// 配置参数建议存到环境变量,部署到Heroku时直接配置Config Vars即可
const config = {
  tenantId: process.env.O365_TENANT_ID,
  clientId: process.env.O365_CLIENT_ID,
  clientSecret: process.env.O365_CLIENT_SECRET,
  senderMail: process.env.O365_SENDER_MAIL // 共享邮箱B的地址
};

// 初始化MSAL客户端获取访问凭证
const msalClient = new msal.ConfidentialClientApplication({
  auth: {
    clientId: config.clientId,
    authority: `https://login.microsoftonline.com/${config.tenantId}`,
    clientSecret: config.clientSecret
  }
});

async function getGraphClient() {
  const tokenResponse = await msalClient.acquireTokenByClientCredential({
    scopes: ["https://graph.microsoft.com/.default"]
  });
  if (!tokenResponse) throw new Error("获取访问凭证失败");

  return Client.initWithMiddleware({
    authProvider: {
      getAccessToken: async () => tokenResponse.accessToken
    }
  });
}

// 发信函数
async function sendMail(to: string, subject: string, content: string) {
  const graphClient = await getGraphClient();
  const mailBody = {
    message: {
      subject: subject,
      body: {
        contentType: "Text", // 可以改为HTML支持富文本
        content: content
      },
      toRecipients: [
        {
          emailAddress: {
            address: to
          }
        }
      ]
    },
    saveToSentItems: true // 发送后保存到共享邮箱的已发送文件夹
  };

  await graphClient.api(`/users/${config.senderMail}/sendMail`).post(mailBody);
}

可选:用nodemailer SMTP + OAuth2认证

如果你坚持要用nodemailer的SMTP方式,可以用以下配置替换原来的transporter:

import * as nodemailer from "nodemailer";
import * as msal from "@azure/msal-node";

const msalClient = new msal.ConfidentialClientApplication({
  auth: {
    clientId: config.clientId,
    authority: `https://login.microsoftonline.com/${config.tenantId}`,
    clientSecret: config.clientSecret
  }
});

const transporter = nodemailer.createTransport({
  service: "Outlook365",
  auth: {
    type: "OAuth2",
    user: config.senderMail,
    clientId: config.clientId,
    clientSecret: config.clientSecret,
    accessToken: async () => {
      const res = await msalClient.acquireTokenByClientCredential({
        scopes: ["https://outlook.office365.com/.default"]
      });
      return res?.accessToken;
    }
  }
});

不过这种方式还是可能受SMTP相关策略限制,不如直接调用Graph API稳定。

需求适配说明

  • 要实现从共享邮箱B发信到B,直接在调用sendMail时to参数填共享邮箱B的地址即可
  • 该方案支持向任意外部邮箱地址发信,只要Office365租户没有限制外发权限即可
  • 访问凭证由MSAL库自动刷新,无需手动定期更新,配置完成后服务端可全自动运行,无人工交互

常见问题排查

  • 出现权限报错首先检查是否已经在API权限页面点击了「授予管理员同意」
  • 发信失败检查共享邮箱地址是否正确,是否已经配置了应用访问策略允许该应用使用该邮箱发信
  • 部署到Heroku时不要硬编码密钥,所有敏感参数都放到Heroku的Config Vars环境变量中

内容的提问来源于stack exchange,提问作者Nikola-Milovic

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 20:54:03