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.comscp字段包含UserActivity.ReadWrite.CreatedByApp权限
检查UWP应用配置
在Package.appxmanifest中添加“企业身份验证”和“Internet (客户端)”功能;确认应用注册的重定向URI为UWP专用格式(如ms-appx-web://microsoft.aad.brokerplugin/{client-id})。
内容的提问来源于stack exchange,提问作者Valentin Maschenko
相关产品推荐
相关产品推荐

