React应用实现Microsoft单点登录后,如何对接Node.js后端同步身份?
React + Node/Express 同步Azure AD登录及Graph API邮件发送实现
针对你已完成React端MSAL单点登录,需同步Node后端身份以调用Microsoft Graph API发送邮件的需求,以下是落地步骤:
一、Azure AD应用注册调整
- 在现有应用注册中添加Web平台,配置后端重定向URI(示例:
http://localhost:3001/auth/callback) - 启用授权码流:勾选"授权代码"和"ID令牌"选项
- 添加Graph API委托权限:
Mail.Send,并完成管理员授权(租户内应用需此步骤)
二、React前端修改:传递授权码到后端
前端已基于@azure/msal-browser/@azure/msal-react实现登录,需调整为授权码流并将授权码传给后端:
- 调整MSAL配置为授权码流:
const msalConfig = { auth: { clientId: "你的客户端ID", authority: "https://login.microsoftonline.com/你的租户ID", redirectUri: "http://localhost:3000", // 前端原有重定向URI responseType: "code" // 切换为授权码流 } };
- 登录成功后获取授权码并调用后端接口:
// 登录成功回调或组件逻辑中 const handleLoginSuccess = (response) => { const authCode = response.code; fetch("http://localhost:3001/auth/exchange-code", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ code: authCode }) }) .then(res => res.json()) .then(data => { // 可将后端返回的会话标识存到localStorage或状态管理中 localStorage.setItem("userSession", JSON.stringify(data)); }) .catch(err => console.error("授权码传递失败:", err)); };
三、Node/Express后端实现
使用@azure/msal-node处理授权码兑换令牌,并调用Graph API发送邮件:
- 安装依赖:
npm install @azure/msal-node express cors
- 核心代码实现:
const express = require("express"); const cors = require("cors"); const { ConfidentialClientApplication } = require("@azure/msal-node"); const app = express(); app.use(cors()); app.use(express.json()); // MSAL客户端配置 const msalConfig = { auth: { clientId: "你的客户端ID", authority: "https://login.microsoftonline.com/你的租户ID", clientSecret: "你的客户端密钥" // 从Azure应用注册的"证书和密码"中获取 } }; const cca = new ConfidentialClientApplication(msalConfig); const REDIRECT_URI = "http://localhost:3001/auth/callback"; // 与Azure配置一致 const GRAPH_SCOPES = ["Mail.Send"]; // 兑换授权码为Graph API访问令牌 app.post("/auth/exchange-code", async (req, res) => { const { code } = req.body; try { const tokenRequest = { code, redirectUri: REDIRECT_URI, scopes: GRAPH_SCOPES }; const tokenResponse = await cca.acquireTokenByCode(tokenRequest); // 生产环境建议用express-session存储令牌,而非直接返回给前端 res.json({ accessToken: tokenResponse.accessToken, accountInfo: tokenResponse.account }); } catch (err) { res.status(500).json({ error: err.message }); } }); // 调用Graph API发送邮件接口 app.post("/api/send-email", async (req, res) => { const { accessToken, toEmail, subject, content } = req.body; try { const graphRes = await fetch("https://graph.microsoft.com/v1.0/me/sendMail", { method: "POST", headers: { "Authorization": `Bearer ${accessToken}`, "Content-Type": "application/json" }, body: JSON.stringify({ message: { subject, body: { contentType: "Text", content }, toRecipients: [{ emailAddress: { address: toEmail } }] }, saveToSentItems: true }) }); if (graphRes.ok) { res.json({ success: true, msg: "邮件发送成功" }); } else { const graphErr = await graphRes.json(); res.status(400).json({ success: false, error: graphErr }); } } catch (err) { res.status(500).json({ success: false, error: err.message }); } }); app.listen(3001, () => console.log("后端服务运行于 http://localhost:3001"));
四、核心注意事项
- 前端与后端的
clientId、tenantId必须完全一致,后端clientSecret需从Azure应用注册的"证书和密码"生成 - 授权码仅能使用一次,后端兑换令牌后即失效
- 生产环境必须使用HTTPS传输,避免令牌泄露
- 敏感令牌建议存储在后端会话中(如
express-session+Redis),不要直接返回给前端长期存储
内容的提问来源于stack exchange,提问作者dmikester1
相关产品推荐
相关产品推荐

