英文:
Blazor WebAssembly GraphClientExtensions fails after upgrading Microsoft.Graph to version 5.0
问题
Blazor WebAssembly GraphClientExtensions 在升级 Microsoft.Graph 到版本 5.0 后出现故障
框架: .NET 7
错误:
-
未找到类型或命名空间名称 'IAuthenticationProvider'
-
未找到类型或命名空间名称 'IHttpProvider'
-
未找到类型或命名空间名称 'ISerializer'
GraphClientExtensions 代码:
internal static class GraphClientExtensions
{
public static IServiceCollection AddGraphClient(
this IServiceCollection services, params string[] scopes)
{
services.Configure<RemoteAuthenticationOptions<MsalProviderOptions>>(
options =>
{
foreach (var scope in scopes)
{
options.ProviderOptions.AdditionalScopesToConsent.Add(scope);
}
});
services.AddScoped<IAuthenticationProvider,
NoOpGraphAuthenticationProvider>();
services.AddScoped<IHttpProvider, HttpClientHttpProvider>(sp =>
new HttpClientHttpProvider(new HttpClient()));
services.AddScoped(sp =>
{
return new GraphServiceClient(
sp.GetRequiredService<IAuthenticationProvider>(),
sp.GetRequiredService<IHttpProvider>());
});
return services;
}
private class NoOpGraphAuthenticationProvider : IAuthenticationProvider
{
public NoOpGraphAuthenticationProvider(IAccessTokenProvider tokenProvider)
{
TokenProvider = tokenProvider;
}
public IAccessTokenProvider TokenProvider { get; }
public async Task AuthenticateRequestAsync(HttpRequestMessage request)
{
var result = await TokenProvider.RequestAccessToken(
new AccessTokenRequestOptions()
{
Scopes = new[] { "https://graph.microsoft.com/User.Read" }
});
if (result.TryGetToken(out var token))
{
request.Headers.Authorization ??= new AuthenticationHeaderValue(
"Bearer", token.Value);
}
}
}
private class HttpClientHttpProvider : IHttpProvider
{
private readonly HttpClient http;
public HttpClientHttpProvider(HttpClient http)
{
this.http = http;
}
public ISerializer Serializer { get; } = new Serializer();
public TimeSpan OverallTimeout { get; set; } = TimeSpan.FromSeconds(300);
public void Dispose()
{
}
public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request)
{
return http.SendAsync(request);
}
public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
HttpCompletionOption completionOption,
CancellationToken cancellationToken)
{
return http.SendAsync(request, completionOption, cancellationToken);
}
}
}
升级以下包导致了错误:
-
从 4.54.0 升级到 5.0.0 的 Microsoft.Graph
-
从 2.0.15 升级到 3.0.0 的 Microsoft.Graph.Core
英文:
Blazor WebAssembly GraphClientExtensions fails after upgrading Microsoft.Graph to version 5.0
Framework: Dot NET 7
Errors:
-
The type or namespace name 'IAuthenticationProvider' could not be found
-
The type or namespace name 'IHttpProvider' could not be found
-
The type or namespace name 'ISerializer' could not be found
GraphClientExtensions Code:
internal static class GraphClientExtensions
{
public static IServiceCollection AddGraphClient(
this IServiceCollection services, params string[] scopes)
{
services.Configure<RemoteAuthenticationOptions<MsalProviderOptions>>(
options =>
{
foreach (var scope in scopes)
{
options.ProviderOptions.AdditionalScopesToConsent.Add(scope);
}
});
services.AddScoped<IAuthenticationProvider,
NoOpGraphAuthenticationProvider>();
services.AddScoped<IHttpProvider, HttpClientHttpProvider>(sp =>
new HttpClientHttpProvider(new HttpClient()));
services.AddScoped(sp =>
{
return new GraphServiceClient(
sp.GetRequiredService<IAuthenticationProvider>(),
sp.GetRequiredService<IHttpProvider>());
});
return services;
}
private class NoOpGraphAuthenticationProvider : IAuthenticationProvider
{
public NoOpGraphAuthenticationProvider(IAccessTokenProvider tokenProvider)
{
TokenProvider = tokenProvider;
}
public IAccessTokenProvider TokenProvider { get; }
public async Task AuthenticateRequestAsync(HttpRequestMessage request)
{
var result = await TokenProvider.RequestAccessToken(
new AccessTokenRequestOptions()
{
Scopes = new[] { "https://graph.microsoft.com/User.Read" }
});
if (result.TryGetToken(out var token))
{
request.Headers.Authorization ??= new AuthenticationHeaderValue(
"Bearer", token.Value);
}
}
}
private class HttpClientHttpProvider : IHttpProvider
{
private readonly HttpClient http;
public HttpClientHttpProvider(HttpClient http)
{
this.http = http;
}
public ISerializer Serializer { get; } = new Serializer();
public TimeSpan OverallTimeout { get; set; } = TimeSpan.FromSeconds(300);
public void Dispose()
{
}
public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request)
{
return http.SendAsync(request);
}
public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
HttpCompletionOption completionOption,
CancellationToken cancellationToken)
{
return http.SendAsync(request, completionOption, cancellationToken);
}
}
}
Upgrading the below packages caused the error:
-
Microsoft.Graph from 4.54.0 to 5.0.0
-
Microsoft.Graph.Core from 2.0.15 to 3.0.0
答案1
得分: 2
I see that you've provided a code snippet and some related information. How can I assist you further with this code or any specific questions you have?
英文:
Just attach a guide for migrating to V5 here.
==============================
First, the issue comes from the changes of the package. There's no IHttpProvider
and ISerializer
in Microsoft.Graph V5.0
, and IAuthenticationProvider
changed from Microsoft.Graph.IAuthenticationProvider
to Microsoft.AspNetCore.Components.WebAssembly.Authentication.IAccessTokenProvider
.
To troubleshoot this issue, I think we need to first integrate AAD into blazor wsam app. We just need to create a new application with picking up the authentication.
Then we add the Graph SDK. But the tutorial in official document hasn't updated. Using code in screenshot below will had issue like what you mentioned.
Now the construction method for GraphServiceClient
is like below:
public GraphServiceClient(IRequestAdapter requestAdapter, string baseUrl = null): base(InitializeRequestAdapterWithBaseUrl(requestAdapter,baseUrl))
{
this.RequestAdapter = requestAdapter;
}
public GraphServiceClient(TokenCredential tokenCredential,IEnumerable<string> scopes = null,string baseUrl = null
):this(new Microsoft.Graph.Authentication.AzureIdentityAuthenticationProvider(tokenCredential, null, null,scopes?.ToArray() ?? Array.Empty<string>()), baseUrl)
{
}
public GraphServiceClient(IAuthenticationProvider authenticationProvider,string baseUrl = null
): this(new BaseGraphRequestAdapter(authenticationProvider, graphClientOptions),baseUrl)
{
}
public GraphServiceClient(HttpClient httpClient, IAuthenticationProvider authenticationProvider = null,
string baseUrl = null):this(new BaseGraphRequestAdapter(authenticationProvider ?? new AnonymousAuthenticationProvider(), graphClientOptions, httpClient: httpClient),baseUrl)
{
}
So we need to re-implement the IAuthenticationProvider
so that we can use it to inject new version GraphServiceClient
. And below is what I did and it worked for me.
In blazor webassembly, we can change to write code like this:
Program.cs:
using BlazorWsamNet7;
using BlazorWsamNet7.Data;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
var baseUrl = string.Join("/",
builder.Configuration.GetSection("MicrosoftGraph")["BaseUrl"],
builder.Configuration.GetSection("MicrosoftGraph")["Version"]);
var scopes = builder.Configuration.GetSection("MicrosoftGraph:Scopes")
.Get<List<string>>();
builder.Services.AddGraphClient(baseUrl, scopes);
builder.Services.AddMsalAuthentication(options =>
{
builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
});
await builder.Build().RunAsync();
Then GraphClientExtensions
:
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using Microsoft.Authentication.WebAssembly.Msal.Models;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Graph;
using Microsoft.Kiota.Abstractions;
using Microsoft.Kiota.Abstractions.Authentication;
using System.IdentityModel.Tokens.Jwt;
using System.Net.Http;
using System.Net.Http.Headers;
namespace BlazorWsamNet7.Data
{
internal static class GraphClientExtensions
{
public static IServiceCollection AddGraphClient(
this IServiceCollection services, string? baseUrl, List<string>? scopes)
{
services.Configure<RemoteAuthenticationOptions<MsalProviderOptions>>(
options =>
{
scopes?.ForEach((scope) =>
{
options.ProviderOptions.AdditionalScopesToConsent.Add(scope);
});
});
services.AddScoped<IAuthenticationProvider, GraphAuthenticationProvider>();
services.AddScoped(sp =>
{
return new GraphServiceClient(
new HttpClient(),
sp.GetRequiredService<IAuthenticationProvider>(),
baseUrl);
});
return services;
}
private class GraphAuthenticationProvider : IAuthenticationProvider
{
private readonly IConfiguration config;
public GraphAuthenticationProvider(Microsoft.AspNetCore.Components.WebAssembly.Authentication.IAccessTokenProvider tokenProvider,
IConfiguration config)
{
TokenProvider = tokenProvider;
this.config = config;
}
public Microsoft.AspNetCore.Components.WebAssembly.Authentication.IAccessTokenProvider TokenProvider { get; }
public async Task AuthenticateRequestAsync(RequestInformation request, Dictionary<string, object>? additionalAuthenticationContext = null, CancellationToken cancellationToken = default)
{
var result = await TokenProvider.RequestAccessToken(
new AccessTokenRequestOptions()
{
Scopes = config.GetSection("MicrosoftGraph:Scopes").Get<string[]>()
});
if (result.TryGetToken(out var token))
{
//request.Headers.Authorization ??= new AuthenticationHeaderValue("Bearer", token.Value);
request.Headers.Add("Authorization", "Bearer "+token.Value);
}
}
}
}
}
appsetting.json
:
{
"AzureAd": {
"Authority": "https://login.microsoftonline.com/tenant_id",
"ClientId": "aad_client_id",
"ValidateAuthority": true
},
"MicrosoftGraph": {
"BaseUrl": "https://graph.microsoft.com",
"Version": "v1.0",
"Scopes": [
"user.read"
]
}
}
my View razor component:
@page "/profile"
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
@using Microsoft.Graph;
@using Microsoft.Graph.Models;
@inject GraphServiceClient GraphClient
@attribute [Authorize]
<h3>UserProfile</h3>
@{
<table class="table">
<thead>
<tr>
<th>Property</th>
<th>Value</th>
</tr>
</thead>
<tr>
<td> DisplayName </td>
<td> @_user.DisplayName </td>
</tr>
<tr>
<td> UserPrincipalName </td>
<td> @_user.UserPrincipalName </td>
</tr>
</table>
}
@code {
protected User _user = new User();
protected override async Task OnInitializedAsync()
{
try
{
_user = await GraphClient.Me.GetAsync();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
Test result:
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论