Adicionar Microsoft Entra ID autenticação a um aplicativo .NET Aspire

Este guia mostra como proteger um aplicativo distribuído .NET Aspire com autenticação e autorização Microsoft Entra ID. Isso abrange:

  1. Front-end do Blazor Server (MyService.Web): Entrada do usuário com OpenID Connect e aquisição de token
  2. Back-end protegido da API (MyService.ApiService): validação JWT usando Microsoft.Identity.Web
  3. 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

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 em Audiences e Scopes)
  • 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>/.default ou api://<api-client-id>/access_as_user

Etapa 1: Registrar a API

  1. Vá para centro de administração do Microsoft Entra>Identity>Applications>Registros de aplicativo.
  2. Selecione Novo registro.
    • Nome:MyService.ApiService
    • Tipos de conta com suporte: Contas somente neste diretório organizacional (locatário único)
    • Selecione Registrar.
  3. 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.
  4. Copie a ID do aplicativo (cliente) – você precisará disso para ambos os appsettings.json arquivos.

Para obter mais informações, consulte Início Rápido: Configurar um aplicativo para expor uma API Web.

Etapa 2: Registrar o aplicativo Web

  1. 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 sua launchSettings.json porta real)
    • Selecione Registrar.
  2. Vá para Autenticação>Adicionar URI para adicionar todas as URLs de desenvolvimento (de launchSettings.json).
  3. Vá para Certificados & segredosSegredos do ClienteNovo segredo do cliente.
    • Adicione uma descrição e uma expiração.
    • Copie o valor do segredo imediatamente – ele não será mostrado novamente.
  4. 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).
  5. 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
  1. O usuário visita o aplicativo Blazor → Não autenticado → vê o botão "Logon".
  2. User seleciona Login → Redireciona para /authentication/login → desafio OIDC no Microsoft Entra.
  3. Usuário entra → Microsoft Entra redireciona para /signin-oidc → cookie configurado.
  4. O usuário navega até a página Clima → Blazor chama WeatherApiClient.GetAsync().
  5. MicrosoftIdentityMessageHandler intercepta a solicitação, adquire um token do cache (ou atualiza silenciosamente) e anexa o Authorization: Bearer <token> cabeçalho.
  6. API recebe solicitação → Microsoft. Identity.Web valida o JWT → retorna dados.
  7. 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() e app.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:

  1. IsAuthenticatedAsync() verifica se o usuário está conectado antes de fazer chamadas à API.
  2. HandleExceptionAsync() captura MicrosoftIdentityWebChallengeUserException (ou como InnerException).
  3. Se for uma exceção de desafio, o usuário será redirecionado para se autenticar novamente com as reivindicações ou escopos necessários.
  4. Se não for uma exceção de desafio, HandleExceptionAsync retorna false para 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.Web adicionado
  • [ ] Seção appsettings.json atualizada com AzureAd
  • [ ] Atualizado Program.cs com AddMicrosoftIdentityWebApi
  • [ ] Adicionado .RequireAuthorization() a pontos de extremidade protegidos

Projeto Web/Blazor

  • [ ] Pacote Microsoft.Identity.Web adicionado (v3.3.0+)
  • [ ] Seções atualizadas appsettings.json com AzureAd e WeatherApi
  • [ ] Atualizado Program.cs com OIDC, obtenção de token
  • [ ] Adicionado AddScoped<BlazorAuthenticationChallengeHandler>()
  • [ ] Criado Components/UserInfo.razor (o botão de logon)
  • [ ] Atualizado MainLayout.razor para incluir <UserInfo />
  • [ ] Atualizado Routes.razor com AuthorizeRouteView
  • [ ] Adicionados try/catch com ChallengeHandler em 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.json tem 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

  1. Abra o navegador → interface do usuário da Web do Blazor (verifique se há URL no painel do Aspire).
  2. Selecione Login → Entrar com Microsoft Entra.
  3. Navegue até a página Clima .
  4. 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.