Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Este guia mostra como proteger um aplicativo distribuído .NET Aspire com autenticação e autorização Microsoft Entra ID. Isso abrange:
-
Front-end do Blazor Server (
MyService.Web): Entrada do usuário com OpenID Connect e aquisição de token -
Back-end protegido da API (
MyService.ApiService): validação JWT usando Microsoft.Identity.Web - Fluxo de ponta a ponta: o Blazor adquire tokens de acesso e chama a API protegida com a descoberta do serviço Aspire
Este guia pressupõe que você começou com um projeto Aspire criado usando o seguinte comando:
aspire new aspire-starter --name MyService
Pré-requisitos
- .NET 9 SDK ou posterior
- .NET Aspire CLI - consulte Instalar a CLI Aspire
- Microsoft Entra tenant — consulte Registrar aplicativos no Microsoft Entra ID para configuração
Dica
Novo no Aspire? Consulte a visão geral do .NET Aspire.
Entender o fluxo de trabalho de duas fases
Este guia segue uma abordagem de duas fases:
| Fase | O que acontece | Resultado |
|---|---|---|
| Fase 1 | Adicione código de autenticação com valores de placeholder | O aplicativo compila, mas não é executado |
| Fase 2 | Provisionar registros de aplicativo Microsoft Entra | O aplicativo é executado com autenticação real |
Registrar aplicativos no Microsoft Entra ID
Antes que seu aplicativo possa autenticar usuários, você precisa de dois registros de aplicativo no Microsoft Entra:
| Registro de Aplicativo | Propósito | Configuração de chave |
|---|---|---|
API (MyService.ApiService) |
Valida tokens de entrada | URI da ID do aplicativo, access_as_user escopo |
Aplicativo Web (MyService.Web) |
Faz login de usuários, obtém tokens | URIs de redirecionamento, segredo do cliente, permissões de API |
Se você já tiver registros de aplicativo configurados, você precisará desses valores para:appsettings.json
- TenantId — ID de locatário do Microsoft Entra
- API ClientId — ID do aplicativo (cliente) do registro do aplicativo de API
-
URI da ID do Aplicativo de API — Geralmente
api://<api-client-id>(usado emAudienceseScopes) - ID do Cliente do Aplicativo Web — ID do aplicativo (cliente) do registro do Aplicativo Web
- Segredo do Cliente (ou certificado) – Credencial para o aplicativo Web (armazenar em segredos do usuário, não appsettings.json)
-
Escopos — os escopos das solicitações do seu aplicativo web, por exemplo,
api://<api-client-id>/.defaultouapi://<api-client-id>/access_as_user
Etapa 1: Registrar a API
- Vá para centro de administração do Microsoft Entra>Identity>Applications>Registros de aplicativo.
- Selecione Novo registro.
-
Nome:
MyService.ApiService - Tipos de conta com suporte: Contas somente neste diretório organizacional (locatário único)
- Selecione Registrar.
-
Nome:
- Vá para Expor uma API> e Adicionar ao lado do URI da ID do Aplicativo.
- Aceite o padrão (
api://<client-id>) ou personalize-o. - Selecione Adicionar um escopo:
-
Nome do escopo:
access_as_user - Quem pode consentir: Administradores e usuários
- Nome de exibição de consentimento do administrador: Acessar a API do MyService
- Descrição do consentimento do administrador: Permite que o aplicativo acesse a API MyService em nome do usuário conectado.
- Selecione Adicionar escopo.
-
Nome do escopo:
- Aceite o padrão (
- Copie a ID do aplicativo (cliente) – você precisará disso para ambos os
appsettings.jsonarquivos.
Para obter mais informações, consulte Início Rápido: Configurar um aplicativo para expor uma API Web.
Etapa 2: Registrar o aplicativo Web
- Acesse Registros de aplicativo>Novo registro.
-
Nome:
MyService.Web - Tipos de conta com suporte: Somente contas neste diretório organizacional
-
URI de redirecionamento: Selecione Web e insira a URL do aplicativo +
/signin-oidc- Para desenvolvimento local:
https://localhost:7001/signin-oidc(verifique sualaunchSettings.jsonporta real)
- Para desenvolvimento local:
- Selecione Registrar.
-
Nome:
- Vá para Autenticação>Adicionar URI para adicionar todas as URLs de desenvolvimento (de
launchSettings.json). - Vá para
Certificados & segredos Segredos do Cliente Novo segredo do cliente .- Adicione uma descrição e uma expiração.
- Copie o valor do segredo imediatamente – ele não será mostrado novamente.
- Vá para permissões> de APIAdicionar uma permissão>Minhas APIs.
- Selecione
MyService.ApiService. - Selecione
access_as_user>Adicionar permissões. - Selecione Conceder consentimento do administrador para [locatário] (ou os usuários são solicitados no primeiro uso).
- Selecione
- Copie a ID do aplicativo (cliente) para o
appsettings.jsonaplicativo Web.
Observação
Algumas organizações não permitem segredos do cliente. Para obter alternativas, consulte credenciais de certificado ou autenticação sem certificado.
Para obter mais informações, consulte Início Rápido: Registrar um aplicativo.
Etapa 3: atualizar a configuração
Depois de criar os registros do aplicativo, atualize seus appsettings.json arquivos:
API (MyService.ApiService/appsettings.json):
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "YOUR_TENANT_ID",
"ClientId": "YOUR_API_CLIENT_ID",
"Audiences": ["api://YOUR_API_CLIENT_ID"]
}
}
Aplicativo Web (MyService.Web/appsettings.json):
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "YOUR_TENANT_ID",
"ClientId": "YOUR_WEB_CLIENT_ID",
"CallbackPath": "/signin-oidc",
"ClientCredentials": [
{ "SourceType": "ClientSecret" }
]
},
"WeatherApi": {
"Scopes": ["api://YOUR_API_CLIENT_ID/.default"]
}
}
Armazene o segredo com segurança:
cd MyService.Web
dotnet user-secrets set "AzureAd:ClientCredentials:0:ClientSecret" "YOUR_SECRET_VALUE"
| Valor | Onde encontrar |
|---|---|
TenantId |
centro de administração do Microsoft Entra > Visão geral > ID do locatário |
API ClientId |
Registro de aplicativos > MyService.ApiService > ID do aplicativo (cliente) |
Web ClientId |
Registros de aplicativos > MyService.Web > ID do aplicativo (cliente) |
Client Secret |
Criado na Etapa 2 (copiar imediatamente quando criado) |
Observação
O modelo de início do Aspire cria automaticamente uma WeatherApiClient classe no MyService.Web projeto. Esse HttpClient digitado é usado em todo este guia para demonstrar a chamada à API protegida. Você não precisa criar essa classe por conta própria – ela faz parte do modelo.
Comece rapidamente
Esta seção fornece uma referência condensada para adicionar autenticação. Para obter instruções passo a passo detalhadas, consulte a Parte 1 e a Parte 2.
API (MyService.ApiService)
Instale o pacote NuGet Microsoft.Identity.Web:
dotnet add package Microsoft.Identity.Web
Adicione a configuração de Microsoft Entra a appsettings.json:
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "<tenant-id>",
"ClientId": "<api-client-id>",
"Audiences": ["api://<api-client-id>"]
}
}
Registrar autenticação e autorização em Program.cs:
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));
builder.Services.AddAuthorization();
// ...
app.UseAuthentication();
app.UseAuthorization();
// ...
app.MapGet("/weatherforecast", () => { /* ... */ }).RequireAuthorization();
Aplicativo Web (MyService.Web)
Instale o pacote NuGet Microsoft.Identity.Web:
dotnet add package Microsoft.Identity.Web
Adicione a configuração de Microsoft Entra a appsettings.json:
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "<tenant-id>",
"ClientId": "<web-client-id>",
"CallbackPath": "/signin-oidc",
"ClientCredentials": [{ "SourceType": "ClientSecret" }]
},
"WeatherApi": { "Scopes": ["api://<api-client-id>/.default"] }
}
Configure a autenticação, a aquisição de tokens e o cliente de API downstream em Program.cs:
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddInMemoryTokenCaches();
builder.Services.AddCascadingAuthenticationState();
builder.Services.AddScoped<BlazorAuthenticationChallengeHandler>();
builder.Services.AddHttpClient<WeatherApiClient>(client =>
client.BaseAddress = new("https+http://apiservice"))
.AddMicrosoftIdentityMessageHandler(builder.Configuration.GetSection("WeatherApi"));
// ...
app.UseAuthentication();
app.UseAuthorization();
app.MapGroup("/authentication").MapLoginAndLogout();
O MicrosoftIdentityMessageHandler adquire tokens automaticamente e os anexa, e o BlazorAuthenticationChallengeHandler lida com os desafios de consentimento e de Acesso Condicional.
Importante
Não se esqueça de criar UserInfo.razor para o botão de logon. Consulte Adicionar componentes da interface do usuário do Blazor para obter detalhes.
Observação
BlazorAuthenticationChallengeHandler e LoginLogoutEndpointRouteBuilderExtensions são incluídos em Microsoft.Identity.Web (v3.3.0+). Nenhuma cópia de arquivo é necessária.
Identificar arquivos a serem modificados
A tabela a seguir lista os arquivos que você altera em cada projeto:
| Projeto | Arquivo | Changes |
|---|---|---|
| ApiService | Program.cs |
Autenticação do Portador JWT, middleware de autorização |
appsettings.json |
Configuração do Microsoft Entra | |
.csproj |
Adicionar Microsoft.Identity.Web |
|
| Web | Program.cs |
Autenticação OIDC, aquisição de token, BlazorAuthenticationChallengeHandler |
appsettings.json |
Configuração do Microsoft Entra: escopos de API downstream | |
.csproj |
Adicionar Microsoft.Identity.Web (v3.3.0+) |
|
Components/UserInfo.razor |
UI do botão de login (novo arquivo) | |
Components/Layout/MainLayout.razor |
Incluir o componente UserInfo | |
Components/Routes.razor |
AuthorizeRouteView para páginas protegidas | |
| Páginas fazendo chamadas para APIs | Experimentar/capturar com ChallengeHandler |
Entender o fluxo de autenticação
O diagrama a seguir mostra como o front-end blazor, Microsoft Entra e a API protegida interagem:
flowchart LR
A[User Browser] -->|1 Login OIDC| B[Blazor Server<br/>MyService.Web]
B -->|2 Redirect| C[Microsoft Entra ID]
C -->|3 auth code| B
B -->|4 exchange auth code| C
C -->|5 tokens| B
B -->|6 cookie + session| A
B -->|7 HTTP + Bearer token| D[ASP.NET API<br/>MyService.ApiService<br/>Microsoft.Identity.Web]
D -->|8 Validate JWT| C
D -->|9 Weather data| B
- O usuário visita o aplicativo Blazor → Não autenticado → vê o botão "Logon".
-
User seleciona Login → Redireciona para
/authentication/login→ desafio OIDC no Microsoft Entra. -
Usuário entra → Microsoft Entra redireciona para
/signin-oidc→ cookie configurado. -
O usuário navega até a página Clima → Blazor chama
WeatherApiClient.GetAsync(). -
MicrosoftIdentityMessageHandlerintercepta a solicitação, adquire um token do cache (ou atualiza silenciosamente) e anexa oAuthorization: Bearer <token>cabeçalho. - API recebe solicitação → Microsoft. Identity.Web valida o JWT → retorna dados.
- Blazor renderiza dados meteorológicos.
Examinar a estrutura da solução
O modelo de início do Aspire cria o seguinte layout de projeto:
MyService/
├── MyService.AppHost/ # Aspire orchestration
├── MyService.ApiService/ # Protected API (Microsoft.Identity.Web)
├── MyService.Web/ # Blazor Server (Microsoft.Identity.Web)
├── MyService.ServiceDefaults/ # Shared defaults
└── MyService.Tests/ # Tests
Parte 1: proteger o back-end da API com Microsoft. Identity.Web
Esta seção configura o projeto de API para validar tokens do Portador JWT emitidos pelo Microsoft Entra.
Adicione o pacote Microsoft.Identity.Web
Executar o seguinte comando para instalar o pacote Microsoft.Identity.Web do NuGet.
cd MyService.ApiService
dotnet add package Microsoft.Identity.Web
Definir configurações de Microsoft Entra
Adicione a configuração de Microsoft Entra a MyService.ApiService/appsettings.json:
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "<your-tenant-id>",
"ClientId": "<your-api-client-id>",
"Audiences": [
"api://<your-api-client-id>"
]
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
Propriedades principais
-
ClientId: ID de registro do aplicativo de API Microsoft Entra -
TenantId: ID do locatário do Microsoft Entra, ou"organizations"para o multilocatário, ou"common"para qualquer conta Microsoft -
Audiences: públicos válidos de token (normalmente seu URI do ID do aplicativo)
Atualizar o arquivo Program.cs da API
Substitua o conteúdo do MyService.ApiService/Program.cs pelo seguinte código para adicionar a autenticação JWT Bearer e proteger pontos de extremidade:
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Identity.Web;
var builder = WebApplication.CreateBuilder(args);
builder.AddServiceDefaults();
// Add Microsoft.Identity.Web JWT Bearer authentication
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));
builder.Services.AddProblemDetails();
builder.Services.AddOpenApi();
builder.Services.AddAuthorization();
var app = builder.Build();
app.UseExceptionHandler();
app.UseAuthentication();
app.UseAuthorization();
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
string[] summaries = ["Freezing", "Bracing", "Chilly", "Cool", "Mild",
"Warm", "Balmy", "Hot", "Sweltering", "Scorching"];
app.MapGet("/", () =>
"API service is running. Navigate to /weatherforecast to see sample data.");
app.MapGet("/weatherforecast", () =>
{
var forecast = Enumerable.Range(1, 5).Select(index =>
new WeatherForecast
(
DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
Random.Shared.Next(-20, 55),
summaries[Random.Shared.Next(summaries.Length)]
))
.ToArray();
return forecast;
})
.WithName("GetWeatherForecast")
.RequireAuthorization();
app.MapDefaultEndpoints();
app.Run();
record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
{
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}
Principais alterações:
- Registrar autenticação do Portador JWT com
AddMicrosoftIdentityWebApi - Adicionar
app.UseAuthentication()eapp.UseAuthorization()middleware - Aplique a
.RequireAuthorization()endpoints protegidos
Testar a API protegida
Verifique se a API rejeita solicitações não autenticadas e aceita tokens válidos.
Enviar uma solicitação sem um token:
curl https://localhost:<PORT>/weatherforecast
# Expected: 401 Unauthorized
Enviar uma solicitação com um token válido:
curl -H "Authorization: Bearer <TOKEN>" https://localhost:<PORT>/weatherforecast
# Expected: 200 OK with weather data
Parte 2: Configurar o frontend Blazor para autenticação
O aplicativo Blazor Server usa Microsoft. Identity.Web para:
- Conectar usuários com o OIDC
- Adquirir tokens de acesso para chamar a API
- Anexar tokens a solicitações HTTP de saída
Adicione o pacote Microsoft.Identity.Web
Execute o seguinte comando para instalar o pacote NuGet Microsoft.Identity.Web:
cd MyService.Web
dotnet add package Microsoft.Identity.Web
Definir configurações de Microsoft Entra
Adicione a configuração do Microsoft Entra e os escopos da API downstream em MyService.Web/appsettings.json:
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "<your-tenant>.onmicrosoft.com",
"TenantId": "<tenant-guid>",
"ClientId": "<web-app-client-id>",
"CallbackPath": "/signin-oidc",
"ClientCredentials": [
{
"SourceType": "ClientSecret",
"ClientSecret": "<your-client-secret>"
}
]
},
"WeatherApi": {
"Scopes": [ "api://<api-client-id>/.default" ]
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
Detalhes da configuração:
-
ClientId: ID de registro do aplicativo Web (não a ID da API) -
ClientCredentials: credenciais para que o aplicativo web possa adquirir tokens. Dá suporte a vários tipos de credencial. Confira a visão geral de credenciais para opções prontas para produção. -
Scopes: deve corresponder ao URI do App ID da API com o sufixo/.default
Aviso
Para produção, use certificados ou identidade gerenciada em vez de segredos do cliente. Consulte a autenticação sem certificado para a abordagem recomendada.
Atualizar o aplicativo Web Program.cs
Substitua o conteúdo de MyService.Web/Program.cs pelo seguinte código para configurar a autenticação OIDC, a aquisição de token e o cliente de API downstream:
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.Identity.Abstractions;
using Microsoft.Identity.Web;
using MyService.Web;
using MyService.Web.Components;
var builder = WebApplication.CreateBuilder(args);
builder.AddServiceDefaults();
// Authentication + Microsoft Identity Web
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddInMemoryTokenCaches();
builder.Services.AddCascadingAuthenticationState();
// Blazor components
builder.Services.AddRazorComponents().AddInteractiveServerComponents();
// Blazor authentication challenge handler for incremental consent and Conditional Access
builder.Services.AddScoped<BlazorAuthenticationChallengeHandler>();
builder.Services.AddOutputCache();
// Downstream API client with MicrosoftIdentityMessageHandler
builder.Services.AddHttpClient<WeatherApiClient>(client =>
{
// Aspire service discovery: resolves "apiservice" at runtime
client.BaseAddress = new("https+http://apiservice");
})
.AddMicrosoftIdentityMessageHandler(builder.Configuration.GetSection("WeatherApi"));
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error", createScopeForErrors: true);
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.UseAntiforgery();
app.UseOutputCache();
app.MapStaticAssets();
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode();
// Login/Logout endpoints with incremental consent support
app.MapGroup("/authentication").MapLoginAndLogout();
app.MapDefaultEndpoints();
app.Run();
Pontos principais:
-
AddMicrosoftIdentityWebApp: configura a autenticação OIDC -
EnableTokenAcquisitionToCallDownstreamApi: habilita a aquisição de token para APIs downstream -
AddScoped<BlazorAuthenticationChallengeHandler>: manipula o consentimento incremental e o acesso condicional no Servidor Blazor -
AddMicrosoftIdentityMessageHandler: Anexa tokens portadores às solicitações do HttpClient automaticamente -
https+http://apiservice: a descoberta de serviço do Aspire resolve isso na URL real da API -
Ordem do middleware:
UseAuthentication()→UseAuthorization()pontos de extremidade →
A AddMicrosoftIdentityMessageHandler extensão dá suporte a vários padrões de configuração:
Opção 1: Configuração de appsettings.json (mostrada anteriormente)
.AddMicrosoftIdentityMessageHandler(builder.Configuration.GetSection("WeatherApi"));
Opção 2: configuração embutida com o delegado de ação
.AddMicrosoftIdentityMessageHandler(options =>
{
options.Scopes.Add("api://<api-client-id>/.default");
});
Opção 3: Configuração por solicitação (sem parâmetros)
.AddMicrosoftIdentityMessageHandler();
// Then in your service, configure per-request:
var request = new HttpRequestMessage(HttpMethod.Get, "/weatherforecast")
.WithAuthenticationOptions(options =>
{
options.Scopes.Add("api://<api-client-id>/.default");
});
var response = await _httpClient.SendAsync(request);
Adicionar componentes da interface do usuário do Blazor
Importante
Esta etapa é frequentemente esquecida. Sem o componente UserInfo, os usuários não têm como entrar.
BlazorAuthenticationChallengeHandler e LoginLogoutEndpointRouteBuilderExtensions disponíveis em Microsoft.Identity.Web v3.3.0+. Elas estarão disponíveis automaticamente depois que você referenciar o pacote. Nenhuma cópia de arquivo é necessária.
Criar MyService.Web/Components/UserInfo.razor:
@using Microsoft.AspNetCore.Components.Authorization
<AuthorizeView>
<Authorized>
<span class="nav-item">Hello, @context.User.Identity?.Name</span>
<form action="/authentication/logout" method="post" class="nav-item">
<AntiforgeryToken />
<input type="hidden" name="returnUrl" value="/" />
<button type="submit" class="btn btn-link nav-link">Logout</button>
</form>
</Authorized>
<NotAuthorized>
<a href="/authentication/login?returnUrl=/" class="nav-link">Login</a>
</NotAuthorized>
</AuthorizeView>
Adicionar ao layout: Inclua <UserInfo /> em seu MainLayout.razor:
@inherits LayoutComponentBase
<div class="page">
<div class="sidebar">
<NavMenu />
</div>
<main>
<div class="top-row px-4">
<UserInfo />
</div>
<article class="content px-4">
@Body
</article>
</main>
</div>
Atualizar Routes.razor para AuthorizeRouteView
Substitua RouteView porAuthorizeRouteView:Components/Routes.razor
@using Microsoft.AspNetCore.Components.Authorization
<Router AppAssembly="typeof(Program).Assembly">
<Found Context="routeData">
<AuthorizeRouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)">
<NotAuthorized>
<p>You are not authorized to view this page.</p>
<a href="/authentication/login">Login</a>
</NotAuthorized>
</AuthorizeRouteView>
<FocusOnNavigate RouteData="routeData" Selector="h1" />
</Found>
</Router>
Manipular exceções em páginas que utilizam APIs
O Servidor Blazor requer tratamento explícito de exceção para acesso condicional e consentimento. Você deve lidar com MicrosoftIdentityWebChallengeUserException em todas as páginas que fazem chamadas para uma API downstream, a menos que seu aplicativo seja pré-autorizado e solicite todos os escopos antecipadamente.Program.cs
Weather.razor O exemplo a seguir demonstra o tratamento adequado de exceções:
@page "/weather"
@attribute [Authorize]
@using Microsoft.AspNetCore.Authorization
@using Microsoft.Identity.Web
@inject WeatherApiClient WeatherApi
@inject BlazorAuthenticationChallengeHandler ChallengeHandler
<PageTitle>Weather</PageTitle>
<h1>Weather</h1>
@if (!string.IsNullOrEmpty(errorMessage))
{
<div class="alert alert-warning">@errorMessage</div>
}
else if (forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<table class="table">
<thead>
<tr>
<th>Date</th>
<th>Temp. (C)</th>
<th>Summary</th>
</tr>
</thead>
<tbody>
@foreach (var forecast in forecasts)
{
<tr>
<td>@forecast.Date.ToShortDateString()</td>
<td>@forecast.TemperatureC</td>
<td>@forecast.Summary</td>
</tr>
}
</tbody>
</table>
}
@code {
private WeatherForecast[]? forecasts;
private string? errorMessage;
protected override async Task OnInitializedAsync()
{
if (!await ChallengeHandler.IsAuthenticatedAsync())
{
await ChallengeHandler.ChallengeUserWithConfiguredScopesAsync("WeatherApi:Scopes");
return;
}
try
{
forecasts = await WeatherApi.GetWeatherAsync();
}
catch (Exception ex)
{
// Handle incremental consent / Conditional Access
if (!await ChallengeHandler.HandleExceptionAsync(ex))
{
errorMessage = $"Error loading weather data: {ex.Message}";
}
}
}
}
O padrão funciona da seguinte maneira:
-
IsAuthenticatedAsync()verifica se o usuário está conectado antes de fazer chamadas à API. -
HandleExceptionAsync()capturaMicrosoftIdentityWebChallengeUserException(ou como InnerException). - Se for uma exceção de desafio, o usuário será redirecionado para se autenticar novamente com as reivindicações ou escopos necessários.
- Se não for uma exceção de desafio,
HandleExceptionAsyncretornafalsepara que você possa lidar com o erro por conta própria.
Armazenar o segredo do cliente em segredos do usuário
Use o .NET Secret Manager para armazenar o segredo do cliente com segurança durante o desenvolvimento.
Cuidado
Nunca confirme segredos no controle do código-fonte.
Inicialize os segredos do usuário e armazene o segredo do cliente:
cd MyService.Web
dotnet user-secrets init
dotnet user-secrets set "AzureAd:ClientCredentials:0:ClientSecret" "<your-client-secret>"
Em seguida, atualize appsettings.json para remover o segredo codificado:
{
"AzureAd": {
"ClientCredentials": [
{
"SourceType": "ClientSecret"
}
]
}
}
Microsoft. Identity.Web dá suporte a vários tipos de credencial. Para produção, consulte a visão geral das credenciais.
Verificar a implementação
Use esta lista de verificação para confirmar se você concluiu todas as etapas necessárias.
Projeto de API
- [ ] Pacote
Microsoft.Identity.Webadicionado - [ ] Seção
appsettings.jsonatualizada comAzureAd - [ ] Atualizado
Program.cscomAddMicrosoftIdentityWebApi - [ ] Adicionado
.RequireAuthorization()a pontos de extremidade protegidos
Projeto Web/Blazor
- [ ] Pacote
Microsoft.Identity.Webadicionado (v3.3.0+) - [ ] Seções atualizadas
appsettings.jsoncomAzureAdeWeatherApi - [ ] Atualizado
Program.cscom OIDC, obtenção de token - [ ] Adicionado
AddScoped<BlazorAuthenticationChallengeHandler>() - [ ] Criado
Components/UserInfo.razor(o botão de logon) - [ ] Atualizado
MainLayout.razorpara incluir<UserInfo /> - [ ] Atualizado
Routes.razorcomAuthorizeRouteView - [ ] Adicionados try/catch com
ChallengeHandlerem cada página que chama APIs - [ ] Segredo do cliente armazenado em segredos do usuário
Verificação
- [ ]
dotnet buildÊxito - [ ] Registros de aplicativo criados na central de administração do Microsoft Entra
- [ ]
appsettings.jsontem GUIDs reais (sem marcadores de posição)
Testar e solucionar
Depois de concluir a implementação, execute o aplicativo e verifique o fluxo de autenticação de ponta a ponta.
Executar o aplicativo
Inicie o Aspire AppHost para iniciar os projetos web e de API:
# From solution root
dotnet restore
dotnet build
# Launch AppHost (starts both Web and API)
dotnet run --project .\MyService.AppHost\MyService.AppHost.csproj
Testar o fluxo de autenticação
- Abra o navegador → interface do usuário da Web do Blazor (verifique se há URL no painel do Aspire).
- Selecione Login → Entrar com Microsoft Entra.
- Navegue até a página Clima .
- Verifique as cargas de dados meteorológicos (da API protegida).
Resolver problemas comuns
A tabela a seguir lista problemas frequentes e suas soluções:
| Questão | Solução |
|---|---|
| 401 em chamadas à API | Verificar se os escopos em appsettings.json correspondem ao URI do ID do aplicativo da API |
| OIDC falha no redirecionamento | Adicionar /signin-oidc aos URIs de redirecionamento do Microsoft Entra |
| Token não anexado | Verifique se AddMicrosoftIdentityMessageHandler é chamado no HttpClient |
| Falha na descoberta do serviço | Verifique AppHost.cs as referências de ambos os projetos e veja se eles estão em execução |
| AADSTS65001 | Consentimento do administrador necessário – conceder consentimento no centro de administração do Microsoft Entra |
| Sem botão de login | Verifique se UserInfo.razor existe e está incluído em MainLayout.razor |
| Loop de consentimento | Verifique se o try/catch está habilitado em todas as páginas que realizam chamadas à API |
Habilitar o log do MSAL
Ao solucionar problemas de autenticação, habilite o log detalhado da MSAL para ver os detalhes de aquisição de token. Adicione os seguintes níveis de log a appsettings.json:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Microsoft.Identity": "Debug",
"Microsoft.IdentityModel": "Debug"
}
}
}
Aviso
Desative o registro de depuração em ambientes de produção, pois ele pode ser muito verboso.
Inspecionar tokens
Para depurar problemas de token, decodificar seu JWT em jwt.ms e verificar:
-
aud(destinatário): corresponde ao ID do cliente da API ou ao URI do ID do aplicativo -
iss(emissor): corresponde ao arrendatário (https://login.microsoftonline.com/<tenant-id>/v2.0) -
scp(escopos): inclui os escopos necessários -
exp(expiração): o token não expirou
Explorar cenários comuns
As seções a seguir mostram como estender a implementação base para casos de uso adicionais.
Proteger páginas Blazor
Adicione o [Authorize] atributo a páginas que exigem autenticação:
@page "/weather"
@attribute [Authorize]
Ou defina políticas de autorização em Program.cs:
// Program.cs
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("AdminOnly", policy => policy.RequireRole("Admin"));
});
@attribute [Authorize(Policy = "AdminOnly")]
Validar escopos na API
Verifique se a API aceita apenas tokens com escopos específicos encadeando RequireScope:
app.MapGet("/weatherforecast", () =>
{
// ... implementation
})
.RequireAuthorization()
.RequireScope("access_as_user");
Usar tokens exclusivos de aplicativo (serviço a serviço)
Para cenários de daemon ou chamadas de serviço a serviço sem um contexto de usuário, defina RequestAppToken como true:
builder.Services.AddHttpClient<WeatherApiClient>(client =>
{
client.BaseAddress = new("https+http://apiservice");
})
.AddMicrosoftIdentityMessageHandler(options =>
{
options.Scopes.Add("api://<api-client-id>/.default");
options.RequestAppToken = true;
});
Usar credenciais sem certificado para produção
Para implantações de produção em Azure, use a identidade gerenciada em vez de segredos do cliente. Configure a seção ClientCredentials da seguinte forma:
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "<tenant-guid>",
"ClientId": "<web-app-client-id>",
"ClientCredentials": [
{
"SourceType": "SignedAssertionFromManagedIdentity",
"ManagedIdentityClientId": "<user-assigned-mi-client-id>"
}
]
}
}
Para obter mais informações, consulte autenticação sem certificado.
Chamar APIs downstream da API (em nome de)
Se sua API precisar chamar outra API downstream em nome do usuário, habilite a aquisição de token em nome do usuário em Program.cs:
// MyService.ApiService/Program.cs
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddInMemoryTokenCaches();
builder.Services.AddDownstreamApi("GraphApi", builder.Configuration.GetSection("GraphApi"));
Adicione a configuração da API downstream a appsettings.json:
{
"GraphApi": {
"BaseUrl": "https://graph.microsoft.com/v1.0",
"Scopes": [ "User.Read" ]
}
}
Em seguida, chame a API downstream a partir de um endpoint.
{
var user = await downstreamApi.GetForUserAsync<JsonElement>("GraphApi", "me");
return user;
}).RequireAuthorization();
Para obter mais informações, consulte Como chamar APIs downstream.