Office Add-In访问Dataverse Web API的最佳实践:免重复登录与密钥安全
最佳实践:Office Add-In 无重复登录访问 Dataverse Web API(无需 MS Graph/客户端密钥)
核心方案:Office.js SSO 能力 + Azure AD 公共客户端应用注册
完全符合你不想存储客户端密钥的需求,利用 Outlook 已有登录会话实现无缝授权,无需额外跳转登录。
1. Azure AD 应用注册配置
- 注册时选择**公共客户端(移动和桌面应用)**类型,这类应用无需客户端密钥,天然适配前端/Office Add-In 无法安全存储敏感信息的场景
- 添加 API 权限:找到你的 Dataverse 环境对应的 API(格式为
https://<你的组织域名>.crm.dynamics.com),添加user_impersonation权限,建议由管理员批量授予租户级权限,避免用户逐个同意 - 配置重定向 URI:选择「单页应用(SPA)」类型,添加本地开发 URI(如
https://localhost:3000)和生产环境的 Add-In 域名 URI,同时必须添加 Office SSO 专用回调 URI:https://<你的Add-In域名>/auth-callback.html
2. 用 Office.js 直接获取 Dataverse 访问令牌
无需额外第三方包,直接调用 Office.js 内置的 SSO 接口,复用 Outlook 已有的用户会话:
async function getDynamicsAccessToken() { try { const dataverseResource = "https://<你的组织域名>.crm.dynamics.com"; // 请求Dataverse专属令牌,明确不涉及MS Graph const token = await Office.auth.getAccessToken({ allowSignInPrompt: true, // 仅用户未登录时触发登录提示 forMSGraphAccess: false, extraQueryParameters: `resource=${encodeURIComponent(dataverseResource)}` }); return token; } catch (err) { console.error("令牌获取失败:", err); throw err; } }
3. 调用 Dataverse Web API
拿到令牌后,在请求头中携带即可访问数据表:
async function fetchDataverseTable(tableLogicalName) { const token = await getDynamicsAccessToken(); const apiUrl = `https://<你的组织域名>.crm.dynamics.com/api/data/v9.2/${tableLogicalName}`; const response = await fetch(apiUrl, { method: "GET", headers: { "Authorization": `Bearer ${token}`, "OData-MaxVersion": "4.0", "OData-Version": "4.0", "Accept": "application/json" } }); if (!response.ok) { throw new Error(`API 请求失败: ${response.statusText}`); } return await response.json(); }
关键注意事项
- 最小权限原则:仅申请 Dataverse 的
user_impersonation权限,避免过度授权 - 多环境适配:如果需要支持多个 Dataverse 环境,可让用户配置组织 URL,或通过 Office 上下文获取租户 ID 动态拼接资源 URI
- 令牌过期处理:当 API 返回 401 错误时,重新调用
getAccessToken获取新令牌即可
进阶方案:用 MSAL.js 实现更灵活的身份流程
如果需要刷新令牌等高级功能,可使用 @azure/msal-browser,但仍需配置为公共客户端模式,无需客户端密钥:
import { PublicClientApplication } from "@azure/msal-browser"; const msalConfig = { auth: { clientId: "<你的应用客户端ID>", authority: "https://login.microsoftonline.com/<你的租户ID>", redirectUri: "https://<你的Add-In域名>/auth-callback.html" } }; const pca = new PublicClientApplication(msalConfig); async function getTokenWithMSAL() { const resource = "https://<你的组织域名>.crm.dynamics.com"; const request = { scopes: [`${resource}/user_impersonation`] }; try { // 静默复用Outlook登录会话 const resp = await pca.ssoSilent(request); return resp.accessToken; } catch (silentErr) { // 仅当静默失败时弹出登录窗口(极少触发,因为Outlook已有会话) const resp = await pca.loginPopup(request); return resp.accessToken; } }
内容的提问来源于stack exchange,提问作者Jason_Hough
相关产品推荐
相关产品推荐

