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 explica como chamar APIs downstream de aplicativos Web ASP.NET Core e OWIN usando Microsoft. Identity.Web. Em aplicativos Web, você adquire tokens em nome do usuário conectado para chamar APIs com permissões delegadas.
Entender o fluxo de tokens
Quando um usuário entra em seu aplicativo Web, você pode chamar APIs downstream (Microsoft Graph, serviços Azure ou APIs personalizadas) em seu nome. Microsoft. O Identity.Web manipula a aquisição de token, o cache e a atualização automática.
Examinar o fluxo de token do usuário
sequenceDiagram
participant User as User Browser
participant WebApp as Your Web App
participant AzureAD as Microsoft Entra ID
participant API as Downstream API
User->>WebApp: 1. Access page requiring API data
Note over WebApp: User already signed in
WebApp->>AzureAD: 2. Request access token for API<br/>(using user's refresh token)
AzureAD->>AzureAD: 3. Validate & check consent
AzureAD->>WebApp: 4. Return access token
Note over WebApp: Cache token
WebApp->>API: 5. Call API with token
API->>WebApp: 6. Return data
WebApp->>User: 7. Render page with data
Examinar os pré-requisitos
Verifique se seu ambiente atende aos seguintes requisitos antes de começar.
- Aplicativo Web configurado com autenticação do OpenID Connect
- O login do usuário está funcionando
- Registro de aplicativo com permissões de API configuradas
- Consentimento do usuário obtido (ou consentimento do administrador concedido)
Implementar ASP.NET Core
1. Configurar autenticação e aquisição de token
Adicione serviços de autenticação e habilite a aquisição de token em seu Program.cs arquivo.
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.Identity.Web;
var builder = WebApplication.CreateBuilder(args);
// Add authentication with explicit scheme
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddInMemoryTokenCaches();
builder.Services.AddRazorPages()
.AddMicrosoftIdentityUI();
builder.Services.AddAuthorization(options =>
{
options.FallbackPolicy = options.DefaultPolicy;
});
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
2. Configurar appsettings.json
Defina o registro do aplicativo Microsoft Entra ID e as configurações de API downstream em appsettings.json.
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "your-tenant-id",
"ClientId": "your-client-id",
"CallbackPath": "/signin-oidc",
"SignedOutCallbackPath": "/signout-callback-oidc",
"ClientCredentials": [
{
"SourceType": "ClientSecret",
"ClientSecret": "your-client-secret"
}
]
},
"DownstreamApis": {
"GraphAPI": {
"BaseUrl": "https://graph.microsoft.com/v1.0",
"Scopes": ["user.read", "mail.read"]
},
"MyAPI": {
"BaseUrl": "https://myapi.example.com",
"Scopes": ["api://my-api-id/access_as_user"]
}
}
}
Importante: Para aplicativos Web que chamam APIs downstream, você precisa de credenciais de cliente (certificado ou segredo) além da configuração de entrada.
3. Adicionar suporte à API downstream
Escolha uma das opções a seguir para registrar suas APIs downstream.
Opção A: registrar APIs nomeadas
O código a seguir registra várias APIs downstream da configuração.
using Microsoft.Identity.Web;
// Register multiple downstream APIs
builder.Services.AddDownstreamApis(
builder.Configuration.GetSection("DownstreamApis"));
Option B: use Microsoft Graph Helper
O código a seguir registra o cliente do SDK do Microsoft Graph a partir da configuração.
// Install: Microsoft.Identity.Web.GraphServiceClient
builder.Services.AddMicrosoftGraph(builder.Configuration.GetSection("DownstreamApis:GraphAPI"));
4. Chamar a API downstream do controlador
Insira IDownstreamApi em seu controlador e chame a API em nome do usuário conectado.
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Identity.Web;
using Microsoft.Identity.Abstractions;
[Authorize]
public class ProfileController : Controller
{
private readonly IDownstreamApi _downstreamApi;
private readonly ILogger<ProfileController> _logger;
public ProfileController(
IDownstreamApi downstreamApi,
ILogger<ProfileController> logger)
{
_downstreamApi = downstreamApi;
_logger = logger;
}
public async Task<IActionResult> Index()
{
try
{
// Call downstream API on behalf of user
var userData = await _downstreamApi.GetForUserAsync<UserData>(
"MyAPI",
options => options.RelativePath = "api/profile");
return View(userData);
}
catch (MicrosoftIdentityWebChallengeUserException ex)
{
// Incremental consent required
// Redirect user to consent page
return Challenge(
new AuthenticationProperties
{
RedirectUri = "/Profile"
},
OpenIdConnectDefaults.AuthenticationScheme);
}
catch (HttpRequestException ex)
{
_logger.LogError(ex, "Failed to call downstream API");
return View("Error");
}
}
}
5. Chamar uma API descendente em uma Página Razor
Injete IDownstreamApi no seu modelo de Página Razor e chame a API.
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Identity.Web;
using Microsoft.Identity.Abstractions;
[Authorize]
public class ProfileModel : PageModel
{
private readonly IDownstreamApi _downstreamApi;
public UserData UserData { get; set; }
public ProfileModel(IDownstreamApi downstreamApi)
{
_downstreamApi = downstreamApi;
}
public async Task OnGetAsync()
{
try
{
UserData = await _downstreamApi.GetForUserAsync<UserData>(
"MyAPI",
options => options.RelativePath = "api/profile");
}
catch (MicrosoftIdentityWebChallengeUserException)
{
// Handle incremental consent
// User will be redirected to consent page
throw;
}
}
}
Chamar Microsoft Graph
Para chamadas de API do Microsoft Graph, use o GraphServiceClient dedicado.
Instalar pacotes
Instale o pacote Microsoft Graph para Microsoft. Identity.Web.
dotnet add package Microsoft.Identity.Web.GraphServiceClient
Configure o cliente do Graph no código de inicialização.
// Startup configuration
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddMicrosoftGraph(options =>
{
options.Scopes = "user.read mail.read";
})
.AddInMemoryTokenCaches();
Chame o API do Graph
Insira GraphServiceClient no controlador para chamar os endpoints do Microsoft Graph.
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Graph;
[Authorize]
{
private readonly GraphServiceClient _graphClient;
public HomeController(GraphServiceClient graphClient)
{
_graphClient = graphClient;
}
public async Task<IActionResult> Index()
{
// Get current user's profile
var user = await _graphClient.Me.GetAsync();
// Get user's emails
var messages = await _graphClient.Me.Messages
.GetAsync(config => config.QueryParameters.Top = 10);
return View(new { User = user, Messages = messages });
}
}
Saiba mais sobre Microsoft Graph integração
Chamar os clientes do SDK do Azure
Para chamar serviços de Azure, use MicrosoftIdentityTokenCredential.
Instalar pacotes
Instale os pacotes de SDK do Azure necessários.
dotnet add package Microsoft.Identity.Web.Azure
dotnet add package Azure.Storage.Blobs
Registre a credencial do token do Microsoft Entra no código de inicialização.
using Microsoft.Identity.Web;
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddInMemoryTokenCaches();
// Add Azure token credential
builder.Services.AddMicrosoftIdentityAzureTokenCredential();
Acessar serviços de Azure
Injete a credencial de token e use-a com os clientes do SDK do Azure.
using Azure.Storage.Blobs;
using Microsoft.Identity.Web;
public class StorageController : Controller
{
private readonly MicrosoftIdentityTokenCredential _credential;
public StorageController(MicrosoftIdentityTokenCredential credential)
{
_credential = credential;
}
[Authorize]
public async Task<IActionResult> ListBlobs()
{
var blobClient = new BlobServiceClient(
new Uri("https://myaccount.blob.core.windows.net"),
_credential);
var container = blobClient.GetBlobContainerClient("mycontainer");
var blobs = new List<string>();
await foreach (var blob in container.GetBlobsAsync())
{
blobs.Add(blob.Name);
}
return View(blobs);
}
}
Saiba mais sobre SDK do Azure integração
Chamar APIs personalizadas com IDownstreamApi
Para suas próprias APIs REST, IDownstreamApi fornece uma abordagem simples e controlada por configuração.
Configurar a API
Defina as configurações de API downstream em appsettings.json.
{
"DownstreamApis": {
"MyAPI": {
"BaseUrl": "https://myapi.example.com",
"Scopes": ["api://my-api-id/access_as_user"],
"RequestAppToken": false
}
}
}
Enviar solicitações GET
Recupere dados da API downstream com parâmetros de consulta opcionais.
// Simple GET
var data = await _downstreamApi.GetForUserAsync<MyData>(
"MyAPI",
options => options.RelativePath = "api/resource");
// GET with query parameters
var results = await _downstreamApi.GetForUserAsync<SearchResults>(
"MyAPI",
options =>
{
options.RelativePath = "api/search";
options.QueryParameters = new Dictionary<string, string>
{
["query"] = "test",
["limit"] = "10"
};
});
Enviar solicitações POST
Crie um novo recurso na API downstream postando um corpo da solicitação.
var newItem = new CreateItemRequest
{
Name = "New Item",
Description = "Item description"
};
var created = await _downstreamApi.PostForUserAsync<CreateItemRequest, CreatedItem>(
"MyAPI",
newItem,
options => options.RelativePath = "api/items");
Enviar solicitações PUT e DELETE
Atualize ou exclua recursos na API downstream.
// PUT request
var updated = await _downstreamApi.PutForUserAsync<UpdateRequest, UpdatedItem>(
"MyAPI",
updateData,
options => options.RelativePath = "api/items/123");
// DELETE request
await _downstreamApi.DeleteForUserAsync(
"MyAPI",
null,
options => options.RelativePath = "api/items/123");
Saiba mais sobre chamadas de API personalizadas
Usar IAuthorizationHeaderProvider (avançado)
Para obter o controle máximo sobre solicitações HTTP, use IAuthorizationHeaderProvider.
Registrar o cliente HTTP
Registre um cliente HTTP nomeado para sua API downstream.
builder.Services.AddHttpClient("MyAPI", client =>
{
client.BaseAddress = new Uri("https://myapi.example.com");
});
Criar solicitações HTTP personalizadas
Crie e envie solicitações HTTP com cabeçalhos personalizados e autorização.
using Microsoft.Identity.Abstractions;
public class CustomApiService
{
private readonly IAuthorizationHeaderProvider _authProvider;
private readonly IHttpClientFactory _httpClientFactory;
public CustomApiService(
IAuthorizationHeaderProvider authProvider,
IHttpClientFactory httpClientFactory)
{
_authProvider = authProvider;
_httpClientFactory = httpClientFactory;
}
public async Task<MyData> GetDataAsync()
{
// Get authorization header
var authHeader = await _authProvider.CreateAuthorizationHeaderForUserAsync(
new[] { "api://my-api-id/access_as_user" });
// Create HTTP request with custom logic
var client = _httpClientFactory.CreateClient("MyAPI");
var request = new HttpRequestMessage(HttpMethod.Get, "api/resource");
request.Headers.Add("Authorization", authHeader);
request.Headers.Add("X-Custom-Header", "custom-value");
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<MyData>();
}
}
Saiba mais sobre a lógica HTTP personalizada
Lidar com o consentimento incremental e o acesso condicional
Ao chamar APIs downstream, o aplicativo pode precisar lidar com cenários em que a interação do usuário é necessária. Isso acontece em três cenários principais:
- Consentimento Incremental – Solicitando permissões adicionais além do que foi inicialmente concedido
- Acesso Condicional – Atender aos requisitos de segurança, como MFA, conformidade do dispositivo ou políticas de localização
- Remoção do cache de token – repovoando o cache de token após a reinicialização do aplicativo ou a expiração do cache
Microsoft. O Identity.Web fornece o tratamento automático desses cenários com o mínimo de código necessário.
Entender o fluxo
Quando Microsoft.Identity.Web detecta que a interação do usuário é necessária, lança um MicrosoftIdentityWebChallengeUserException. A estrutura lida automaticamente com isso por meio do [AuthorizeForScopes] atributo ou do MicrosoftIdentityConsentAndConditionalAccessHandler serviço (para Blazor), que:
- Redireciona o usuário para Microsoft Entra ID para consentimento/autenticação
- Preserva a URL de solicitação original
- Retorna o usuário para o destino pretendido depois de concluir o fluxo
- Armazena em cache os tokens recém-adquiridos
Examinar os pré-requisitos
Para habilitar o tratamento automático de consentimento, verifique se você Program.cs inclui a configuração a seguir.
builder.Services.AddMicrosoftIdentityWebAppAuthentication(builder.Configuration, "AzureAd")
.EnableTokenAcquisitionToCallDownstreamApi()
.AddDownstreamApi("MyAPI", builder.Configuration.GetSection("MyAPI"))
.AddInMemoryTokenCaches();
// For MVC applications - enables the account controller
builder.Services.AddControllersWithViews()
.AddMicrosoftIdentityUI();
// Ensure routes are mapped
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers(); // Required for AccountController
Aplicar [AuthorizeForScopes] em controladores MVC
O [AuthorizeForScopes] atributo, definido em controladores ou ações do controlador, manipula MicrosoftIdentityWebChallengeUserException automaticamente desafiando o usuário quando permissões adicionais são necessárias.
Declarar escopos em linha
Especifique os escopos necessários diretamente no atributo.
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Identity.Web;
using Microsoft.Identity.Abstractions;
[Authorize]
[AuthorizeForScopes(Scopes = new[] { "user.read" })]
public class ProfileController : Controller
{
private readonly IDownstreamApi _downstreamApi;
public ProfileController(IDownstreamApi downstreamApi)
{
_downstreamApi = downstreamApi;
}
public async Task<IActionResult> Index()
{
// AuthorizeForScopes automatically handles consent challenges
var userData = await _downstreamApi.GetForUserAsync<UserData>(
"MyAPI",
options => options.RelativePath = "api/profile");
return View(userData);
}
// Different action requires additional scopes
[AuthorizeForScopes(Scopes = new[] { "user.read", "mail.read" })]
public async Task<IActionResult> Emails()
{
var emails = await _downstreamApi.GetForUserAsync<EmailList>(
"GraphAPI",
options => options.RelativePath = "me/messages");
return View(emails);
}
}
Configurar escopos de 'appsettings'
Armazene escopos em appsettings.json para melhor manutenção:
appsettings.json:
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "common",
"ClientId": "[Your-Client-ID]",
"ClientCredentials": [
{
"SourceType": "ClientSecret",
"ClientSecret": "[Your-Client-Secret]"
}
]
},
"DownstreamApis": {
"TodoList": {
"BaseUrl": "https://localhost:5001",
"Scopes": [ "api://[API-Client-ID]/access_as_user" ]
},
"GraphAPI": {
"BaseUrl": "https://graph.microsoft.com/v1.0",
"Scopes": [ "https://graph.microsoft.com/Mail.Read", "https://graph.microsoft.com/Mail.Send" ]
}
}
}
Controlador:
[Authorize]
[AuthorizeForScopes(ScopeKeySection = "DownstreamApis:TodoList:Scopes:0")]
public class TodoListController : Controller
{
private readonly IDownstreamApi _downstreamApi;
public TodoListController(IDownstreamApi downstreamApi)
{
_downstreamApi = downstreamApi;
}
public async Task<IActionResult> Index()
{
var todos = await _downstreamApi.GetForUserAsync<IEnumerable<TodoItem>>(
"TodoList",
options => options.RelativePath = "api/todolist");
return View(todos);
}
[AuthorizeForScopes(ScopeKeySection = "DownstreamApis:GraphAPI:Scopes:0")]
public async Task<IActionResult> EmailTodos()
{
// If user hasn't consented to Mail.Send, they'll be prompted
await _downstreamApi.PostForUserAsync<EmailMessage, object>(
"GraphAPI",
new EmailMessage { /* ... */ },
options => options.RelativePath = "me/sendMail");
return RedirectToAction("Index");
}
}
Configurar ID externa do Microsoft Entra com fluxos de usuários
Para aplicativos de ID externa (B2C) com vários fluxos de usuário, especifique o fluxo de usuário no atributo.
[Authorize]
public class AccountController : Controller
{
private const string SignUpSignInFlow = "b2c_1_susi";
private const string EditProfileFlow = "b2c_1_edit_profile";
private const string ResetPasswordFlow = "b2c_1_reset";
[AuthorizeForScopes(
ScopeKeySection = "DownstreamApis:TodoList:Scopes:0",
UserFlow = SignUpSignInFlow)]
public async Task<IActionResult> Index()
{
var data = await _downstreamApi.GetForUserAsync<UserData>(
"TodoList",
options => options.RelativePath = "api/data");
return View(data);
}
[AuthorizeForScopes(
Scopes = new[] { "openid", "offline_access" },
UserFlow = EditProfileFlow)]
public async Task<IActionResult> EditProfile()
{
// This triggers the B2C edit profile flow
return RedirectToAction("Index");
}
}
Aplicar [AuthorizeForScopes] em Páginas Razor
Aplique [AuthorizeForScopes] na classe de modelo de página:
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Identity.Web;
using Microsoft.Identity.Abstractions;
[Authorize]
[AuthorizeForScopes(ScopeKeySection = "DownstreamApis:MyAPI:Scopes:0")]
public class IndexModel : PageModel
{
private readonly IDownstreamApi _downstreamApi;
public UserData UserData { get; set; }
public IndexModel(IDownstreamApi downstreamApi)
{
_downstreamApi = downstreamApi;
}
public async Task OnGetAsync()
{
// Automatically handles consent challenges
UserData = await _downstreamApi.GetForUserAsync<UserData>(
"MyAPI",
options => options.RelativePath = "api/profile");
}
}
Gerenciar consentimento no Blazor Server
Os aplicativos Blazor Server exigem tratamento explícito de exceção usando o serviço MicrosoftIdentityConsentAndConditionalAccessHandler.
Configurar Program.cs
Registre o manipulador de consentimento do Servidor Blazor no código de inicialização.
builder.Services.AddMicrosoftIdentityWebAppAuthentication(builder.Configuration, "AzureAd")
.EnableTokenAcquisitionToCallDownstreamApi()
.AddDownstreamApis("TodoList", builder.Configuration.GetSection("DownstreamApis"))
.AddInMemoryTokenCaches();
// Register the consent handler for Blazor
builder.Services.AddServerSideBlazor()
.AddMicrosoftIdentityConsentHandler();
Criar o componente Blazor
Encapsular chamadas de API em blocos try-catch e usar ConsentHandler.HandleException() para lidar com os desafios de consentimento.
@page "/todolist"
@using Microsoft.Identity.Web
@using Microsoft.Identity.Abstractions
@using MyApp.Models
@inject MicrosoftIdentityConsentAndConditionalAccessHandler ConsentHandler
@inject IDownstreamApi DownstreamApi
<h3>My Todo List</h3>
@if (todos == null)
{
<p><em>Loading...</em></p>
}
else
{
<ul>
@foreach (var todo in todos)
{
<li>@todo.Title</li>
}
</ul>
}
@code {
private IEnumerable<TodoItem> todos;
protected override async Task OnInitializedAsync()
{
await LoadTodosAsync();
}
[AuthorizeForScopes(ScopeKeySection = "DownstreamApis:TodoList:Scopes:0")]
private async Task LoadTodosAsync()
{
try
{
todos = await DownstreamApi.GetForUserAsync<IEnumerable<TodoItem>>(
"TodoList",
options => options.RelativePath = "api/todolist");
}
catch (Exception ex)
{
// Handles MicrosoftIdentityWebChallengeUserException
// and initiates user consent/authentication flow
ConsentHandler.HandleException(ex);
}
}
private async Task AddTodoAsync(string title)
{
try
{
await DownstreamApi.PostForUserAsync<TodoItem, TodoItem>(
"TodoList",
new TodoItem { Title = title },
options => options.RelativePath = "api/todolist");
await LoadTodosAsync();
}
catch (Exception ex)
{
ConsentHandler.HandleException(ex);
}
}
}
Gerenciar exceções manualmente (avançado)
Se você precisar de lógica de fluxo de consentimento personalizado, manipule MicrosoftIdentityWebChallengeUserException explicitamente:
[Authorize]
public class AdvancedController : Controller
{
private readonly IDownstreamApi _downstreamApi;
private readonly ILogger<AdvancedController> _logger;
public AdvancedController(
IDownstreamApi downstreamApi,
ILogger<AdvancedController> logger)
{
_downstreamApi = downstreamApi;
_logger = logger;
}
public async Task<IActionResult> SendEmail()
{
try
{
await _downstreamApi.PostForUserAsync<EmailMessage, object>(
"GraphAPI",
new EmailMessage
{
Subject = "Test",
Body = "Test message"
},
options => options.RelativePath = "me/sendMail");
return RedirectToAction("Success");
}
catch (MicrosoftIdentityWebChallengeUserException ex)
{
// Log the consent requirement
_logger.LogWarning(
"Consent required for scopes: {Scopes}. Challenging user.",
string.Join(", ", ex.Scopes));
// Custom properties for redirect
var properties = new AuthenticationProperties
{
RedirectUri = Url.Action("SendEmail", "Advanced"),
};
// Add custom state if needed
properties.Items["consent_attempt"] = "1";
return Challenge(properties, OpenIdConnectDefaults.AuthenticationScheme);
}
catch (HttpRequestException ex)
{
_logger.LogError(ex, "Failed to send email");
return View("Error");
}
}
}
Lidar com cenários de acesso condicional
As políticas de acesso condicional podem exigir fatores de autenticação adicionais. O tratamento é idêntico ao consentimento incremental:
[Authorize]
[AuthorizeForScopes(ScopeKeySection = "DownstreamApis:SecureAPI:Scopes:0")]
public class SecureDataController : Controller
{
private readonly IDownstreamApi _downstreamApi;
public SecureDataController(IDownstreamApi downstreamApi)
{
_downstreamApi = downstreamApi;
}
public async Task<IActionResult> Index()
{
// If conditional access requires MFA, AuthorizeForScopes
// automatically challenges the user
var sensitiveData = await _downstreamApi.GetForUserAsync<SensitiveData>(
"SecureAPI",
options => options.RelativePath = "api/sensitive");
return View(sensitiveData);
}
}
Gatilhos comuns de acesso condicional:
- MFA (Autenticação Multifator)
- Requisito de dispositivo em conformidade
- Local de rede confiável
- Aceitação de termos de uso
- Requisito de alteração de senha
Seguir as práticas recomendadas
Aplique essas recomendações ao implementar o consentimento e o tratamento de acesso condicional.
Usar [AuthorizeForScopes] – Abordagem mais simples para controladores MVC e Páginas Razor
Escopos de repositório na configuração – Use ScopeKeySection = "DownstreamApis:ApiName:Scopes:0" para fazer referência aos escopos em appsettings.json
Aplicar no nível do controlador – Definir escopos padrão no controlador, substituir em ações específicas
Manipular exceções no Blazor – Sempre encapsular chamadas à API com try-catch e use ConsentHandler.HandleException()
Permitir relançamento de exceções - Se você capturar , relance-a para que MicrosoftIdentityWebChallengeUserException possa processá-la
Testar o acesso condicional – Verifique se seu aplicativo gerencia MFA e outras políticas de acesso condicional corretamente
Não suprimir exceções - Capturar sem gerar novamente interrompe o fluxo de consentimento
Não armazenar respostas em cache indefinidamente – os tokens expiram; design para autenticação novamente
Comparar permissões estáticas e consentimento incremental
Permissões estáticas (Consentimento do Administrador)
Todas as permissões são solicitadas durante o registro do aplicativo e consentidas por um administrador de locatário:
Vantagens:
- Os usuários nunca veem avisos de consentimento
- Necessário para aplicativos de Microsoft de primeira parte
- Experiência do usuário mais simples
Desvantagens:
- Requer envolvimento do administrador de locatários
- Com privilégios excessivos desde o início
- Menos flexível para cenários multilocatários
Configuration:
// Request all pre-approved scopes for Microsoft Graph
var scopes = new[] { "https://graph.microsoft.com/.default" };
var userData = await _downstreamApi.GetForUserAsync<UserData>(
"GraphAPI",
options =>
{
options.RelativePath = "me";
options.Scopes = scopes; // Use .default scope
});
Consentimento Incremental (Dinâmico)
As permissões são solicitadas conforme necessário durante o runtime:
Vantagens:
- Melhor segurança (princípio de privilégio mínimo)
- Os usuários consentem com o que realmente usam
- Funciona para aplicativos multilocatários
Desvantagens:
- Os usuários podem ser interrompidos com solicitações de consentimento
- Requer tratamento
MicrosoftIdentityWebChallengeUserException
Recomendação: Use o consentimento incremental para aplicativos multilocatários; utilize permissões estáticas para aplicativos empresariais de primeira parte em que o consentimento do administrador é garantido
Configurar o cache de token
Microsoft.Identity.Web armazena tokens em cache para melhorar o desempenho e reduzir as chamadas para Microsoft Entra.
Usar cache na memória (padrão)
Adicione um cache de token na memória para cenários de desenvolvimento ou de servidor único.
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddInMemoryTokenCaches(); // In-memory cache
Use para:
- Desenvolvimento
- Implantações de servidor único
- Base de usuários pequenas
Limitações:
- Não compartilhado entre instâncias
- Perda na reinicialização do aplicativo
- O consumo de memória aumenta com os usuários
Usar cache distribuído (recomendado para produção)
Configure um cache distribuído, como Redis ou SQL Server para implantações de produção.
// Install: Microsoft.Identity.Web.TokenCache
// Redis
builder.Services.AddStackExchangeRedisCache(options =>
{
options.Configuration = builder.Configuration["Redis:ConnectionString"];
options.InstanceName = "MyApp_";
});
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddDistributedTokenCaches();
// SQL Server
builder.Services.AddDistributedSqlServerCache(options =>
{
options.ConnectionString = builder.Configuration["SqlCache:ConnectionString"];
options.SchemaName = "dbo";
options.TableName = "TokenCache";
});
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddDistributedTokenCaches();
Use para:
- Implantações de vários servidores (balanceamento de carga)
- Cenários de alta disponibilidade
- Base de usuário grande
- Cache persistente entre reinicializações
Lidar com falhas de aquisição de token
Capturar exceções comuns
O código a seguir demonstra como capturar e lidar com as exceções de aquisição de token mais comuns.
try
{
var data = await _downstreamApi.GetForUserAsync<MyData>(
"MyAPI",
options => options.RelativePath = "api/resource");
}
catch (MicrosoftIdentityWebChallengeUserException ex)
{
// User needs to consent or reauthenticate
_logger.LogWarning($"User consent required: {ex.Message}");
return Challenge(new AuthenticationProperties { RedirectUri = Request.Path });
}
catch (MsalUiRequiredException ex)
{
// User interaction required (sign-in again, MFA, etc.)
_logger.LogWarning($"User interaction required: {ex.Message}");
return Challenge(OpenIdConnectDefaults.AuthenticationScheme);
}
catch (MsalServiceException ex)
{
// Service error (Microsoft Entra ID unavailable, etc.)
_logger.LogError(ex, "Microsoft Entra ID service error");
return StatusCode(503, "Authentication service temporarily unavailable");
}
catch (HttpRequestException ex)
{
// Downstream API unreachable
_logger.LogError(ex, "Downstream API call failed");
return StatusCode(503, "Downstream service unavailable");
}
Implementar degradação graciosa
Carregue dados opcionais de APIs downstream e volte aos padrões quando as chamadas falharem.
public async Task<IActionResult> Dashboard()
{
var model = new DashboardModel();
// Try to load optional data from downstream API
try
{
model.EnrichedData = await _downstreamApi.GetForUserAsync<EnrichedData>(
"MyAPI",
options => options.RelativePath = "api/enriched");
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Failed to load enriched data, using defaults");
model.EnrichedData = new EnrichedData { /* defaults */ };
}
return View(model);
}
Implementar o OWIN (.NET Framework)
Para aplicativos Web baseados em OWIN no .NET Framework, siga estas etapas.
1. Instalar pacotes
Instale os pacotes NuGet necessários.
Install-Package Microsoft.Identity.Web.OWIN
Install-Package Microsoft.Owin.Host.SystemWeb
2. Configurar a inicialização
Configure a autenticação e a aquisição de token do Microsoft Entra na classe de inicialização do OWIN.
using Microsoft.Identity.Web;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using Owin;
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.AddMicrosoftIdentityWebApp(
Configuration,
configSectionName: "AzureAd",
openIdConnectScheme: "OpenIdConnect",
cookieScheme: CookieAuthenticationDefaults.AuthenticationType,
subscribeToOpenIdConnectMiddlewareDiagnosticsEvents: true);
app.EnableTokenAcquisitionToCallDownstreamApi();
app.AddDistributedTokenCaches();
}
}
3. Chamar a API downstream
Adquira um token e chame a API downstream de um controlador MVC.
using Microsoft.Identity.Web;
using System.Threading.Tasks;
using System.Web.Mvc;
[Authorize]
public class ProfileController : Controller
{
public async Task<ActionResult> Index()
{
var downstreamApi = TokenAcquirerFactory.GetDefaultInstance()
.GetTokenAcquirer()
.GetDownstreamApi();
var userData = await downstreamApi.GetForUserAsync<UserData>(
"MyAPI",
options => options.RelativePath = "api/profile");
return View(userData);
}
}
Note: o suporte OWIN tem algumas diferenças em relação ao ASP.NET Core. Consulte a documentação do OWIN para obter detalhes.
Siga as melhores práticas de segurança
Gerenciar escopos
Aplique o princípio de privilégio mínimo ao solicitar permissões de API.
Fazer:
- Solicite apenas os escopos que você precisa
- Usar o consentimento de forma incremental para recursos avançados
- Documentar os escopos necessários em seu aplicativo
Não fazer:
- Solicitar escopos desnecessários antecipadamente
- Solicitar escopos somente de administrador sem justificativa
- Suponha que todos os escopos serão concedidos
Manipular tokens com segurança
Siga estas diretrizes para proteger tokens de acesso em seu aplicativo.
Fazer:
- Deixe o Microsoft.Identity.Web gerenciar os tokens
- Usar cache distribuído em produção
- Lidar de forma elegante com falhas na aquisição de token
Não fazer:
- Armazene tokens por conta própria
- Tokens de acesso a logs
- Enviar tokens para o código do lado do cliente
Gerenciar erros
Implemente um tratamento de erros robusto para falhas de autenticação e de chamada à API.
Fazer:
- Capturar e manipular exceções de consentimento
- Fornecer mensagens de erro claras aos usuários
- Registrar erros para depuração
Não fazer:
- Expor erros de token aos usuários
- Falhas silenciosas em chamadas de API
- Ignorar exceções de autenticação
Solucionar problemas comuns
Revise estas soluções para erros de autenticação frequentemente encontrados.
Problema: "AADSTS65001: o usuário ou administrador não consentiu"
Causa: O usuário não consentiu com os escopos necessários.
Solution:
catch (MicrosoftIdentityWebChallengeUserException ex)
{
// Redirect to consent page
return Challenge(
new AuthenticationProperties { RedirectUri = Request.Path },
OpenIdConnectDefaults.AuthenticationScheme);
}
Problema: "AADSTS50076: autenticação multifator necessária"
Causa: O usuário precisa concluir a MFA.
Solution:
catch (MsalUiRequiredException)
{
// Redirect user to sign in with MFA
return Challenge(OpenIdConnectDefaults.AuthenticationScheme);
}
Problema: tokens que não persistem entre reinicializações de aplicativo
Causa: Usando o cache na memória.
Solution: Alternar para o cache distribuído (Redis, SQL Server ou Cosmos DB).
Problema: 401 Não autorizado da API downstream
Causas possíveis::
- Escopos incorretos solicitados
- Permissão de API não concedida no registro de aplicativo
- Token expirado
Solution:
- Verifiquem se os escopos em appsettings.json correspondem aos requisitos da API
- Verificar se o registro de aplicativo tem permissões de API
- Verifique se os tokens estão sendo armazenados em cache e atualizados
Para diagnóstico detalhado: Consulte o Guia de Log e Diagnóstico para IDs de correlação, depuração de cache de token e padrões abrangentes de resolução de problemas.
Otimizar o desempenho
Planejar a estratégia de cache de token
Selecione uma estratégia de cache que corresponda à topologia de implantação.
- Usar cache distribuído para implantações de vários servidores
- Configurar a expiração de cache apropriada
- Monitorar desempenho do cache
Minimizar solicitações de token
Microsoft.Identity.Web armazena tokens em cache automaticamente. Ambas as chamadas no exemplo a seguir reutilizam o mesmo token armazenado em cache.
// Bad: Multiple token acquisitions
var profile = await _downstreamApi.GetForUserAsync<Profile>(
"API",
options => options.RelativePath = "profile");
var settings = await _downstreamApi.GetForUserAsync<Settings>(
"API",
options => options.RelativePath = "settings");
// Good: Single token, multiple calls (token is cached)
// Both calls use the same cached token
var profile = await _downstreamApi.GetForUserAsync<Profile>(
"API",
options => options.RelativePath = "profile");
var settings = await _downstreamApi.GetForUserAsync<Settings>(
"API",
options => options.RelativePath = "settings");
Fazer chamadas de API paralelas
Chame várias APIs downstream simultaneamente para reduzir a latência geral.
// Call multiple APIs in parallel
var profileTask = _downstreamApi.GetForUserAsync<Profile>(
"API1",
options => options.RelativePath = "profile");
var settingsTask = _downstreamApi.GetForUserAsync<Settings>(
"API2",
options => options.RelativePath = "settings");
await Task.WhenAll(profileTask, settingsTask);
var profile = profileTask.Result;
var settings = settingsTask.Result;
Explorar o conteúdo relacionado
Encontre diretrizes adicionais para cenários relacionados.
- Visão geral da chamada de APIs downstream
- Realizando chamadas de APIs web
- integração Microsoft Graph
- Visão geral do cache de token