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

UWP应用调用Microsoft Graph API管理用户活动遇认证及租户错误

UWP应用调用Microsoft Graph API创建/获取用户活动时的认证错误排查

问题概述

在UWP应用中通过Microsoft Graph API创建用户活动时,请求返回AuthenticationError;同时获取用户活动的请求返回UnknownError,提示租户不存在或通用错误。

错误响应详情

创建用户活动初始错误

{"error": {"code": "AuthenticationError","message": "Error authenticating with resource","innerError": {"date": "2022-12-28T09:20:16","request-id": "some id","client-request-id": "some id"}}}

获取用户活动错误

{"error": {"code": "UnknownError","message": "{\"ErrorCode\":2,\"ErrorMessage\":\"Substrate operation failed. Url: https://substrate.office.com/api/v2.0/users('******(73)')/CurrentCollections('******(10)') Status: Unauthorized. Error Code: invalid_tenant Error Message: The tenant for tenant guid 'tenant' does not exist., SubstrateError: null\"}","innerError": {"date": "2022-12-29T05:44:57","request-id": "id","client-request-id": "id"}}}

创建用户活动后续错误

{"error": {"code": "UnknownError","message": "{\"ErrorCode\":35,\"ErrorMessage\":\"General error occurred. Contact product team.\"}","innerError": {"date": "2022-12-29T05:59:02","request-id": "id","client-request-id": "id"}}}

代码实现

public sealed class UserActivityProvider : IUserActivityProvider
{
    private const string activityId = "SendMessageUserActivity";
    private static HttpClient httpClient = new HttpClient();

    public UserActivityProvider()
    {
    }

    private async Task<string> GetAccessTokenAsync(Account account)
    {
        var accessToken = string.Empty;

        var publicClientApplication = PublicClientApplicationBuilder.Create(MicrosoftConstants.ClientId)
           .WithRedirectUri(MicrosoftConstants.RedirectUri)
           .Build();

        var scopes = new string[]
        {
            "UserActivity.ReadWrite.CreatedByApp"
        };

        AuthenticationResult? authToken = null;

        try
        {
            authToken = await publicClientApplication.AcquireTokenSilent(scopes, account.Email).ExecuteAsync();
        }
        catch (Exception)
        {
            authToken = await publicClientApplication.AcquireTokenInteractive(scopes).ExecuteAsync();
        }

        if (authToken != null)
        {
            accessToken = authToken.AccessToken;
        }

        return accessToken;
    }

    public async Task CreateUserActivityAsync(Account account, CreatingMessageUserActivityParameters userActivityParameters)
    {
        var accessToken = await GetAccessTokenAsync(account);
        if (accessToken != string.Empty)
        {
            var contentForCreatingActivity = new StringContent("{\r\n  \"appActivityId\": \"SendMessageUserActivity\",\r\n  \"activitySourceHost\": \"https://www.contoso.com\",\r\n  \"userTimezone\": \"Africa/Casablanca\",\r\n  \"appDisplayName\": \"Contoso, Ltd.\",\r\n  \"activationUrl\": \"https://www.contoso.com/article?id=12345\",\r\n  \"contentUrl\": \"https://www.contoso.com/article?id=12345\",\r\n  \"fallbackUrl\": \"https://www.contoso.com/article?id=12345\",\r\n  \"contentInfo\": {\r\n    \"@context\": \"https://schema.org\",\r\n    \"@type\": \"Article\",\r\n    \"author\": \"Jennifer Booth\",\r\n    \"name\": \"How to Tie a Reef Knot\"\r\n  },\r\n  \"visualElements\": {\r\n    \"attribution\": {\r\n      \"iconUrl\": \"https://www.contoso.com/icon\",\r\n      \"alternateText\": \"Contoso, Ltd.\",\r\n      \"addImageQuery\": false\r\n    },\r\n    \"description\": \"How to Tie a Reef Knot. A step-by-step visual guide to the art of nautical knot-tying.\",\r\n    \"backgroundColor\": \"#ff0000\",\r\n    \"displayText\": \"Contoso How-To: How to Tie a Reef Knot\",\r\n    \"content\": {\r\n      \"$schema\": \"https://adaptivecards.io/schemas/adaptive-card.json\",\r\n      \"type\": \"AdaptiveCard\",\r\n      \"body\": [\r\n        {\r\n          \"type\": \"TextBlock\",\r\n          \"text\": \"Contoso MainPage\"\r\n        }\r\n      ]\r\n    }\r\n  }\r\n}", Encoding.UTF8, "application/json");
            httpClient.DefaultRequestHeaders.Add("Authorization", "Bearer " + accessToken);
            var response = await httpClient.PutAsync($"https://graph.microsoft.com/beta/me/activities/{activityId}", contentForCreatingActivity);
            var stringifiedResponse = await response.Content.ReadAsStringAsync();
        }
    }

    public async Task<string?> IsUserActivityExistsAsync(Account account)
    {
        string? resultSubject = null;

        var accessToken = await GetAccessTokenAsync(account);
        if (accessToken != string.Empty)
        {
            httpClient.DefaultRequestHeaders.Add("Authorization", "Bearer " + accessToken);
            var response = await httpClient.GetAsync("https://graph.microsoft.com/v1.0/me/activities");
            var stringifiedResponse = await response.Content.ReadAsStringAsync();
        }

        return resultSubject;
    }
}

已知信息

已使用官方文档指定的UserActivity.ReadWrite.CreatedByApp权限范围,但问题依然存在。

排查与解决方案

  • 修复HttpClient重复添加Header问题
    静态httpClient会重复添加Authorization头导致请求无效,每次请求前需先移除旧头再添加新头:

    if (httpClient.DefaultRequestHeaders.Contains("Authorization"))
    {
        httpClient.DefaultRequestHeaders.Remove("Authorization");
    }
    httpClient.DefaultRequestHeaders.Add("Authorization", "Bearer " + accessToken);
    
  • 验证租户与账号类型
    错误提示invalid_tenant需确认:

    • 登录账号的租户信息有效,若为个人Microsoft账户,需确保Azure AD应用注册时账户类型设置为“任何组织目录中的账户和个人Microsoft账户”。
  • 统一Graph API版本
    创建活动用beta版本,获取活动用v1.0版本存在兼容性问题,建议统一使用beta版本:

    var response = await httpClient.GetAsync("https://graph.microsoft.com/beta/me/activities");
    
  • 修正ActivitySourceHost值
    activitySourceHost需与应用注册关联的域名或重定向URI主机一致,不能使用示例中的contoso.com,替换为自身应用的有效主机地址。

  • 验证令牌有效性
    解析access token确认:

    • aud(受众)值为https://graph.microsoft.com
    • scp字段包含UserActivity.ReadWrite.CreatedByApp权限
  • 检查UWP应用配置
    在Package.appxmanifest中添加“企业身份验证”和“Internet (客户端)”功能;确认应用注册的重定向URI为UWP专用格式(如ms-appx-web://microsoft.aad.brokerplugin/{client-id})。


内容的提问来源于stack exchange,提问作者Valentin Maschenko

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 17:20:43