Node.js程序使用Outlook发件失败,求助切换至OAuth2认证方案
解决Node.js通过OAuth2认证发送Outlook邮件的步骤
一、在Azure AD注册应用
- 登录Azure门户,进入Azure Active Directory → 应用注册 → 新注册
- 填写应用名称,选择支持的账户类型:
- 若使用个人Outlook邮箱,选「任何组织目录中的账户和个人Microsoft账户」
- 若仅企业内部邮箱,选「此组织目录中的账户」
- 设置重定向URI:无用户交互的后台服务选「公共客户端/本机(移动和桌面)」,填入
https://login.microsoftonline.com/common/oauth2/nativeclient;Web应用填对应回调地址 - 注册完成后,记录客户端ID(Client ID)和租户ID(Tenant ID)(个人邮箱场景用
common作为租户ID)
二、配置应用权限
- 在已注册的应用中,进入API权限 → 添加权限
- 选择Microsoft Graph,根据场景选权限类型:
- 后台服务(无用户交互):选「应用权限」,勾选
Mail.Send,点击「添加权限」后,点击「授予管理员同意」(需企业管理员操作) - 有用户交互场景:选「委托权限」,勾选
Mail.Send,用户首次使用时手动授权即可
- 后台服务(无用户交互):选「应用权限」,勾选
- 确认权限已成功添加并生效
三、生成客户端凭证(仅应用权限场景需要)
- 进入应用的证书和密码 → 客户端密码 → 新客户端密码
- 填写描述和过期时间,点击「添加」,立即记录生成的客户端密码值(离开页面后无法再查看)
四、修改Node.js代码实现OAuth2认证发送邮件
推荐使用nodemailer结合@azure/msal-node获取令牌,以下是两种场景的示例:
场景1:后台服务(客户端凭证流,无用户)
const nodemailer = require('nodemailer'); const { ConfidentialClientApplication } = require('@azure/msal-node'); // Azure AD配置参数 const config = { auth: { clientId: '你的客户端ID', clientSecret: '你的客户端密码', authority: 'https://login.microsoftonline.com/你的租户ID(或common)' } }; const cca = new ConfidentialClientApplication(config); // 获取访问令牌 async function getAccessToken() { const tokenRequest = { scopes: ['https://graph.microsoft.com/.default'] }; const response = await cca.acquireTokenByClientCredential(tokenRequest); return response.accessToken; } // 发送邮件 async function sendEmail() { const accessToken = await getAccessToken(); const transporter = nodemailer.createTransport({ host: 'smtp.office365.com', port: 587, secure: false, auth: { type: 'OAuth2', user: '你的Outlook邮箱地址', clientId: config.auth.clientId, clientSecret: config.auth.clientSecret, tenantId: config.auth.authority.split('/').pop(), accessToken: accessToken } }); const mailOptions = { from: '你的Outlook邮箱地址', to: '收件人邮箱地址', subject: '测试邮件(OAuth2)', text: '这是通过OAuth2认证发送的测试邮件' }; try { await transporter.sendMail(mailOptions); console.log('邮件发送成功'); } catch (error) { console.error('邮件发送失败:', error); } } sendEmail();
场景2:有用户交互(授权码流)
需先获取用户授权码,再交换令牌,适合桌面或Web应用:
const nodemailer = require('nodemailer'); const { PublicClientApplication } = require('@azure/msal-node'); const config = { auth: { clientId: '你的客户端ID', authority: 'https://login.microsoftonline.com/common' } }; const pca = new PublicClientApplication(config); // 生成授权URL(引导用户登录授权) async function getAuthUrl() { const authCodeUrlParameters = { scopes: ['https://graph.microsoft.com/Mail.Send'], redirectUri: 'https://login.microsoftonline.com/common/oauth2/nativeclient' }; const response = await pca.getAuthCodeUrl(authCodeUrlParameters); console.log('请访问此URL授权:', response); // 用户授权后会返回授权码,手动填入下方code变量 } // 用授权码交换令牌并发送邮件 async function sendEmailWithAuthCode(code) { const tokenRequest = { code: code, scopes: ['https://graph.microsoft.com/Mail.Send'], redirectUri: 'https://login.microsoftonline.com/common/oauth2/nativeclient' }; const response = await pca.acquireTokenByCode(tokenRequest); const transporter = nodemailer.createTransport({ host: 'smtp.office365.com', port: 587, secure: false, auth: { type: 'OAuth2', user: '你的Outlook邮箱地址', clientId: config.auth.clientId, refreshToken: response.refreshToken, accessToken: response.accessToken } }); const mailOptions = { from: '你的Outlook邮箱地址', to: '收件人邮箱地址', subject: '测试邮件(OAuth2授权码流)', text: '这是通过授权码流OAuth2认证发送的测试邮件' }; try { await transporter.sendMail(mailOptions); console.log('邮件发送成功'); } catch (error) { console.error('邮件发送失败:', error); } } // 先调用getAuthUrl获取授权URL,拿到code后调用sendEmailWithAuthCode // getAuthUrl(); // sendEmailWithAuthCode('用户授权后得到的code');
五、常见注意事项
- 确保应用权限已正确配置并授予同意,否则会出现令牌权限不足的错误
- 客户端密码和令牌属于敏感信息,不要硬编码在代码中,建议用环境变量管理
- 客户端凭证流适用于后台服务,无需用户干预;授权码流需要用户手动授权,适合有交互的场景
- 令牌有过期时间,应用权限场景下可定期刷新,授权码流可用refreshToken自动刷新
内容的提问来源于stack exchange,提问作者Jerry HTGCN
相关产品推荐
相关产品推荐

