Office AddIn SSO下实现On-Behalf-Of Flow对接Dataverse的问题
Office AddIn SSO 接入Dataverse 的 On-Behalf-Of (OBO) 流实现方案
核心原因
Office Runtime返回的accessToken是针对AddIn自身的身份令牌,无法直接用于调用Dataverse API,必须通过OBO流将其兑换为Dataverse专用的访问令牌。
一、前置Azure AD应用配置
- 确保AddIn的Azure AD应用已开启OAuth2.0隐式流(在「身份验证」→「平台配置」中启用)
- 为应用添加Dataverse的委派权限(如
DynamicsCRM.ReadWrite.All),并完成管理员同意 - 记录应用的
客户端ID、客户端密钥以及Dataverse环境的URL(如https://your-env.crm.dynamics.com)
二、后端实现OBO令牌交换(必须步骤,前端无法单独完成)
前端将从Office获取的userTokenEncoded发送至你的后端API,由后端完成令牌兑换:
C# 示例代码
using System.Net.Http.Headers; using System.Text.Json; public class TokenResponse { [JsonPropertyName("access_token")] public string AccessToken { get; set; } } public async Task<string> GetDataverseToken(string oboToken, string tenantId, string clientId, string clientSecret, string dynamicsEnvUrl) { using var client = new HttpClient(); var request = new HttpRequestMessage(HttpMethod.Post, $"https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token"); var formData = new Dictionary<string, string> { ["grant_type"] = "urn:ietf:params:oauth:grant-type:jwt-bearer", ["client_id"] = clientId, ["client_secret"] = clientSecret, ["assertion"] = oboToken, ["scope"] = $"{dynamicsEnvUrl}/.default", ["requested_token_use"] = "on_behalf_of" }; request.Content = new FormUrlEncodedContent(formData); var response = await client.SendAsync(request); response.EnsureSuccessStatusCode(); var tokenResponse = await response.Content.ReadFromJsonAsync<TokenResponse>(); return tokenResponse.AccessToken; }
Node.js 示例代码
const axios = require('axios'); async function getDataverseToken(oboToken, tenantId, clientId, clientSecret, dynamicsEnvUrl) { const params = new URLSearchParams(); params.append('grant_type', 'urn:ietf:params:oauth:grant-type:jwt-bearer'); params.append('client_id', clientId); params.append('client_secret', clientSecret); params.append('assertion', oboToken); params.append('scope', `${dynamicsEnvUrl}/.default`); params.append('requested_token_use', 'on_behalf_of'); const response = await axios.post(`https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`, params); return response.data.access_token; }
三、调用Dataverse API
使用兑换得到的Dataverse访问令牌发起API请求:
C# 示例
public async Task<string> GetDataverseAccounts(string dataverseToken, string dynamicsEnvUrl) { using var client = new HttpClient(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", dataverseToken); var response = await client.GetAsync($"{dynamicsEnvUrl}/api/data/v9.2/accounts"); response.EnsureSuccessStatusCode(); return await response.Content.ReadAsStringAsync(); }
关键注意事项
- 后端必须持有应用的
客户端密钥,严禁在前端代码中暴露该密钥 - 兑换前可验证前端传来的OBO令牌有效性(检查签名、受众、过期时间),避免恶意请求
- 若为多租户应用,可从OBO令牌的
tid字段提取租户ID,无需硬编码
内容的提问来源于stack exchange,提问作者Jason_Hough
相关产品推荐
相关产品推荐

