Configurando Autenticação Serviço-a-Serviço com Azure AD e Identidades Gerenciadas no .NET
A autenticação de serviço para serviço (S2S) é um pilar em arquiteturas modernas de microserviços e sistemas distribuídos. Este artigo detalha como estabelecer de forma segura e eficiente a autenticação S2S entre duas aplicações .NET, aproveitando os recursos do Azure Active Directory (Azure AD) e das Identidades Gerenciadas (Managed Identities) para eliminar a complexidade do gerenciamento de segredos.
Este método é particularmente benéfico porque:
- A aplicação cliente (API Client) que inicia a chamada deve ser implantada em um ambiente Azure (ex: Azure App Service, Azure Container Apps, Máquina Virtual).
- A aplicação provedora (API Provider) que expõe o serviço pode ser implantada tanto no Azure quanto em ambientes fora do Azure.
- Nenhuma das aplicações necessita armazenar segredos de cliente (client secrets) em suas configurações ou código, o que significativamente melhora a postura de segurança.
Visão Geral do Fluxo de Autenticação S2S
O processo de autenticação de serviço para serviço usando Azure AD e Identidades Gerenciadas simplifica o padrão OAuth 2.0 Client Credentials, delegando grande parte da gestão de credenciais à plataforma Azure.
1. Configuração e Permissões Iniciais
- O API Provider precisa ser registrado como uma Aplicação no Azure AD, representando a identidade do seu serviço no diretório.
- Dentro do registro da aplicação do API Provider, são definidas funções de aplicação (App Roles). Essas funções especificam as permissões que outros serviços podem solicitar.
- Para o API Client, que deve ser executado em um recurso Azure, uma Identidade Gerenciada Atribuída ao Sistema é habilitada.
- A Identidade Gerenciada do API Client recebe permissão para uma ou mais funções de aplicação do API Provider, estabelecendo a autorização prévia para a comunicação.
2. Obtenção e Validação do Token em Tempo de Execução
- Quando o API Client necessita invocar o API Provider, ele solicita um token de acesso para o escopo do API Provider usando o SDK do Azure Identity.
- O ambiente Azure, através do Serviço de Metadados de Instância (IMDS), intercede a solicitação. Ele utiliza as credenciais da Identidade Gerenciada do API Client para autenticar-se no Azure AD e obter o token de acesso apropriado.
- O token de acesso gerado pelo Azure AD é então retornado ao API Client.
- O API Client inclui este token de acesso no cabeçalho
Authorization: Bearerde suas requisições HTTP para o API Provider. - O API Provider, configurado para autenticação JWT Bearer, recebe a requisição. Ele valida o token de acesso utilizando as chaves públicas do Azure AD para verificar sua assinatura, valdiade e claims (incluindo as funções de aplicativo).
- Se o token for válido e o API Client possuir a função necessária, a requisição é autorizada e processada. Caso contrário, o API Provider responde com um erro 401 Unauthorized.
Implementando o API Provider em .NET
O API Provider é o backend que hospeda os endpoints protegidos. Ele usará a biblioteca Microsoft.AspNetCore.Authentication.JwtBearer para validar os tokens de acesso.
1. Configuração no Azure Active Directory
-
Navegue até o Portal do Azure. Em Azure Active Directory, selecione Registros de aplicativo.
-
Clique em Novo registro. Insira um Nome significativo (ex:
ServicoDeDadosAPI), selecione as opções de Tipos de conta com suporte e clique em Registrar. -
Na página de visão geral do aplicativo recém-registrado, anote os seguintes IDs essenciais:
- ID do Aplicaitvo (cliente)
- ID do Diretório (locatário)
-
No menu à esquerda, selecione Expor uma API. Clique em Adicionar para definir o URI da ID do Aplicativo. Geralmente, o valor padrão sugerido é adequado (formato
api://{ID do Aplicativo (cliente)}). Anote este URI. -
No menu à esquerda, selecione Funções do aplicativo. Clique em Criar função de aplicativo. Defina uma nova função (ex:
ConsumidorApi) com Tipo de membro permitido definido como Aplicativos. Anote o ID da função gerado para esta função.
2. Estrutura do Projeto .NET
Crie uma nova aplicação ASP.NET Core Web API ou use um projeto existente:
dotnet new webapi -n ServicoDeDados
Instale o pacote NuGet necessário para autenticação JWT Bearer:
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
dotnet add package Microsoft.Identity.Web
3. Definições de Configuração (appsettings.json)
Configure a autenticação no arquivo appsettings.json:
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "[Seu-Dominio-AD.onmicrosoft.com]", // Ex: contoso.onmicrosoft.com
"TenantId": "[ID do Diretório (locatário) do API Provider]",
"ClientId": "[ID do Aplicativo (cliente) do API Provider]"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
4. Código C# do API Provider
Configure os serviços de autenticação e autorização em Program.cs:
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Identity.Web;
using System.Security.Claims;
var builder = WebApplication.CreateBuilder(args);
// Configura a autenticação JWT Bearer com Azure AD
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));
// Adiciona uma política de autorização baseada na função de aplicação
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("AcessoApiProtegida", policy =>
{
// Requer que o token JWT contenha a claim de função "ConsumidorApi"
policy.RequireClaim(ClaimTypes.Role, "ConsumidorApi");
});
});
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthentication(); // Deve ser chamado antes de UseAuthorization
app.UseAuthorization();
app.MapControllers();
app.Run();
Crie um controller para expor um endpoint protegido, por exemplo, Controllers/DadosController.cs:
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Security.Claims;
namespace ServicoDeDados.Controllers
{
[ApiController]
[Route("[controller]")]
public class DadosController : ControllerBase
{
[HttpGet("protegidos")]
[Authorize(Policy = "AcessoApiProtegida")] // Aplica a política de autorização
public IActionResult GetProtectedData()
{
var identidadeClaims = User.Identity as ClaimsIdentity;
if (identidadeClaims == null)
{
return Unauthorized("Identidade do cliente não processada.");
}
// Exemplo de como recuperar claims importantes do token
var clientId = identidadeClaims.FindFirst("azp")?.Value; // 'azp' é o Client ID do aplicativo cliente
var tenantId = identidadeClaims.FindFirst("tid")?.Value; // 'tid' é o Tenant ID
var roles = identidadeClaims.FindAll(ClaimTypes.Role).Select(c => c.Value).ToList();
Console.WriteLine($"Requisição de cliente com ID: {clientId}, Tenant ID: {tenantId}");
Console.WriteLine($"Funções atribuídas: {string.Join(", ", roles)}");
return Ok($"Bem-vindo ao serviço de dados! Conteúdo protegido para o cliente: {clientId}.");
}
}
}
Implementando o API Client em .NET
O API Client é o serviço que consumirá os endpoints protegidos do API Provider, utilizando uma Identidade Gerenciada para autenticação.
1. Configuração no Azure
-
Implante o API Client em um recurso Azure (ex: Azure App Service, Azure Container Apps).
-
No recurso Azure onde o API Client está hospedado, navegue até a seção Identidade (no menu à esquerda). Habilite o Status para Atribuído pelo sistema. Anote o ID do Objeto (entidade) gerado, pois ele representa a Identidade Gerenciada do seu API Client.
-
Para permitir que o API Client solicite tokens para o API Provider com a função definida, execute o comando a seguir no Azure Cloud Shell (modo Bash):
az rest \ -m POST \ --headers "Content-Type=application/json" \ -u "https://graph.microsoft.com/v1.0/servicePrincipals/{ID_OBJETO_DO_API_CLIENT}/appRoleAssignments" \ -b "{ \"principalId\": \"{ID_OBJETO_DO_API_CLIENT}\", \"resourceId\": \"{ID_OBJETO_SP_DO_API_PROVIDER}\", \"appRoleId\": \"{ID_DA_FUNCAO_DO_API_PROVIDER}\" }"Substitua os seguintes placeholders:
{ID_OBJETO_DO_API_CLIENT}: O ID do Objeto da Identidade Gerenciada do API Client (obtido no passo 2 acima).{ID_OBJETO_SP_DO_API_PROVIDER}: O ID do Objeto da Entidade de Serviço do API Provider no Azure AD. Você pode encontrá-lo em Azure Active Directory > Aplicativos empresariais. Procure pelo nome do seu API Provider e anote o ID do Objeto.{ID_DA_FUNCAO_DO_API_PROVIDER}: O ID da função de aplicativo (ex:ConsumidorApi) que você definiu e anotou no API Provider.
2. Estrutura do Projeto .NET
Crie uma nova aplicação ASP.NET Core Web App ou Console para o API Client:
dotnet new webapp -n ClienteDeApi
Instale o pacote NuGet para integração com Identidades Gerenciadas e o HttpClientFactory (para gerenciar HttpClient de forma robusta):
dotnet add package Azure.Identity
dotnet add package Microsoft.Extensions.Http
3. Código C# do API Client
Injetar IHttpClientFactory e utilizá-lo para chamar a API protegida. Modifique Program.cs para registrar HttpClientFactory:
// Em Program.cs
var builder = WebApplication.CreateBuilder(args);
// Adicionar serviços ao contêiner.
builder.Services.AddControllersWithViews();
builder.Services.AddHttpClient(); // Adicione esta linha para registrar IHttpClientFactory
var app = builder.Build();
// ... restante do pipeline de requisição HTTP ...
Exemplo de controller (Controllers/HomeController.cs) para fazer a chamada:
using Azure.Core;
using Azure.Identity;
using Microsoft.AspNetCore.Mvc;
using System.Net.Http.Headers;
namespace ClienteDeApi.Controllers
{
public class HomeController : Controller
{
private readonly IHttpClientFactory _httpClientFactory;
public HomeController(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
public async Task<iactionresult> Index()
{
// Substitua pelos seus valores configurados
var apiProviderAppIdUri = "api://[ID_DO_APLICATIVO_CLIENTE_DO_API_PROVIDER]"; // Ex: api://xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
var apiProviderEndpoint = "https://[URL_DO_SEU_API_PROVIDER]/Dados/protegidos"; // Ex: https://suaapi.azurewebsites.net/Dados/protegidos
string resultadoChamada;
try
{
// DefaultAzureCredential detecta automaticamente Identidade Gerenciada em ambientes Azure
var credencialAzure = new DefaultAzureCredential();
// Obter token de acesso para o escopo do API Provider (App ID URI + "/.default")
var contextoRequisicaoToken = new TokenRequestContext(new[] { $"{apiProviderAppIdUri}/.default" });
var resultadoToken = await credencialAzure.GetTokenAsync(contextoRequisicaoToken);
var tokenAcesso = resultadoToken.Token;
if (string.IsNullOrEmpty(tokenAcesso))
{
throw new InvalidOperationException("Não foi possível obter o token de acesso.");
}
var clienteHttp = _httpClientFactory.CreateClient();
// Adicionar o token de acesso como cabeçalho de autorização
clienteHttp.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", tokenAcesso);
// Realizar a chamada HTTP para o API Provider
var respostaHttp = await clienteHttp.GetAsync(apiProviderEndpoint);
respostaHttp.EnsureSuccessStatusCode(); // Lança uma exceção para códigos de status de erro (4xx, 5xx)
resultadoChamada = await respostaHttp.Content.ReadAsStringAsync();
}
catch (Exception ex)
{
resultadoChamada = $"Erro ao chamar o API Provider: {ex.Message}";
Console.WriteLine(resultadoChamada);
}
ViewBag.Mensagem = resultadoChamada;
return View();
}
}
}
</iactionresult>
Crie uma view simples (Views/Home/Index.cshtml) para exibir a resposta:
@page
@{
ViewData["Title"] = "Página Inicial do Cliente API";
}
<meta charset="utf-8"></meta>
<title>Cliente de API</title>
<h1>Cliente de API - Chamada S2S</h1>
<hr></hr>
<h3>Resposta do Provedor de API:</h3>
<p>@ViewBag.Mensagem</p>