#azure-active-directory #blazor-webassembly
#azure-active-directory #blazor-webassembly
Вопрос:
У меня есть проект blazor webassembly, который создается из https://github.com/Azure-Samples/ms-identity-blazor-wasm/tree/main/WebApp-graph-user/Call-MSGraph .
По сути, это проект, который создается при использовании шаблона .net core для приложения Blazor, использующего аутентификацию через AD B2B
dotnet new blazorwasm -au SingleOrg --client-id "{CLIENT ID}" -o {APP NAME} --tenant-id "{TENANT ID}"
Затем я смог вызвать graph.api, когда пользователь вошел в систему. Затем я попытался вызвать свой собственный Api с этой аутентификацией, как описано в https://docs.microsoft.com/en-us/aspnet/core/blazor/security/webassembly/additional-scenarios?view=aspnetcore-3.1.
Я использовал
builder.Services.AddHttpClient<ITestDataService, TestDataService>(
client => client.BaseAddress = new Uri("https://localhost:44342/"))
.AddHttpMessageHandler(x =>
{
var handler = x.GetRequiredService<AuthorizationMessageHandler>()
.ConfigureHandler(new[] { "https://localhost:44342/" },
scopes: new[] { "https://graph.microsoft.com/User.Read" });
return handler;
});
Я вижу, что при вызове Api подключается токен, но проверка подлинности завершается с ошибкой (401). API создается из шаблонов Visual Studio для B2B AD и использует конфигурацию, которая также используется для приложения Blazor.
Это его Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(AzureADDefaults.BearerAuthenticationScheme)
.AddAzureADBearer(options => Configuration.Bind("AzureAd", options));
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseCors(policy =>
policy.WithOrigins("http://localhost:5000",
"https://localhost:5001")
.AllowAnyMethod()
.WithHeaders(HeaderNames.ContentType,
HeaderNames.Authorization,
"x-custom-header")
.AllowCredentials());
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
Вы хоть представляете, чего не хватает?
Полный исходный код доступен на github https://github.com/mathiasfritsch/blazor-calls-api
Ответ №1:
Если вы хотите вызвать Microsoft graph и свой пользовательский API в одном проекте blazor webassembly, мы можем реализовать это, создав другой HTTP-клиент для вызова другого API
Например
- Регистрация приложения API сервера
- Зарегистрируйте приложение AAD для приложения API сервера
- Предоставить API
- Регистрация клиентского приложения
- Регистрация клиентского приложения
- Включить неявный поток предоставления
- Добавьте разрешения API. (Разрешения Graph API и разрешения приложений API)
- Настройка приложения API
Пожалуйста, добавьте следующий код в Startup.cs
public void ConfigureServices(IServiceCollection services)
{
JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
services.AddCors(options =>
{
options.AddDefaultPolicy(
builder => builder.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod());
});
services.AddAuthentication(AzureADDefaults.BearerAuthenticationScheme)
.AddAzureADBearer(options => Configuration.Bind("AzureAd", options));
services.Configure<JwtBearerOptions>(AzureADDefaults.JwtBearerAuthenticationScheme, options =>
{
options.Authority = "/v2.0";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidIssuers = new[] {
$"https://sts.windows.net/{Configuration["AzureAD:TenantId"]}/",
$"https://login.microsoftonline.com/{Configuration["AzureAD:TenantId"]}/v2.0"
},
RoleClaimType = "roles",
// The web API accepts as audiences both the Client ID (options.Audience) and api://{ClientID}.
ValidAudiences = new[]
{
options.Audience,
$"api://{options.Audience}"
}
};
});
....
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.OAuthClientId(Configuration["Swagger:ClientId"]);
c.OAuthScopeSeparator(" ");
c.OAuthAppName("Protected Api");
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
- Настройка клиентского приложения
- Создайте пользовательский обработчик сообщений AuthorizationMessageHandler для Graph API и пользовательского API
// custom API
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
public class CustomAuthorizationMessageHandler : AuthorizationMessageHandler
{
public CustomAuthorizationMessageHandler(IAccessTokenProvider provider,
NavigationManager navigationManager)
: base(provider, navigationManager)
{
ConfigureHandler(
authorizedUrls: new[] { "https://localhost:44300/" },
scopes: new[] { "the API app scope" });
}
}
//Graph API
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
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/User.Read" });
}
}
- Добавьте следующий код в 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<CustomAuthorizationMessageHandler>();
builder.Services.AddScoped<GraphAuthorizationMessageHandler>();
// register HTTP client to call our own api
builder.Services.AddHttpClient("MyAPI", client => client.BaseAddress = new Uri("https://localhost:44300/"))
.AddHttpMessageHandler<CustomAuthorizationMessageHandler>();
// register HTTP client to call graph api
builder.Services.AddHttpClient("GraphAPI", client => client.BaseAddress = new Uri("https://graph.microsoft.com/"))
.AddHttpMessageHandler<GraphAuthorizationMessageHandler>();
builder.Services.AddMsalAuthentication(options =>
{
builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
options.ProviderOptions.DefaultAccessTokenScopes.Add("<the API app scope>");
options.ProviderOptions.AdditionalScopesToConsent.Add("https://graph.microsoft.com/User.Read");
});
await builder.Build().RunAsync();
}
}
- Вызов api
@inject IHttpClientFactory _clientFactory
var httpClient = _clientFactory.CreateClient("<the client name you register>");
await apiClient.GetStringAsync("path");