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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 09:41:29