2024年,WinUI3 使用 AccountsSettingsPane 获取微软账户信息
背景介绍:UWP 应用可以使用 AccountsSettingsPane 调用系统 UI 实现授权登录功能,相比跳转到网页可以获得更流畅的体验。
起
动手写代码之前,看文档的介绍非常美好。只需要处理 WebAccountProvider 和 WebTokenRequest 对象就能完成授权登录,简直可以说是少有的清晰明了的文档。文档中还提供了 WebAccountManagement Sample,列举了多种授权用法。
承
真的动手开始接入的时候,问题接踵而至。使用官方文档提供的代码和 WebAccountManagement Sample 中的代码都不能成功获取授权。
文档中获取授权的核心逻辑分两段,首先在 AccountsSettingsPane
中配置微软账户的 WebAccountProvider
,然后创建 WebTokenRequest
通过 WebAccountProvider
请求 Token。
官方文档中的 GetMsaTokenAsync 方法是创建 WebTokenRequest
的方法,内容如下:
private async void GetMsaTokenAsync(WebAccountProviderCommand command)
{
WebTokenRequest request = new WebTokenRequest(command.WebAccountProvider, "wl.basic");
WebTokenRequestResult result = await WebAuthenticationCoreManager.RequestTokenAsync(request);
}
执行后 result.ProviderError
值为 WebTokenRequestStatus.ProviderError
,result.ResponseError.ErrorMessage
内提示 0x80070057 参数错误
。
类似的, WebAccountManagement Sample 中给出的代码为 RequestTokenAndSaveAccount 。
此时报错信息变成了 0x80860003 应用程序请求身份验证令牌被禁用或者配置错误
。
可以注意到二者的区别其实只有 clientId
参数不同。
转
通过多多多多多多方查阅资料,发现微软的授权体系一直在变,改名部立大功。而许多历史文档和问答都被微软删掉了,砍刀部立大功。
很久以前的 Live SDK 完全找不到文档了,相关的设置因为微软商店开发者后台变来变去也早已经废弃了,Live SDK 设置似乎在 Application Registration Portal 页面,可以找到一个隐藏的 check box,打勾之后没办法保存配置。这个页面有一个应用程序 ID ,传到上面的 api 里结果依旧失败。
Application Registration Portal 也已经废弃了,微软推荐使用 Microsoft AAD Registered Apps 。
经过观察和实践得知,某个时间之前创建的应用会在 Application Registration Portal 列表中展示,在此之后创建的应用需要在 Microsoft AAD Registered Apps 中进行相关配置。但微软并不会自动创建一个 Azure 账户,所以需要使用与 Partner Center 登录的同一个邮箱注册 Azure 账户,之后在 应用注册 功能的 个人帐户中的应用程序 列表中就能看到对应的应用了。
对于 Microsoft AAD Registered App 中自动创建的应用,它的 Client ID 与 Partner Center 中对应的应用的 MSA 应用 ID 是一致的,都是 GUID 格式,无需任何配置即可使用;
对于 Application Registration Portal 中的应用, MSA 应用 ID 为 16 位数字和大写字母组成的字符串,此类应用无法转移到 Microsoft AAD Registered App 中。微软的授权登录已经不支持这种 Client ID了,所以需要在 Microsoft AAD Registered App 中手动创建一个应用注册,并进行如 文档 所述的相关配置。
使用 Microsoft AAD Registered App 中应用的 Client ID,运行代码结果依旧失败。
注意
创建应用注册时,受支持的账户类型一定要选第三项 任何组织目录(任何 Microsoft Entra ID 租户 - 多租户)中的帐户和个人 Microsoft 帐户(例如 Skype、Xbox) ,否则将不能使用未在"目录"中配置过的账户获得授权,并且应用注册的管理页面 没有任何 UI 选项 可以修改这个配置,唯一的解决方法是在应用注册的清单页面做如下配置
//...
"signInAudience": "AzureADandPersonalMicrosoftAccount",
//...
"api": {
//...
"requestedAccessTokenVersion": 2,
//...
},
//...
这时我注意到微软提供了一个 Microsoft Entra ID 的授权库
Microsoft.Identity.Client
Microsoft.Identity.Client.Broker
使用 文档 中的 BrokerOptions(BrokerOptions.OperatingSystems.Windows)
配置能成功调起系统的账户授权窗口,并且能完成授权拿到用户信息和令牌,但又不想引入额外依赖,故而抓包看了一下请求。
合
根据抓包的结果,在多次尝试之后找到了正确配置 WebTokenRequest 的方法:
var webTokenRequest = new WebTokenRequest(provider, scope: "User.Read openid profile", clientId);
webTokenRequest.Properties.Add("resource", "https://graph.microsoft.com");
webTokenRequest.Properties.Add("api-version", "2.0");
webTokenRequest.Properties.Add("oauth2_batch", "1");
此处 clientId 是 Microsoft AAD Registered Apps 中给出的 应用程序(客户端) ID
,而且注意到 scope 传入 User.Read openid profile
时, result.ResponseData[0].Token
的值为 x-www-form-urlencoded 格式的数据,可以手动解析出 access_token
字段;而 scope 仅传入 User.Read
时, result.ResponseData[0].Token
无任何前后缀,是完整的令牌。建议和 Microsoft.Identity.Client 库保持一致,传入 User.Read openid profile
。
至此,我们已经可以从系统的 AccountsSettingsPane 获取到个人微软账户的 access token 了。
最后提供一套简单的 Win32 应用获取 token 和用户信息的代码:
https://gist.github.com/cnbluefire/4721d8bcf90773bd1bce1344d33f7e7c