Pular para conteúdo

Internal-Trusted Authentication

Visão Geral

Internal-Trusted Authentication é um padrão de autenticação utilizado para comunicação segura entre serviços internos da arquitetura Santa Mão. Este padrão permite que sistemas confiáveis (workers, background services, APIs internas) se autentiquem sem intervenção do usuário.

Tecnologia utilizada: Keycloak - Plataforma open-source de gerenciamento de identidade e acesso.

Conceitos Fundamentais

O que é Internal-Trusted?

Internal-Trusted é um modelo de autenticação onde serviços internos da aplicação são considerados "confiáveis" e podem obter tokens de acesso automaticamente para se comunicar com outros serviços.

Características:

  • Sem interação do usuário: Autenticação automática entre serviços
  • Client Credentials Flow: Utiliza OAuth 2.0 Client Credentials Grant
  • Service-to-Service: Comunicação máquina-para-máquina
  • Centralizado: Gerenciado pelo Keycloak

Keycloak no Santa Mão

Keycloak é uma solução completa de Identity and Access Management (IAM) que fornece:

  • Single Sign-On (SSO): Login único entre aplicações
  • Identity Brokering: Integração com provedores externos (Google, Facebook, etc)
  • User Federation: Integração com LDAP/Active Directory
  • OAuth 2.0 / OpenID Connect: Protocolos padrão de autenticação
  • Fine-grained Authorization: Controle granular de permissões

URL do Keycloak (Desenvolvimento):

https://auth.santamao.com.br

Client Credentials Flow

Client Credentials Flow

Fluxo:

  1. Serviço envia client_id e client_secret para o Keycloak
  2. Keycloak valida as credenciais
  3. Keycloak retorna um access_token JWT
  4. Serviço usa o token no header Authorization: Bearer {token}
  5. API valida o token com o Keycloak

Implementação no Santa Mão

Configuração do Keycloak

Realm: santamao

Clients Internos:

Client ID Descrição Uso
notification-worker Worker de notificações Envia emails, SMS e push
payment-worker Worker de pagamentos Processa pagamentos
analytics-worker Worker de analytics Processa dados analíticos
image-worker Worker de imagens Processa uploads de imagens
cache-worker Worker de cache Gerencia invalidação de cache
internal-api API interna de administração Operações administrativas

Configuração de um Client no Keycloak

1. Criar Client

Name: notification-worker
Client Protocol: openid-connect
Access Type: confidential
Service Accounts Enabled: ON
Authorization Enabled: OFF

2. Obter Credenciais

Client ID: notification-worker
Client Secret: [gerado automaticamente pelo Keycloak]

3. Configurar Roles

Roles do Client:
- send-notification
- read-user-preferences
- write-notification-log

Implementação em .NET

appsettings.json:

{
  "Keycloak": {
    "Authority": "https://auth.santamao.com.br/realms/santamao",
    "TokenEndpoint": "https://auth.santamao.com.br/realms/santamao/protocol/openid-connect/token",
    "ClientId": "notification-worker",
    "ClientSecret": "your-client-secret-here",
    "Audience": "santamao-api"
  }
}

Obter Token (Worker):

public class KeycloakTokenService : IKeycloakTokenService
{
    private readonly HttpClient _httpClient;
    private readonly IConfiguration _configuration;
    private string? _cachedToken;
    private DateTime _tokenExpiration;

    public async Task<string> GetAccessTokenAsync(CancellationToken cancellationToken = default)
    {
        // Verificar se token em cache ainda é válido
        if (!string.IsNullOrEmpty(_cachedToken) && DateTime.UtcNow < _tokenExpiration)
        {
            return _cachedToken;
        }

        var tokenEndpoint = _configuration["Keycloak:TokenEndpoint"];
        var clientId = _configuration["Keycloak:ClientId"];
        var clientSecret = _configuration["Keycloak:ClientSecret"];

        var requestBody = new Dictionary<string, string>
        {
            { "grant_type", "client_credentials" },
            { "client_id", clientId },
            { "client_secret", clientSecret }
        };

        var request = new HttpRequestMessage(HttpMethod.Post, tokenEndpoint)
        {
            Content = new FormUrlEncodedContent(requestBody)
        };

        var response = await _httpClient.SendAsync(request, cancellationToken);
        response.EnsureSuccessStatusCode();

        var content = await response.Content.ReadAsStringAsync(cancellationToken);
        var tokenResponse = JsonSerializer.Deserialize<TokenResponse>(content);

        _cachedToken = tokenResponse.AccessToken;
        _tokenExpiration = DateTime.UtcNow.AddSeconds(tokenResponse.ExpiresIn - 60); // 60s de margem

        return _cachedToken;
    }
}

public class TokenResponse
{
    [JsonPropertyName("access_token")]
    public string AccessToken { get; set; }

    [JsonPropertyName("expires_in")]
    public int ExpiresIn { get; set; }

    [JsonPropertyName("token_type")]
    public string TokenType { get; set; }
}

Chamar API com Token:

public class NotificationApiClient
{
    private readonly HttpClient _httpClient;
    private readonly IKeycloakTokenService _tokenService;

    public async Task SendNotificationAsync(NotificationDto notification)
    {
        var token = await _tokenService.GetAccessTokenAsync();

        _httpClient.DefaultRequestHeaders.Authorization = 
            new AuthenticationHeaderValue("Bearer", token);

        var response = await _httpClient.PostAsJsonAsync(
            "https://api.santamao.com.br/v1/notifications", 
            notification);

        response.EnsureSuccessStatusCode();
    }
}

Validar Token (API):

// Program.cs ou Startup.cs
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.Authority = configuration["Keycloak:Authority"];
        options.Audience = configuration["Keycloak:Audience"];
        options.RequireHttpsMetadata = true;

        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ClockSkew = TimeSpan.FromMinutes(5)
        };
    });

services.AddAuthorization(options =>
{
    options.AddPolicy("InternalTrusted", policy =>
    {
        policy.RequireAuthenticatedUser();
        policy.RequireClaim("azp"); // Authorized party (client_id)
    });

    options.AddPolicy("NotificationWorker", policy =>
    {
        policy.RequireAuthenticatedUser();
        policy.RequireClaim("azp", "notification-worker");
    });
});

Proteger Endpoint:

[ApiController]
[Route("api/v1/notifications")]
[Authorize(Policy = "InternalTrusted")] // Qualquer serviço interno
public class NotificationsController : ControllerBase
{
    [HttpPost]
    [Authorize(Policy = "NotificationWorker")] // Apenas notification-worker
    public async Task<IActionResult> SendNotification([FromBody] NotificationDto notification)
    {
        var clientId = User.FindFirst("azp")?.Value; // notification-worker

        await _notificationService.SendAsync(notification);

        return Ok();
    }
}

Benefícios

Segurança - Autenticação centralizada e padronizada
Auditoria - Rastreamento completo de acessos por serviço
Flexibilidade - Fácil adicionar/remover permissões
Escalabilidade - Suporta múltiplas instâncias de workers
Padrão de Mercado - OAuth 2.0 / OpenID Connect
Gerenciamento Centralizado - Interface web do Keycloak
Token Caching - Performance otimizada com cache de tokens

Quando Usar e Quando NÃO Usar

✅ Usar Internal-Trusted Quando:

  • Comunicação entre serviços internos (worker → API)
  • Background jobs que acessam recursos protegidos
  • APIs administrativas internas
  • Integração entre microserviços
  • Serviços que não têm contexto de usuário

❌ NÃO Usar Internal-Trusted Quando:

  • Autenticação de usuários finais (use Authorization Code Flow)
  • Frontend web/mobile (use PKCE Flow)
  • APIs públicas abertas
  • Serviços externos não confiáveis

Segurança e Boas Práticas

1. Proteção de Credenciais

✅ Correto:

// appsettings.Production.json (não commitado)
{
  "Keycloak": {
    "ClientSecret": "#{KEYCLOAK_CLIENT_SECRET}#" // Variável de ambiente
  }
}

❌ Incorreto:

// NÃO commitar secrets no código!
{
  "Keycloak": {
    "ClientSecret": "abc123-secret-hardcoded"
  }
}

Usar: - Azure Key Vault - AWS Secrets Manager - Kubernetes Secrets - Variáveis de ambiente

2. Rotação de Credenciais

  • Rotacionar client_secret periodicamente (a cada 90 dias)
  • Suportar múltiplos secrets simultaneamente durante transição
  • Logging de quando credenciais são trocadas

3. Princípio do Menor Privilégio

✅ Correto:

notification-worker:
  - send-notification
  - read-user-preferences

❌ Incorreto:

notification-worker:
  - admin (muito amplo!)

4. Validação de Claims

public class ValidateClientMiddleware
{
    public async Task InvokeAsync(HttpContext context)
    {
        if (context.User.Identity?.IsAuthenticated == true)
        {
            var clientId = context.User.FindFirst("azp")?.Value;
            var allowedClients = new[] { "notification-worker", "payment-worker" };

            if (!allowedClients.Contains(clientId))
            {
                context.Response.StatusCode = 403;
                await context.Response.WriteAsync("Client not allowed");
                return;
            }
        }

        await _next(context);
    }
}

5. Token Expiration

  • Tokens devem ter TTL curto (5-15 minutos)
  • Implementar cache com margem de segurança
  • Não armazenar tokens permanentemente

Relacionamento com Outros Padrões

Internal-Trusted trabalha em conjunto com:

  • EDA - Workers autenticados consomem eventos do Redpanda e chamam APIs
  • CQRS - Workers executam Commands através de APIs autenticadas
  • DDD - Mantém isolamento de domínios com autenticação apropriada

Fluxo Integrado:

Fluxo Integrado com Outros Padrões

Troubleshooting

Problema: Worker não consegue obter token

  1. Verificar se client_id e client_secret estão corretos
  2. Verificar se Keycloak está acessível
  3. Verificar se client está habilitado no Keycloak
  4. Verificar logs do Keycloak

Problema: API rejeita token válido

  1. Verificar se Authority está correto na API
  2. Verificar sincronização de relógio (NTP)
  3. Verificar se Audience corresponde
  4. Verificar políticas de autorização

Problema: Performance degradada

  1. Implementar cache de token
  2. Aumentar TTL do token (com cuidado)
  3. Usar connection pooling para HTTP requests
  4. Verificar latência de rede com Keycloak

Conclusão

O padrão Internal-Trusted Authentication com Keycloak no Santa Mão proporciona autenticação segura e padronizada para comunicação entre serviços internos.

Benefícios Principais:

✅ Autenticação centralizada via Keycloak
✅ Segurança robusta com OAuth 2.0
✅ Auditoria completa de acessos
✅ Facilidade de gerenciamento de permissões
✅ Escalabilidade para múltiplos serviços

A implementação consistente deste padrão garante que todos os serviços internos se comuniquem de forma segura, auditável e seguindo padrões de mercado.


Última atualização: Novembro 2025
Maintainers: Equipe de Arquitetura Santa Mão