Blazor Standalone WASM 无法使用 MSAL 获取访问令牌
Blazor Standalone WASM Unable to get Access Token with MSAL
折腾了2天,投入了6h左右,终于决定求助了。
我有一个带有 MSAL 身份验证的独立 Blazor WASM 应用程序,在登录成功并尝试获取访问令牌后我收到错误消息:
blazor.webassembly.js:1 info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[1]
Authorization was successful.
blazor.webassembly.js:1 crit: Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
Unhandled exception rendering component: An exception occurred executing JS interop: The JSON value could not be converted to System.DateTimeOffset. Path: $.token.expires | LineNumber: 0 | BytePositionInLine: 73.. See InnerException for more details.
Microsoft.JSInterop.JSException: An exception occurred executing JS interop: The JSON value could not be converted to System.DateTimeOffset. Path: $.token.expires | LineNumber: 0 | BytePositionInLine: 73.. See InnerException for more details.
---> System.Text.Json.JsonException: The JSON value could not be converted to System.DateTimeOffset. Path: $.token.expires | LineNumber: 0 | BytePositionInLine: 73.
---> System.InvalidOperationException: Cannot get the value of a token type 'Null' as a string.
at System.Text.Json.Utf8JsonReader.TryGetDateTimeOffset(DateTimeOffset& value)
at System.Text.Json.Utf8JsonReader.GetDateTimeOffset()
此错误仅在我登录后显示。
我的设置是 运行 在 .NET 5.0 上,身份验证提供程序是一个 Azure B2C 租户,我将重定向 URI 正确配置为“单页应用程序”并授予“offline_access”和“openid”。
这是我的Program.cs
public class Program
{
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
// Authenticate requests to Function API
builder.Services.AddScoped<APIFunctionAuthorizationMessageHandler>();
//builder.Services.AddHttpClient("MyAPI",
// client => client.BaseAddress = new Uri("<https://my_api_uri>"))
// .AddHttpMessageHandler<APIFunctionAuthorizationMessageHandler>();
builder.Services.AddMudServices();
builder.Services.AddMsalAuthentication(options =>
{
// Configure your authentication provider options here.
// For more information, see https://aka.ms/blazor-standalone-auth
builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
options.ProviderOptions.LoginMode = "redirect";
options.ProviderOptions.DefaultAccessTokenScopes.Add("openid");
options.ProviderOptions.DefaultAccessTokenScopes.Add("offline_access");
});
await builder.Build().RunAsync();
}
}
我特意将 HTTPClient link 注释掉到 AuthorizationMessageHandler。
“AzureAD”配置具有设置为 true 的 Authority、ClientId 和 ValidateAuthority。
public class APIFunctionAuthorizationMessageHandler : AuthorizationMessageHandler
{
public APIFunctionAuthorizationMessageHandler(IAccessTokenProvider provider,
NavigationManager navigationManager)
: base(provider, navigationManager)
{
ConfigureHandler(
authorizedUrls: new[] { "<https://my_api_uri>" });
//scopes: new[] { "FunctionAPI.Read" });
}
}
我已经尝试定义范围,例如 openid 或自定义 API 范围,但现在没有。没有区别。
那么为了引起异常,我所做的只是一些简单的事情:
@code {
private string AccessTokenValue;
protected override async Task OnInitializedAsync()
{
var accessTokenResult = await TokenProvider.RequestAccessToken();
AccessTokenValue = string.Empty;
if (accessTokenResult.TryGetToken(out var token))
{
AccessTokenValue = token.Value;
}
}
}
最后的objective就是这样使用:
try {
var httpClient = ClientFactory.CreateClient("MyAPI");
var resp = await httpClient.GetFromJsonAsync<APIResponse>("api/Function1");
FunctionResponse = resp.Value;
Console.WriteLine("Fetched " + FunctionResponse);
}
catch (AccessTokenNotAvailableException exception)
{
exception.Redirect();
}
但是返回了同样的错误,甚至在运行之前看起来也是如此。
此代码也是 Blazor 组件的 OnInitializedAsync()。
欢迎提出任何想法或建议。
我被困住了,有点绝望。
我怀疑没有从 Azure AD B2C 请求或返回访问令牌,但假设这是 AuthorizationMessageHandler 作业。
不胜感激。
谢谢。
找到问题。
在 JavaScript 端进行了一些调试后,文件 AuthenticationService.js,第 171 行的方法“async getTokenCore(e)”经过美化后,我确认访问令牌实际上是未被 returned,仅 IdToken。
阅读这篇关于向 Azure AD B2C 请求访问令牌的文档时,它提到根据您定义的范围,它将 return 返回给您。
范围“openid”告诉它你需要一个 IdToken,然后“offline_access”告诉它你需要一个刷新令牌,最后有一个漂亮的技巧,你可以定义 App Id 的范围和它将 return 一个访问令牌。
此处有更多详细信息:https://docs.microsoft.com/en-us/azure/active-directory-b2c/access-tokens#openid-connect-scopes
所以我在 Program.cs、builder.Services.AddMsalAuthentication 步骤中更改了我的代码。
现在看起来像这样:
builder.Services.AddMsalAuthentication(options =>
{
// Configure your authentication provider options here.
// For more information, see https://aka.ms/blazor-standalone-auth
builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
options.ProviderOptions.LoginMode = "redirect";
options.ProviderOptions.DefaultAccessTokenScopes.Add("00000000-0000-0000-0000-000000000000");
//options.ProviderOptions.DefaultAccessTokenScopes.Add("openid");
//options.ProviderOptions.DefaultAccessTokenScopes.Add("offline_access");
});
我设置了我在此 Blazor 应用程序上使用的实际应用程序 ID,而不是“00000000-0000-0000-0000-000000000000”。
现在错误没有发生并且访问令牌 returned.
谢谢。
我也很难找到高质量的例子。以下是我如何解决从 Webassembly(托管或独立)应用程序调用 1 个或多个 API 的问题。
大多数 MSFT 示例仅处理一个 Api,因此在通过 AddMsalAuthentication 注册 Msal 时使用 options.ProviderOptions.DefaultAccessTokenScopes 选项。这会将您的令牌锁定到单个受众,当您有多个 api 可以调用时,这不起作用。
相反,从 AuthorizationMessageHandler class 为每个 api 端点派生一个处理程序,在 ConfigureHandler 中设置 authorizedUrl 和 范围,注册名为 HttpClient 的对于 DI 容器中的每个端点,并使用 IHttpClientFactory 生成 HttpClient。
场景:
假设我有一个 WebAssembly 应用程序(托管或独立),它调用多个受保护的 api,包括微软图形 api.
首先,我必须为每个派生自 AuthorizationRequestMessageHandler 的 api 创建一个 class:
Api 1:
// This message handler handles calls to the api at the endpoint "https://localhost:7040". It will generate tokens with the right audience and scope
// "aud": "api://aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
// "scp": "access_as_user",
public class ApiOneAuthorizationRequestMessageHandler : AuthorizationMessageHandler
{
// ILogger if you want..
private readonly ILogger<ApiOneAuthorizationRequestMessageHandler> logger = default!;
public ApiOneAuthorizationRequestMessageHandler(IAccessTokenProvider provider,
NavigationManager navigationManager,
ILoggerFactory loggerFactory
)
: base(provider, navigationManager)
{
logger = loggerFactory.CreateLogger<ApiOneAuthorizationRequestMessageHandler>() ?? throw new ArgumentNullException(nameof(logger));
logger.LogDebug($"Setting up {nameof(ApiOneAuthorizationRequestMessageHandler)} to authorize the base url: {"https://localhost:7090/"}");
ConfigureHandler(
authorizedUrls: new[] { "https://localhost:7040" },
scopes: new[] { "api://aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa/access_as_user" });
}
}
Api 2:
// This message handler handles calls to the api at the endpoint "https://localhost:7090". Check out the scope and audience through https://jwt.io
// "aud": "api://bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb",
// "scp": "access_as_user",
public class ApiTwoAuthorizationRequestMessageHandler : AuthorizationMessageHandler
{
public ApiTwoAuthorizationRequestMessageHandler(IAccessTokenProvider provider,
NavigationManager navigationManager
)
: base(provider, navigationManager)
{
ConfigureHandler(
authorizedUrls: new[] { "https://localhost:7090" },
scopes: new[] { "api://bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb/access_as_user" });
}
}
MS 图 Api:
// This message handler handles calls to Microsoft graph.
// "aud": "00000003-0000-0000-c000-000000000000"
// "scp": "Calendars.ReadWrite email MailboxSettings.Read openid profile User.Read",
public class GraphApiAuthorizationRequestMessageHandler : AuthorizationMessageHandler
{
public GraphApiAuthorizationRequestMessageHandler(IAccessTokenProvider provider,
NavigationManager navigationManager
)
: base(provider, navigationManager)
{
ConfigureHandler(
authorizedUrls: new[] { "https://graph.microsoft.com" },
scopes: new[] { "User.Read", "MailboxSettings.Read", "Calendars.ReadWrite" });
}
}
现在,使用上面的端点 AuthorizationMessageHandler 为每个端点注册一个命名的 HttpClient。在 Program.cs:
中执行此操作
HttpClient 名为“ProductsApi”
//register the AuthorizationRequestMessageHandler
builder.Services.AddScoped<ApiOneAuthorizationRequestMessageHandler>();
//register the named HttpClient
builder.Services.AddHttpClient("ProductsApi",
httpClient => httpClient.BaseAddress = new Uri("https://localhost:7040"))
.AddHttpMessageHandler<ApiOneAuthorizationRequestMessageHandler>();
名为“营销Api”的 HttpClient:
builder.Services.AddScoped<ApiTwoAuthorizationRequestMessageHandler>();
builder.Services.AddHttpClient("MarketingApi",
httpClient => httpClient.BaseAddress = new Uri("https://localhost:7090"))
.AddHttpMessageHandler<ApiTwoAuthorizationRequestMessageHandler>();
HttpClient 名为“MSGraphApi”
builder.Services.AddScoped<GraphApiAuthorizationRequestMessageHandler>();
builder.Services.AddHttpClient("MSGraphApi",
httpClient => httpClient.BaseAddress = new Uri("https://graph.microsoft.com"))
.AddHttpMessageHandler<GraphApiAuthorizationRequestMessageHandler>();
注册您指定的 HttpClient 后,将 Msal 与您的 AzureAd 应用程序设置一起注册到 Program.cs。
没有客户用户声明的 Msal 注册:
builder.Services.AddMsalAuthentication(options =>
{
builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
});
如果您通过图表关注 Microsoft Doc 的自定义用户帐户声明Api,您的添加 Msal 应如下所示:
具有自定义用户声明的 Msal 注册:
builder.Services.AddMsalAuthentication<RemoteAuthenticationState, RemoteUserAccount>(options =>
{
builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
})
.AddAccountClaimsPrincipalFactory<RemoteAuthenticationState, RemoteUserAccount, GraphUserAccountFactory>();
要使用 GraphServiceClient,需要一个 GraphClientFactory。它将需要使用 IHttpClientFactory 来创建正确命名的 HttpClient(例如 MSGraphApi)。
GraphClientFactory:
public class GraphClientFactory
{
private readonly IAccessTokenProviderAccessor accessor;
private readonly IHttpClientFactory httpClientFactory;
private readonly ILogger<GraphClientFactory> logger;
private GraphServiceClient graphClient;
public GraphClientFactory(IAccessTokenProviderAccessor accessor,
IHttpClientFactory httpClientFactory,
ILogger<GraphClientFactory> logger)
{
this.accessor = accessor;
this.httpClientFactory = httpClientFactory;
this.logger = logger;
}
public GraphServiceClient GetAuthenticatedClient()
{
HttpClient httpClient;
if (graphClient == null)
{
httpClient = httpClientFactory.CreateClient("MSGraphApi");
graphClient = new GraphServiceClient(httpClient)
{
AuthenticationProvider = new GraphAuthProvider(accessor)
};
}
return graphClient;
}
}
您还需要在 Program.cs 中注册 GraphClientFactory。
builder.Services.AddScoped<GraphClientFactory>();
要访问市场营销 Api,请注入 IHttpClientFactory 并创建一个命名的 HttpClient。
@inject IHttpClientFactory httpClientFactory
<h3>Example Component</h3>
@code {
protected override async Task OnInitializedAsync()
{
try {
var httpClient = httpClientFactory.CreateClient("MarketingApi");
var resp = await httpClient.GetFromJsonAsync<APIResponse>("api/Function1");
FunctionResponse = resp.Value;
Console.WriteLine("Fetched " + FunctionResponse);
}
catch (AccessTokenNotAvailableException exception)
{
exception.Redirect();
}
}
}
现在,通过访问 MarketingApi,您还可以使用 Graph Api 通过使用此 MSFT 教程页面中描述的组件来访问您的日历:
访问产品Api与访问营销Api非常相似。
我希望这可以帮助人们在 Blazor Webassembly 中使用正确的访问令牌访问 Api。
折腾了2天,投入了6h左右,终于决定求助了。
我有一个带有 MSAL 身份验证的独立 Blazor WASM 应用程序,在登录成功并尝试获取访问令牌后我收到错误消息:
blazor.webassembly.js:1 info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[1]
Authorization was successful.
blazor.webassembly.js:1 crit: Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
Unhandled exception rendering component: An exception occurred executing JS interop: The JSON value could not be converted to System.DateTimeOffset. Path: $.token.expires | LineNumber: 0 | BytePositionInLine: 73.. See InnerException for more details.
Microsoft.JSInterop.JSException: An exception occurred executing JS interop: The JSON value could not be converted to System.DateTimeOffset. Path: $.token.expires | LineNumber: 0 | BytePositionInLine: 73.. See InnerException for more details.
---> System.Text.Json.JsonException: The JSON value could not be converted to System.DateTimeOffset. Path: $.token.expires | LineNumber: 0 | BytePositionInLine: 73.
---> System.InvalidOperationException: Cannot get the value of a token type 'Null' as a string.
at System.Text.Json.Utf8JsonReader.TryGetDateTimeOffset(DateTimeOffset& value)
at System.Text.Json.Utf8JsonReader.GetDateTimeOffset()
此错误仅在我登录后显示。
我的设置是 运行 在 .NET 5.0 上,身份验证提供程序是一个 Azure B2C 租户,我将重定向 URI 正确配置为“单页应用程序”并授予“offline_access”和“openid”。
这是我的Program.cs
public class Program
{
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
// Authenticate requests to Function API
builder.Services.AddScoped<APIFunctionAuthorizationMessageHandler>();
//builder.Services.AddHttpClient("MyAPI",
// client => client.BaseAddress = new Uri("<https://my_api_uri>"))
// .AddHttpMessageHandler<APIFunctionAuthorizationMessageHandler>();
builder.Services.AddMudServices();
builder.Services.AddMsalAuthentication(options =>
{
// Configure your authentication provider options here.
// For more information, see https://aka.ms/blazor-standalone-auth
builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
options.ProviderOptions.LoginMode = "redirect";
options.ProviderOptions.DefaultAccessTokenScopes.Add("openid");
options.ProviderOptions.DefaultAccessTokenScopes.Add("offline_access");
});
await builder.Build().RunAsync();
}
}
我特意将 HTTPClient link 注释掉到 AuthorizationMessageHandler。 “AzureAD”配置具有设置为 true 的 Authority、ClientId 和 ValidateAuthority。
public class APIFunctionAuthorizationMessageHandler : AuthorizationMessageHandler
{
public APIFunctionAuthorizationMessageHandler(IAccessTokenProvider provider,
NavigationManager navigationManager)
: base(provider, navigationManager)
{
ConfigureHandler(
authorizedUrls: new[] { "<https://my_api_uri>" });
//scopes: new[] { "FunctionAPI.Read" });
}
}
我已经尝试定义范围,例如 openid 或自定义 API 范围,但现在没有。没有区别。
那么为了引起异常,我所做的只是一些简单的事情:
@code {
private string AccessTokenValue;
protected override async Task OnInitializedAsync()
{
var accessTokenResult = await TokenProvider.RequestAccessToken();
AccessTokenValue = string.Empty;
if (accessTokenResult.TryGetToken(out var token))
{
AccessTokenValue = token.Value;
}
}
}
最后的objective就是这样使用:
try {
var httpClient = ClientFactory.CreateClient("MyAPI");
var resp = await httpClient.GetFromJsonAsync<APIResponse>("api/Function1");
FunctionResponse = resp.Value;
Console.WriteLine("Fetched " + FunctionResponse);
}
catch (AccessTokenNotAvailableException exception)
{
exception.Redirect();
}
但是返回了同样的错误,甚至在运行之前看起来也是如此。 此代码也是 Blazor 组件的 OnInitializedAsync()。
欢迎提出任何想法或建议。 我被困住了,有点绝望。
我怀疑没有从 Azure AD B2C 请求或返回访问令牌,但假设这是 AuthorizationMessageHandler 作业。
不胜感激。
谢谢。
找到问题。
在 JavaScript 端进行了一些调试后,文件 AuthenticationService.js,第 171 行的方法“async getTokenCore(e)”经过美化后,我确认访问令牌实际上是未被 returned,仅 IdToken。
阅读这篇关于向 Azure AD B2C 请求访问令牌的文档时,它提到根据您定义的范围,它将 return 返回给您。
范围“openid”告诉它你需要一个 IdToken,然后“offline_access”告诉它你需要一个刷新令牌,最后有一个漂亮的技巧,你可以定义 App Id 的范围和它将 return 一个访问令牌。 此处有更多详细信息:https://docs.microsoft.com/en-us/azure/active-directory-b2c/access-tokens#openid-connect-scopes
所以我在 Program.cs、builder.Services.AddMsalAuthentication 步骤中更改了我的代码。
现在看起来像这样:
builder.Services.AddMsalAuthentication(options =>
{
// Configure your authentication provider options here.
// For more information, see https://aka.ms/blazor-standalone-auth
builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
options.ProviderOptions.LoginMode = "redirect";
options.ProviderOptions.DefaultAccessTokenScopes.Add("00000000-0000-0000-0000-000000000000");
//options.ProviderOptions.DefaultAccessTokenScopes.Add("openid");
//options.ProviderOptions.DefaultAccessTokenScopes.Add("offline_access");
});
我设置了我在此 Blazor 应用程序上使用的实际应用程序 ID,而不是“00000000-0000-0000-0000-000000000000”。
现在错误没有发生并且访问令牌 returned.
谢谢。
我也很难找到高质量的例子。以下是我如何解决从 Webassembly(托管或独立)应用程序调用 1 个或多个 API 的问题。
大多数 MSFT 示例仅处理一个 Api,因此在通过 AddMsalAuthentication 注册 Msal 时使用 options.ProviderOptions.DefaultAccessTokenScopes 选项。这会将您的令牌锁定到单个受众,当您有多个 api 可以调用时,这不起作用。
相反,从 AuthorizationMessageHandler class 为每个 api 端点派生一个处理程序,在 ConfigureHandler 中设置 authorizedUrl 和 范围,注册名为 HttpClient 的对于 DI 容器中的每个端点,并使用 IHttpClientFactory 生成 HttpClient。
场景: 假设我有一个 WebAssembly 应用程序(托管或独立),它调用多个受保护的 api,包括微软图形 api.
首先,我必须为每个派生自 AuthorizationRequestMessageHandler 的 api 创建一个 class:
Api 1:
// This message handler handles calls to the api at the endpoint "https://localhost:7040". It will generate tokens with the right audience and scope
// "aud": "api://aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
// "scp": "access_as_user",
public class ApiOneAuthorizationRequestMessageHandler : AuthorizationMessageHandler
{
// ILogger if you want..
private readonly ILogger<ApiOneAuthorizationRequestMessageHandler> logger = default!;
public ApiOneAuthorizationRequestMessageHandler(IAccessTokenProvider provider,
NavigationManager navigationManager,
ILoggerFactory loggerFactory
)
: base(provider, navigationManager)
{
logger = loggerFactory.CreateLogger<ApiOneAuthorizationRequestMessageHandler>() ?? throw new ArgumentNullException(nameof(logger));
logger.LogDebug($"Setting up {nameof(ApiOneAuthorizationRequestMessageHandler)} to authorize the base url: {"https://localhost:7090/"}");
ConfigureHandler(
authorizedUrls: new[] { "https://localhost:7040" },
scopes: new[] { "api://aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa/access_as_user" });
}
}
Api 2:
// This message handler handles calls to the api at the endpoint "https://localhost:7090". Check out the scope and audience through https://jwt.io
// "aud": "api://bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb",
// "scp": "access_as_user",
public class ApiTwoAuthorizationRequestMessageHandler : AuthorizationMessageHandler
{
public ApiTwoAuthorizationRequestMessageHandler(IAccessTokenProvider provider,
NavigationManager navigationManager
)
: base(provider, navigationManager)
{
ConfigureHandler(
authorizedUrls: new[] { "https://localhost:7090" },
scopes: new[] { "api://bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb/access_as_user" });
}
}
MS 图 Api:
// This message handler handles calls to Microsoft graph.
// "aud": "00000003-0000-0000-c000-000000000000"
// "scp": "Calendars.ReadWrite email MailboxSettings.Read openid profile User.Read",
public class GraphApiAuthorizationRequestMessageHandler : AuthorizationMessageHandler
{
public GraphApiAuthorizationRequestMessageHandler(IAccessTokenProvider provider,
NavigationManager navigationManager
)
: base(provider, navigationManager)
{
ConfigureHandler(
authorizedUrls: new[] { "https://graph.microsoft.com" },
scopes: new[] { "User.Read", "MailboxSettings.Read", "Calendars.ReadWrite" });
}
}
现在,使用上面的端点 AuthorizationMessageHandler 为每个端点注册一个命名的 HttpClient。在 Program.cs:
中执行此操作HttpClient 名为“ProductsApi”
//register the AuthorizationRequestMessageHandler
builder.Services.AddScoped<ApiOneAuthorizationRequestMessageHandler>();
//register the named HttpClient
builder.Services.AddHttpClient("ProductsApi",
httpClient => httpClient.BaseAddress = new Uri("https://localhost:7040"))
.AddHttpMessageHandler<ApiOneAuthorizationRequestMessageHandler>();
名为“营销Api”的 HttpClient:
builder.Services.AddScoped<ApiTwoAuthorizationRequestMessageHandler>();
builder.Services.AddHttpClient("MarketingApi",
httpClient => httpClient.BaseAddress = new Uri("https://localhost:7090"))
.AddHttpMessageHandler<ApiTwoAuthorizationRequestMessageHandler>();
HttpClient 名为“MSGraphApi”
builder.Services.AddScoped<GraphApiAuthorizationRequestMessageHandler>();
builder.Services.AddHttpClient("MSGraphApi",
httpClient => httpClient.BaseAddress = new Uri("https://graph.microsoft.com"))
.AddHttpMessageHandler<GraphApiAuthorizationRequestMessageHandler>();
注册您指定的 HttpClient 后,将 Msal 与您的 AzureAd 应用程序设置一起注册到 Program.cs。
没有客户用户声明的 Msal 注册:
builder.Services.AddMsalAuthentication(options =>
{
builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
});
如果您通过图表关注 Microsoft Doc 的自定义用户帐户声明Api,您的添加 Msal 应如下所示:
具有自定义用户声明的 Msal 注册:
builder.Services.AddMsalAuthentication<RemoteAuthenticationState, RemoteUserAccount>(options =>
{
builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
})
.AddAccountClaimsPrincipalFactory<RemoteAuthenticationState, RemoteUserAccount, GraphUserAccountFactory>();
要使用 GraphServiceClient,需要一个 GraphClientFactory。它将需要使用 IHttpClientFactory 来创建正确命名的 HttpClient(例如 MSGraphApi)。
GraphClientFactory:
public class GraphClientFactory
{
private readonly IAccessTokenProviderAccessor accessor;
private readonly IHttpClientFactory httpClientFactory;
private readonly ILogger<GraphClientFactory> logger;
private GraphServiceClient graphClient;
public GraphClientFactory(IAccessTokenProviderAccessor accessor,
IHttpClientFactory httpClientFactory,
ILogger<GraphClientFactory> logger)
{
this.accessor = accessor;
this.httpClientFactory = httpClientFactory;
this.logger = logger;
}
public GraphServiceClient GetAuthenticatedClient()
{
HttpClient httpClient;
if (graphClient == null)
{
httpClient = httpClientFactory.CreateClient("MSGraphApi");
graphClient = new GraphServiceClient(httpClient)
{
AuthenticationProvider = new GraphAuthProvider(accessor)
};
}
return graphClient;
}
}
您还需要在 Program.cs 中注册 GraphClientFactory。
builder.Services.AddScoped<GraphClientFactory>();
要访问市场营销 Api,请注入 IHttpClientFactory 并创建一个命名的 HttpClient。
@inject IHttpClientFactory httpClientFactory
<h3>Example Component</h3>
@code {
protected override async Task OnInitializedAsync()
{
try {
var httpClient = httpClientFactory.CreateClient("MarketingApi");
var resp = await httpClient.GetFromJsonAsync<APIResponse>("api/Function1");
FunctionResponse = resp.Value;
Console.WriteLine("Fetched " + FunctionResponse);
}
catch (AccessTokenNotAvailableException exception)
{
exception.Redirect();
}
}
}
现在,通过访问 MarketingApi,您还可以使用 Graph Api 通过使用此 MSFT 教程页面中描述的组件来访问您的日历:
访问产品Api与访问营销Api非常相似。
我希望这可以帮助人们在 Blazor Webassembly 中使用正确的访问令牌访问 Api。