Ir para o conteúdo

Keycloak como Middleware de Identidade

O Wittgenstein não implementa login, armazenamento de senhas nem federação por conta própria. Ele coloca o Keycloak entre cada usuário, cada aplicação e cada fonte de identidade externa, e o trata como middleware de identidade: as aplicações falam um único protocolo (OIDC) com um único lugar, e o Keycloak se adapta ao que cada organização já usa.

flowchart LR
    subgraph Sources["Fontes de identidade"]
        G[IdP social / workspace]
        E[IdP corporativo OIDC ou SAML]
        L[LDAP / Active Directory]
        P[Usuários locais + MFA]
    end

    KC(["Keycloak<br/>um realm por tenant"])

    subgraph Consumers["Consumidores"]
        A[Aplicações web<br/>OIDC + PKCE]
        S[Serviços e agentes<br/>client credentials]
        T[Ferramentas<br/>SAML / OIDC]
    end

    G --> KC
    E --> KC
    L --> KC
    P --> KC
    KC --> A
    KC --> S
    KC --> T
    KC -. JWKS .-> GW[AgentGateway<br/>+ extProc]
    GW --> A

Por que um middleware, e não uma biblioteca

Necessidade Resolvido pelo Keycloak Efeito na aplicação
"Nossos usuários entram com o IdP corporativo" Identity brokering (OIDC / SAML) Nenhum — a app continua recebendo um token Keycloak
"Já temos um diretório" Federação LDAP / Active Directory Nenhum
"Entrar com uma conta de workspace" IdP social / workspace, opcionalmente restrito a um domínio Nenhum
"Exigir segundo fator para administradores" Fluxos de autenticação, MFA Nenhum
"Clientes diferentes nunca podem ver os usuários uns dos outros" Um realm por tenant Apontar a app para outro authority
"Máquinas e agentes também precisam de tokens" Clients de service account (client_credentials) Mesmo caminho de verificação de JWT
"Ferramentas prontas precisam de SSO" Clients SAML e OIDC Configurado uma vez no realm

O código da aplicação é idêntico em todas essas linhas. É isso que permite a uma única plataforma atender clientes com cenários de identidade muito diferentes sem criar forks.

Modelo de tenancy

  • Um realm por tenant quando é necessário isolar usuários, políticas e identidade visual. Cada realm tem seus próprios usuários, IdPs, política de MFA, tempos de vida de token e chaves de assinatura.
  • Um client por aplicação dentro do realm, com o menor conjunto possível de redirect URIs e scopes.
  • Papéis e grupos conduzem a autorização. Os papéis do realm chegam em realm_access.roles e os grupos no claim groups; o wittgenstein-oidc-backend os expõe por get_roles e get_groups, e @has_role([...]) os aplica por rota.
from wittgenstein_oidc_backend import get_current_payload, get_groups, get_roles

payload = get_current_payload()
roles = get_roles(payload)      # ["manager", ...]
groups = get_groups(payload)    # ["/customers/acme", ...]

O significado de domínio fica no seu serviço

A biblioteca extrai claims de forma genérica. Interpretar o nome de um grupo (por exemplo, mapeá-lo para uma conta de cliente) é regra de negócio da sua aplicação, não da plataforma.

Tipos de client e grants

Client Tipo Grant Observações
Aplicação no navegador Confidencial via backend (ou público + PKCE) Authorization Code + PKCE O backend guarda o secret; veja OIDC e Segurança de Tokens
Backend / agente chamando outro serviço Confidencial client_credentials Service account com papéis estritamente necessários
Automação administrativa Confidencial client_credentials Somente os papéis de realm-management de que precisa
Ferramenta de terceiros SAML ou OIDC Conforme a ferramenta Registrada uma vez por realm

Os direct access grants (password grant) ficam desabilitados nos clients de navegador, de modo que credenciais só podem ser digitadas nas páginas do próprio Keycloak, onde valem o MFA e a proteção contra força bruta.

Mapeando identidades para a sua aplicação

  1. Claims — protocol mappers adicionam ao token os papéis, grupos e atributos de que seus serviços precisam.
  2. Provisionamento JIT — no primeiro login, jit_provision_user cria o usuário local, vincula o sub do Keycloak e sincroniza os papéis a cada login seguinte.
  3. Cabeçalhos do gateway — atrás do AgentGateway, o sidecar extProc valida o JWT contra o JWKS e injeta x-user-id e x-user-name, de modo que serviços que só precisam saber "quem está chamando" nunca precisam interpretar um token.

Realms automatizados e repetíveis

Os realms são criados por scripts idempotentes em vez de configurados manualmente: rodar o setup de novo cria o que falta e atualiza o que divergiu (identity providers sofrem upsert, não apenas criação). Um novo realm de tenant é, portanto, uma mudança revisada, e não um procedimento manual (a saída abaixo está abreviada).

./setup-keycloak.sh[keycloak] realm 'acme' ... created
[keycloak] identity provider 'workspace' ... created
[keycloak] client 'acme-app' ... created (confidential, PKCE S256)
[keycloak] client 'acme-service' ... created (service account)
[keycloak] realm role 'manager' ... created
./setup-keycloak.sh[keycloak] realm 'acme' ... exists, skipped
[keycloak] identity provider 'workspace' ... updated
[keycloak] client 'acme-app' ... exists, skipped

Operando o Keycloak

  • TLS — terminado pelo AgentGateway (o provedor de identidade roda atrás dele em modo edge proxy) ou pelo próprio Keycloak em um host dedicado, com certificados emitidos e renovados automaticamente.
  • Armazenamento — um banco PostgreSQL dedicado; nada mais o compartilha.
  • Descoberta — todo realm publica /.well-known/openid-configuration; nenhum endpoint é fixado no código das aplicações.
curl -s https://auth.example.com/realms/acme/.well-known/openid-configuration | jq '{issuer, jwks_uri, token_endpoint}'{
"issuer": "https://auth.example.com/realms/acme",
"jwks_uri": "https://auth.example.com/realms/acme/protocol/openid-connect/certs",
"token_endpoint": "https://auth.example.com/realms/acme/protocol/openid-connect/token"
}
curl -s -X POST https://auth.example.com/realms/acme/protocol/openid-connect/token \ -d grant_type=client_credentials -d client_id=acme-service -d client_secret="$SECRET" | jq 'keys'[
"access_token",
"expires_in",
"token_type"
]

Adicionando um novo tenant

Novo realm → conectar o IdP dele (ou usar usuários locais com MFA) → criar o client da aplicação → dar à app o novo authority. Nenhuma mudança no código da aplicação.