Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Este guia explica como chamar APIs subsequentes a partir de aplicações web ASP.NET Core e OWIN usando Microsoft.Identity.Web. Nas aplicações web, adquires tokens em representação do utilizador autenticado para chamar APIs com permissões delegadas.
Compreender o fluxo de tokens
Quando um utilizador inicia sessão na sua aplicação web, pode chamar APIs downstream (Microsoft Graph, serviços Azure ou APIs personalizadas) em nome deles. Microsoft. O Identity.Web gere a aquisição de tokens, cache e atualização automática.
Revise o fluxo dos tokens de utilizador
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
Rever pré-requisitos
Verifique se o seu ambiente cumpre os seguintes requisitos antes de começar.
- Aplicação web configurada com autenticação OpenID Connect
- Funcionamento do login do utilizador
- Registo de aplicações com permissões da API configuradas
- Consentimento do utilizador obtido (ou consentimento do administrador concedido)
Implementar ASP.NET Core
1. Configurar autenticação e aquisição de tokens
Adicione serviços de autenticação e ative a aquisição de tokens no seu Program.cs ficheiro.
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 registo da aplicação e as definições da API downstream no Microsoft Entra ID 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 aplicações web que chamam APIs a jusante, precisas de credenciais de cliente (certificado ou secreto) além da configuração de início de sessão.
3. Adicionar suporte a API a jusante
Escolha uma das seguintes opções para registar as suas APIs a jusante.
Opção A: Registo de APIs nomeadas
O código seguinte regista múltiplas APIs de destino a partir das configurações.
using Microsoft.Identity.Web;
// Register multiple downstream APIs
builder.Services.AddDownstreamApis(
builder.Configuration.GetSection("DownstreamApis"));
Opção B: Usar Microsoft Graph Helper
O código seguinte regista o cliente Microsoft Graph SDK a partir da configuração.
// Install: Microsoft.Identity.Web.GraphServiceClient
builder.Services.AddMicrosoftGraph(builder.Configuration.GetSection("DownstreamApis:GraphAPI"));
4. Chamar a API downstream a partir do controlador
Injeta IDownstreamApi no teu controlador e chama a API em nome do utilizador iniciado.
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 a API downstream a partir do Razor Page
Injete IDownstreamApi no seu modelo Razor Page 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;
}
}
}
Faça uma chamada ao Microsoft Graph
Para as chamadas à API do Microsoft Graph, utilize a GraphServiceClient dedicada.
Instalar pacotes
Instale o pacote Microsoft Graph para a Microsoft. Identidade.Web.
dotnet add package Microsoft.Identity.Web.GraphServiceClient
Configure o cliente Graph no seu código inicial.
// Startup configuration
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddMicrosoftGraph(options =>
{
options.Scopes = "user.read mail.read";
})
.AddInMemoryTokenCaches();
Chame a API do Graph
Injeta GraphServiceClient no teu controlador para chamar Microsoft Graph endpoints.
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 integração Microsoft Graph
Chame os clientes do SDK do Azure
Para chamar serviços do Azure, use MicrosoftIdentityTokenCredential.
Instalar pacotes
Instale os pacotes SDK do Azure necessários.
dotnet add package Microsoft.Identity.Web.Azure
dotnet add package Azure.Storage.Blobs
Registe a credencial do token Microsoft Entra no seu código de startup.
using Microsoft.Identity.Web;
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddInMemoryTokenCaches();
// Add Azure token credential
builder.Services.AddMicrosoftIdentityAzureTokenCredential();
Aceder aos serviços Azure
Injete a credencial do 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 integração SDK do Azure
Invocar APIs personalizadas com IDownstreamApi
Para as suas próprias APIs REST, IDownstreamApi fornece uma abordagem simples orientada por configuração.
Configurar a API
Defina as definições da API a jusante em appsettings.json.
{
"DownstreamApis": {
"MyAPI": {
"BaseUrl": "https://myapi.example.com",
"Scopes": ["api://my-api-id/access_as_user"],
"RequestAppToken": false
}
}
}
Enviar pedidos GET
Recuperar dados da downstream API com parâmetros opcionais de consulta.
// 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 pedidos POST
Crie um novo recurso na API a jusante publicando um corpo de pedido.
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 pedidos de PUT e DELETE
Atualize ou elimine 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 API personalizadas
Use IAuthorizationHeaderProvider (advanced)
Para controlo máximo sobre pedidos HTTP, use IAuthorizationHeaderProvider.
Registar o cliente HTTP
Regista um cliente HTTP nomeado para a tua API downstream.
builder.Services.AddHttpClient("MyAPI", client =>
{
client.BaseAddress = new Uri("https://myapi.example.com");
});
Criar pedidos HTTP personalizados
Construir e enviar pedidos 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 lógica HTTP personalizada
Lidar com o consentimento incremental e o acesso condicional
Ao chamar APIs a jusante, a sua aplicação pode precisar de lidar com cenários em que a interação do utilizador é necessária. Isto acontece em três cenários principais:
- Consentimento Incremental - Solicitar permissões adicionais para além do inicialmente concedido
- Acesso Condicional - Cumprir requisitos de segurança como MFA, conformidade de dispositivos ou políticas de localização
- Expulsão da Cache de Token - Repopular a cache de token após o reinício da aplicação ou a expiração da cache
Microsoft. O Identity.Web fornece tratamento automático destes cenários com código mínimo necessário.
Compreende o fluxo
Quando o Microsoft.Identity.Web deteta que é necessária a interação do utilizador, lança um MicrosoftIdentityWebChallengeUserException. O framework gere isto automaticamente através do [AuthorizeForScopes] atributo ou do MicrosoftIdentityConsentAndConditionalAccessHandler serviço (para Blazor), que:
- Redireciona o utilizador para o Microsoft Entra ID para consentimento/autenticação
- Preserva a URL original do pedido
- Devolve o utilizador ao destino pretendido após completar o fluxo
- Armazena em cache os tokens recém-adquiridos
Rever pré-requisitos
Para ativar o processamento automático de consentimento, certifique-se Program.cs de que inclui a seguinte configuração.
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 atributo [AuthorizeForScopes], definido em controladores ou ações de controlador, trata automaticamente de MicrosoftIdentityWebChallengeUserException desafiando o utilizador quando são necessárias permissões adicionais.
Declarar âmbitos 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 os telescópios a partir das configurações da aplicação
Armazene os 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 o ID externo Microsoft Entra com fluxos de utilizador
Para aplicações External ID (B2C) com múltiplos fluxos de utilizador, especifique o fluxo de utilizador 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] nas páginas do Razor
Aplicar [AuthorizeForScopes] à classe do 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");
}
}
Lidar com o consentimento no Blazor Server
As aplicações Blazor Server requerem um tratamento explícito de exceções através do MicrosoftIdentityConsentAndConditionalAccessHandler serviço.
Configurar Program.cs
Regista o gestor de consentimento do Blazor Server no teu código de arranque.
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
Envolver chamadas de API em blocos try-catch e usar ConsentHandler.HandleException() para lidar com 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);
}
}
}
Tratar exceções manualmente (avançado)
Se precisar de lógica de fluxo de consentimento personalizada, trate 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 adicionais de autenticação. 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:
- Autenticação multifator (AMF)
- Requisito de dispositivo compatível
- Localização confiável da rede
- Aceitação dos termos de utilização
- Requisito de alteração de palavra-passe
Siga as melhores práticas
Aplique estas recomendações ao implementar o consentimento e o tratamento do acesso condicional.
Utilização [AuthorizeForScopes] - Abordagem mais simples para controladores MVC e Razor Pages
Armazenar âmbitos em configuração - Usar ScopeKeySection = "DownstreamApis:ApiName:Scopes:0" para referenciar os âmbitos em appsettings.json
Aplicar ao nível do controlador - Definir os escopos predefinidos no controlador, anular ações específicas
Tratar exceções no Blazor - Sempre envolver chamadas API com try-catch e usar ConsentHandler.HandleException()
Permitir relançar exceções - Se apanhar , volte a lançá-lo para que MicrosoftIdentityWebChallengeUserException possa processar
Teste o acesso condicional - Verifique se a sua aplicação gere corretamente a MFA e outras políticas da CA
Não suprima exceções – Apanhar sem voltar a lançar quebra o fluxo de consentimento
Não guarde respostas em cache indefinidamente – os tokens expiram; Design para Reautenticação
Compare permissões estáticas e consentimento incremental
Permissões Estáticas (Consentimento do Administrador)
Todas as permissões são solicitadas durante o registo da aplicação e consentidas por um administrador de inquilino:
Prós:
- Os utilizadores nunca veem os pedidos de consentimento
- Obrigatório para aplicações Microsoft de primeira parte
- Experiência de utilizador mais simples
Contras:
- Exige envolvimento da administração dos inquilinos
- Excessivamente privilegiado desde o início
- Menos flexível para cenários multi-inquilino
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 tempo de execução:
Prós:
- Melhor segurança (princípio do menor privilégio)
- Os utilizadores consentem àquilo que realmente utilizam
- Funciona para aplicações multi-inquilino
Contras:
- Os utilizadores podem ser interrompidos por pedidos de consentimento
- Requer manipulação
MicrosoftIdentityWebChallengeUserException
Recomendação: Use consentimento incremental para aplicações multi-inquilinos; Use permissões estáticas para aplicações empresariais de primeira mão onde o consentimento do administrador é garantido
Configurar cache de tokens
Microsoft.Identity.Web armazena tokens em cache para melhorar o desempenho e reduzir chamadas para o serviço Microsoft Entra.
Usar cache em memória (por defeito)
Adicione uma cache de tokens em memória para desenvolvimento ou cenários de servidor único.
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddInMemoryTokenCaches(); // In-memory cache
Utilização para:
- Desenvolvimento
- Implementações de servidor único
- Pequena base de utilizadores
Limitations:
- Não partilhado entre instâncias
- Perdi no reinício da aplicação
- O consumo de memória cresce com os utilizadores
Utilize cache distribuído (recomendado para produção)
Configure uma cache distribuída, como Redis ou SQL Server, para implementações em 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();
Utilização para:
- Implementações multi-servidor (com balanceamento de carga)
- Cenários de elevada disponibilidade
- Grande base de utilizadores
- Cache persistente ao longo dos reinícios
Lidar com falhas na aquisição de tokens
Apanhe exceções comuns
O código seguinte demonstra como detetar e lidar com as exceções mais comuns na aquisição de tokens.
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 suave
Carregue dados opcionais das APIs posteriores e volte aos valores predefinidos quando as chamadas falham.
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 OWIN (Framework .NET)
Para aplicações web baseadas em OWIN no .NET Framework, siga estes passos.
1. Instalar pacotes
Instala os pacotes NuGet necessários.
Install-Package Microsoft.Identity.Web.OWIN
Install-Package Microsoft.Owin.Host.SystemWeb
2. Configurar o arranque
Configure a autenticação do Microsoft Entra e a aquisição de tokens na classe de inicialização 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 a jusante
Adquira um token e invoque a API downstream a partir 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);
}
}
Nota: o suporte OWIN tem algumas diferenças em relação ao ASP.NET Core. Consulte a documentação do OWIN para mais detalhes.
Siga as melhores práticas de segurança
Gerir os âmbitos
Aplicar o princípio do privilégio mínimo ao solicitar permissões de API.
Do:
- Solicite apenas os escopos de acesso que precisar
- Use consentimento incremental para funcionalidades avançadas
- Documente os âmbitos necessários na sua aplicação
Don't:
- Solicite permissões desnecessárias desde o início
- Solicite escopos apenas para administradores sem justificação
- Assumir que todos os âmbitos serão concedidos
Gerir tokens de forma segura
Siga estas orientações para proteger os tokens de acesso na sua aplicação.
Do:
- Deixe a Microsoft. Identity.Web gerir tokens
- Usar cache distribuído em produção
- Lidar com falhas na aquisição de tokens de maneira elegante
Don't:
- Armazena os tokens tu próprio
- Tokens de acesso de registo
- Enviar tokens para o código do lado do cliente
Lidar com erros
Implemente um tratamento robusto de erros para falhas de autenticação e chamadas de API.
Do:
- Capturar e gerir exceções de consentimento
- Fornecer mensagens de erro claras aos utilizadores
- Registar erros para depuração
Don't:
- Expor erros de token aos utilizadores
- Falhar silenciosamente as chamadas de API
- Ignorar exceções de autenticação
Resolver problemas comuns
Revise estas soluções para encontrar erros de autenticação frequentemente encontrados.
Problema: "AADSTS65001: O utilizador ou administrador não consentiu"
Causa: O utilizador não consentiu nas permissões obrigatórias.
Solution:
catch (MicrosoftIdentityWebChallengeUserException ex)
{
// Redirect to consent page
return Challenge(
new AuthenticationProperties { RedirectUri = Request.Path },
OpenIdConnectDefaults.AuthenticationScheme);
}
Problema: "AADSTS50076: É necessária autenticação multifator"
Causa: O utilizador precisa de completar MFA.
Solution:
catch (MsalUiRequiredException)
{
// Redirect user to sign in with MFA
return Challenge(OpenIdConnectDefaults.AuthenticationScheme);
}
Problema: Tokens que não persistem ao longo das reinicializações da aplicação
Causa: Usar cache em memória.
Solution: Mudar para cache distribuída (Redis, SQL Server ou Cosmos DB).
Problema: 401 Não autorizado na API downstream
Causas possíveis:
- Pediram telescópios errados
- Permissão API não concedida no registo da aplicação
- Token expirado
Solution:
- Verificar se os escopos em appsettings.json correspondem aos requisitos da API
- Verifique se o registo da aplicação tem permissões da API
- Garantir que os tokens estão a ser armazenados em cache e atualizados
Para diagnósticos detalhados: Consulte o Guia de Registo e Diagnóstico para IDs de correlação, depuração de cache de tokens e padrões abrangentes de resolução de problemas.
Otimize o desempenho
Planejar estratégia de cache de tokens
Selecione uma estratégia de cache que corresponda à sua topologia de implementação.
- Use cache distribuído para implantações multi-servidor
- Configurar a expiração adequada da cache
- Monitorizar o desempenho da cache
Minimizar pedidos de token
Microsoft. O Identity.Web armazena tokens em cache automaticamente. Ambas as chamadas no exemplo seguinte reutilizam o mesmo token 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 paralelas de API
Chamar várias APIs downstream em simultâneo para reduzir a latência global.
// 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 conteúdo relacionado
Encontre orientações adicionais para cenários relacionados.