如何在 Blazor WebAssembly MSAL 中处理多个资源的令牌

Arc*_*igo 8 azure-ad-msal blazor .net-5 blazor-webassembly

这个问题没有真正的解决方案,只有各种解决方法,直到 net7 出现。在 net7 中,我们应该再次能够根据https://github.com/dotnet/aspnetcore/pull/43954在一次调用中从多个源请求范围


我有一个 .net5 blazor web assembly 应用程序,使用 msal auth for azure 设置。

services.AddMsalAuthentication(options =>
{
    configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
    options.ProviderOptions.DefaultAccessTokenScopes.Add("api://xxxxx/API.Access"); // API
    options.ProviderOptions.Cache.CacheLocation = "localStorage";
}); 
Run Code Online (Sandbox Code Playgroud)

这很好用。

此外,我需要访问 Microsoft graph。我已经使用 graph sdk 完成了此操作,并为 graph sdk 提供了身份验证处理程序

public class GraphAuthenticationProvider : IAuthenticationProvider
{
    private readonly NavigationManager _navigationManager;

    public GraphAuthenticationProvider(IAccessTokenProvider tokenProvider, NavigationManager navigationManager)

    {
        TokenProvider = tokenProvider;
        _navigationManager = navigationManager;
    }

    public IAccessTokenProvider TokenProvider { get; }

    public async Task AuthenticateRequestAsync(HttpRequestMessage request)
    {
        string[] scopes = new[] { "https://graph.microsoft.com/Mail.ReadWrite", "https://graph.microsoft.com/Mail.Send" };

        var result = await TokenProvider.RequestAccessToken(
            new AccessTokenRequestOptions()
            {
                Scopes = scopes,
                ReturnUrl = _navigationManager.Uri
            });

        if (result.TryGetToken(out var token))
        {
            request.Headers.Authorization ??= new AuthenticationHeaderValue(
                "Bearer", token.Value);
        }
        else
        {
            _navigationManager.NavigateTo(result.RedirectUrl);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这似乎是根据我能找到的文档执行此操作的方法,尽管它似乎假设您正在尝试在同一资源上获取其他范围。 https://learn.microsoft.com/en-us/aspnet/core/blazor/security/webassemble/additional-scenarios?view=aspnetcore-5.0#request-additional-access-tokens

这有两个问题:

  • 我找不到任何方法可以通过弹出窗口执行此操作,这意味着我被迫导航到重定向,从而导致丢失程序状态。这可以解决,但似乎同意的弹出版本应该是可能的?
  • 当身份验证完成并且我返回到我自己的应用程序时,它会尝试为所有范围(包括我的 api 和图表)创建一个令牌,这显然会因多个资源错误而失败,将我转到登录错误页面。即使它实际上正确地获取了两个令牌,我也可以简单地离开错误页面并访问我的 api 和图表。

我无法找到具有多个资源的 blazor web assembly msal 的任何文档。有人可以解释我做错了什么或指出我正确的文档吗?

public class GraphAuthorizationMessageHandler : AuthorizationMessageHandler
{
    public GraphAuthorizationMessageHandler(IAccessTokenProvider provider, NavigationManager navigationManager)
        : base(provider, navigationManager)
    {
        ConfigureHandler(authorizedUrls: new[] { "https://graph.microsoft.com/" }, scopes: new[] { "https://graph.microsoft.com/Mail.ReadWrite", "https://graph.microsoft.com/Mail.Send" });
    }
}
Run Code Online (Sandbox Code Playgroud)
services.AddScoped<CustomAuthorizationMessageHandler>();
services.AddScoped<GraphAuthenticationProvider>();

services.AddScoped<GraphHttpProvider>();
services.AddScoped<GraphAuthorizationMessageHandler>();

services.AddHttpClient<GraphHttpProvider>(
    client => client.BaseAddress = new Uri("https://graph.microsoft.com"))
    .AddHttpMessageHandler<GraphAuthorizationMessageHandler>();

services.AddScoped(sp =>
     new GraphServiceClient(
        sp.GetRequiredService<GraphAuthenticationProvider>(),
        sp.GetRequiredService<GraphHttpProvider>())
);
Run Code Online (Sandbox Code Playgroud)

编辑:相关的 github 问题https://github.com/dotnet/aspnetcore/issues/33241 - 似乎目前此功能存在缺陷。

ske*_*311 0

解决方法 - 不幸的是,这不是上述问题的解决方案

发布在这里以防对其他人有用。首先,将以下内容注入到您要使用的组件中:

@inject IAccessTokenProvider TokenProvider
Run Code Online (Sandbox Code Playgroud)

登录后,一旦获得服务器授权,您就可以进行以下调用(一旦用户通过标准登录过程对服务器进行身份验证,此操作就会在后台以静默方式完成):

var tokenResult = await TokenProvider.RequestAccessToken(
new AccessTokenRequestOptions
{
    Scopes = new[] { "openid", "offline_access", "https://graph.microsoft.com/.default" }
});
if (tokenResult != null && tokenResult.TryGetToken(out var token))

{
    user.GraphToken = token.Value;

    var response = await Http.PostAsJsonAsync("UserController/graph", user);
    user = await response.Content.ReadFromJsonAsync<UserObj>();
}
Run Code Online (Sandbox Code Playgroud)

在上面的范围数组中,您可以定义您希望为其获取令牌的资源。然后只需在服务器端进行图形调用即可:

public async Task<UserObj> GetMe(string accessToken)
    {
        UserObj response = new UserObj();
        string url = "https://graph.microsoft.com/v1.0/me/?$select=displayName,givenName,department,jobTitle,officeLocation,surname,preferredName,mail";

        HttpWebRequest requestObj = (HttpWebRequest)WebRequest.Create(url);
        requestObj.Method = "Get";
        requestObj.ContentType = "application/json";
        requestObj.Headers.Add("Authorization", "Bearer " + accessToken);

        HttpWebResponse responseObj = null;
        responseObj = (HttpWebResponse)await requestObj.GetResponseAsync();

        if (responseObj.StatusCode != HttpStatusCode.OK)
        {
            using (var stream = responseObj.GetResponseStream())
            using (var reader = new StreamReader(stream))
            {
                response.Error = String.Format("There was an issue: {0}", reader.ReadToEnd());
            }
        }
        else
        {
            using (var stream = responseObj.GetResponseStream())
            using (var reader = new StreamReader(stream))
            {
                string json = reader.ReadToEnd();
                response = JsonConvert.DeserializeObject<UserObj>(json);
            }
        }

        return response;
    }
Run Code Online (Sandbox Code Playgroud)