Pular para conteúdo

Integração B2B — Entra app-only (client credentials / STS)

Guia de desenho e implementação para sistemas externos chamarem as APIs eCosif com access token de aplicação (sem login humano), no perímetro Azure Enterprise + API Gateway que só aceita JWT Entra assinado.

PLANID: AUTH-14
Status da entrega: implementação concluída (0.7.08.202608301); homolog E2E pendente (AUTH-14.9).
Implantação: manual_implantacao_b2b_entra_app_only.md · Entrega técnica: auth14_entrega_tecnica_b2b_app_only.md
Público: arquitetura, DevOps, integradores B2B, segurança.
Relacionado: gateway_access_token_entra.md (Modo B — token de usuário).

Extensão Itaú (AUTH-14.10): cliente Itaú usa STS interno (openid.itau.com.br) com CC + scope, sem App role Entra. Ver integracao_b2b_itau_sts_cc.md. Os dois perfis B2B convivem com login humano Azure.


1. Resumo executivo

Pergunta Resposta
Como deve funcionar? App do cliente obtém token no Entra STS (client_credentials) → gateway valida JWT → eCosif trata o app como identidade técnica com ACL empresa/filial.
O que já tem? Perímetro Entra + gateway + login/autorização de usuário (Azure Enterprise).
O que foi entregue? Ramo app-only B2B no starter, provider AZURE_ENTERPRISE_APP, API admin onboarding, logs M2M, docs ops.
O que falta? Homolog E2E com cliente (AUTH-14.9); policy gateway no ambiente do banco; publicação imagens alvo.
Atalhos aceitos? Não. Sem contornar gateway, sem JWT ECOSIF “só para o batch”, sem API key improvisada.

Antes de AUTH-14, o 403 reportado era esperado: assinatura/aud podiam passar na borda enquanto o backend não mapeava identidade de aplicação. Com a flag ECOSIF_ENABLE_AZURE_APP_ONLY=true e onboarding, o backend resolve app → usuário técnico; 403 remanescente tende a ser policy do gateway (ver spike).


2. Como deve funcionar (alvo)

┌──────────────────┐   client_credentials    ┌─────────────────────┐
│ Sistema integrador│ ─────────────────────► │ Entra ID STS        │
│ (confidential app)│ ◄───────────────────── │ /oauth2/v2.0/token  │
└────────┬─────────┘   access_token app-only └─────────────────────┘
         │
         │  Authorization: Bearer <access_token>
         │  aud = api://{api-eCosif}
         ▼
┌──────────────────┐  iss, aud, exp, JWKS    ┌─────────────────────┐
│ API Gateway      │ ──────────────────────► │ discovery keys      │
│ (obrigatório)    │  (não exigir scp)       └─────────────────────┘
└────────┬─────────┘  allowlist azp opcional
         │
         ▼
┌──────────────────┐
│ Microsserviços   │  1) Revalida JWT
│ eCosif + starter │  2) Detecta app-only vs usuário
│                  │  3) Resolve vínculo → gr_user técnico
│                  │  4) Autoriza empresa/filial
└──────────────────┘

Runtime (cada request)

  1. Integrador: POST https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token
  2. grant_type=client_credentials
  3. client_id / client_secret (ou certificado)
  4. scope=api://{api-client-id}/.default
  5. Integrador chama /ecosif-* com Authorization: Bearer <access_token>.
  6. Gateway valida assinatura RS256, iss, aud, exp.
  7. eCosif classifica o token; se app-only, resolve identidade técnica e aplica ACL.

Onboarding (uma vez)

  1. App Registration da API eCosif: App role de aplicação (ex. Ecosif.Api.Access).
  2. App Registration do integrador: confidential client + secret/cert no vault do cliente.
  3. Application permission + admin consent.
  4. No eCosif: usuário técnico + vínculo service principal (oid / appid) + UserCompanyBranch — API POST /api/admin/b2b-app-links ou seed homolog.
  5. (Recomendado) Allowlist do appid no gateway.

Offboarding

Revogar consent/secret no Entra e desativar vínculo/usuário técnico no eCosif.


3. O que já existe (baseline)

Capacidade Onde Observação
Modo B gateway Entra ECOSIF_API_TOKEN_MODE=AZURE_ENTERPRISE_GATEWAY Só JWT Entra nas APIs
Access token de usuário MSAL + scp + external-login Doc: gateway_access_token_entra.md
Validação JWKS Gateway + backends Mesmo iss/aud tipicamente ok para app-only
Identidade oid/subidentity_provider_linkgr_user Pensado para pessoa
Isolamento empresa / filial Via usuário eCosif
JWT ECOSIF / signin Modo A / automations Não usar neste cliente (gateway barra)

4. Entregas AUTH-14 (produto) e pendências ops

4.1 Desenvolvimento — concluído (0.7.08.202608301)

Item Módulo PLANID Doc
Spike claims + origem 403 structure, starter AUTH-14.1 spike
Modelo vínculo app → usuário técnico database, flyway AUTH-14.2 identity_provider_link_b2b_app.md
Resolver app-only no starter starter-security AUTH-14.3 auth14_entrega_tecnica
Bump starter nas 5 APIs auth, masterdata, … AUTH-14.4 idem
Onboarding admin /api/admin/b2b-app-links ecosif-auth AUTH-14.5 autenticacao.md
Checklist Entra + gateway structure AUTH-14.6 checklist
Logs M2M estruturados starter, auth AUTH-14.7 idem
Docs integradores + baseline structure, auth AUTH-14.8 este doc + manual

Detalhamento técnico: auth14_entrega_tecnica_b2b_app_only.md.

4.2 Ops / cliente — pendente homolog (AUTH-14.9)

Seguir manual_implantacao_b2b_entra_app_only.md e checklist_b2b_entra_app_only.md.

Entra ID

  • [ ] App role Application na API eCosif
  • [ ] App do integrador + secret/cert + rotação
  • [ ] Application permission + admin consent
  • [ ] Confirmar aud = ECOSIF_AZURE_API_AUDIENCE

API Gateway

  • [ ] Policy JWT sem exigir scp
  • [ ] Manter iss / aud / assinatura
  • [ ] Allowlist azp/appid (recomendado banco)
  • [ ] Não abrir exceção para JWT ECOSIF / Basic / API key

eCosif (ambiente alvo)

  • [ ] Flyway V0.7.00.15 aplicado
  • [ ] Imagens 0.7.08.202608301 publicadas
  • [ ] ECOSIF_ENABLE_AZURE_APP_ONLY=true após onboarding
  • [ ] Homolog E2E assinada (AUTH-14.9)

Plano interno: .internal_docs/tasks-plan/in-progress/20260825_AUTH-14_entra-app-only-b2b.md (hub local).

4.4 Variáveis — exclusivas app-only vs compartilhadas Modo B

Regra: parâmetros de validação/resolução B2B não reutilizam variáveis de login humano (ECOSIF_AZURE_EXPECTED_AUDIENCE, ECOSIF_AZURE_SCOPES, ECOSIF_AZURE_CLIENT_ID, ECOSIF_AUTH_PROVIDER, ecosif.auth.providers.*, external-login).

Exclusivas do modelo app-only (starter — ecosif.security.azure.app-only.*)

Variável Default Uso
ECOSIF_ENABLE_AZURE_APP_ONLY false Liga ramo app-only no starter
ECOSIF_AZURE_APP_REQUIRED_ROLES (vazio) CSV — App roles Entra exigidas (ex.: Ecosif.Api.Access)
ECOSIF_AZURE_APP_ALLOWED_CLIENT_IDS (vazio) Allowlist appid/azp no backend; recomendado espelhar no gateway
ECOSIF_AZURE_APP_LINK_PROVIDER AZURE_ENTERPRISE_APP Valor de identity_provider_link.provider para apps

Lookup de identidade: (provider=AZURE_ENTERPRISE_APP, external_id=<oid SP>) — fallback documentado appid/azp.

Compartilhadas com Modo B (só infraestrutura IdP / perímetro)

Variável Uso
ECOSIF_API_TOKEN_MODE=AZURE_ENTERPRISE_GATEWAY Mesmo modo RS256 nas APIs
ECOSIF_AZURE_TENANT_ID JWKS / issuer
ECOSIF_AZURE_API_AUDIENCE=api://… Mesmo aud no gateway e no backend

Proibido para app-only

Variável Motivo
ECOSIF_AUTH_PROVIDER=AZURE_ENTERPRISE Lookup de pessoa; conflita com provider APP
ECOSIF_AZURE_SCOPES Scopes delegated MSAL (humano)
ECOSIF_AZURE_EXPECTED_AUDIENCE Validação de id_token SPA
ECOSIF_AUTH_AUTO_PROVISION Auto-criação via external-login

Proibido para app-only

Variável Motivo
ECOSIF_AUTH_PROVIDER=AZURE_ENTERPRISE Lookup de pessoa; conflita com provider APP
ECOSIF_AZURE_SCOPES Scopes delegated MSAL (humano)
ECOSIF_AZURE_EXPECTED_AUDIENCE Validação de id_token SPA
ECOSIF_AUTH_AUTO_PROVISION Auto-criação via external-login

9. Obter access token (integrador)

Substitua {tenant-id}, {integrator-client-id}, {integrator-secret} e {api-client-id} (Application ID URI da API eCosif).

curl -sS -X POST \
  "https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id={integrator-client-id}" \
  -d "client_secret={integrator-secret}" \
  -d "scope=api://{api-client-id}/.default"

Resposta esperada (trecho):

{
  "token_type": "Bearer",
  "expires_in": 3599,
  "access_token": "eyJ..."
}

Chamada à API eCosif:

curl -sS -H "Authorization: Bearer ${ACCESS_TOKEN}" \
  "https://{gateway-host}/ecosif-masterdata/api/..."

Não use POST /api/auth/external-login nem POST /api/auth/signin no fluxo B2B. O secret fica no vault do integrador — nunca no eCosif.

Decodifique o payload em jwt.ms (homolog) e confira: aud = api://…, roles presentes, idtyp=app (quando emitido), appid/azp = client id do integrador.


10. Validação no gateway (app-only)

Mesmos parâmetros JWT do Modo B humano (gateway_access_token_entra.md), com diferenças:

Regra Usuário Modo B App-only B2B
Exigir claim scp Pode existir no token Não exigir na policy
Claim roles Opcional (delegated) Esperado (App role)
Allowlist appid/azp Opcional Recomendado (cliente banco)

Azure API Management

Policy base igual ao Modo B — sem validação de scope delegated. Exemplo (substituir {tenant-id} e {client-id}):

<inbound>
    <validate-jwt header-name="Authorization" failed-validation-httpcode="401"
                  failed-validation-error-message="Token Microsoft inválido">
        <openid-config url="https://login.microsoftonline.com/{tenant-id}/v2.0/.well-known/openid-configuration" />
        <audiences>
            <audience>api://{client-id}</audience>
        </audiences>
        <issuers>
            <issuer>https://login.microsoftonline.com/{tenant-id}/v2.0</issuer>
        </issuers>
        <!-- Não adicionar required-claims para scp -->
    </validate-jwt>
    <!-- Opcional: allowlist do integrador (claim appid ou azp) -->
    <!-- <check-header name="Authorization" failed-check-httpcode="403" ... /> -->
    <base />
</inbound>

Para allowlist por appid, use policy customizada APIM (ex.: extrair JWT e comparar claim) ou WAF upstream — documentar no runbook do cliente.

AWS API Gateway (HTTP API)

Igual ao Modo B: JWT Authorizer com issuer https://login.microsoftonline.com/{tenant-id}/v2.0 e audience api://{client-id}. O authorizer não valida scp por padrão. Allowlist de client id exige Lambda authorizer ou regra adicional — recomendado em produção banco.

Rotas públicas inalteradas: POST .../external-login, POST .../signin, health — integrador B2B não usa essas rotas.


11. Token usuário vs app-only

Usuário (já suportado) App-only (AUTH-14)
Como obtém MSAL / login client_credentials
Claims típicos scp, oid pessoa, e-mail roles, appid/azp, oid do SP
Gateway iss/aud/sig Idem; sem exigir scp
eCosif Link usuário Link app → usuário técnico

“Terceira via” pedida pelo cliente = este ramo app-only no mesmo Entra — não um IdP paralelo.


12. Informações mínimas do cliente

(Envio de dados limitado.)

  1. 403 do gateway ou do backend?
  2. Claims: aud, iss, roles, appid/azp, oid, idtyp (payload, sem secret)
  3. Mesmo tenant/API do Enterprise humano?
  4. App Registration + consent de aplicação já existem?
  5. Empresas/filiais e tipo de uso (ler / gravar / importar)
  6. Homolog no mesmo modo gateway da produção?

13. Checklist de homologação (alvo)

  1. [ ] Humano Azure Enterprise → APIs 200 (regressão)
  2. [ ] App vinculado → endpoint piloto 200 com empresa correta
  3. [ ] App sem vínculo → 401
  4. [ ] Token sem role / aud errado → 401
  5. [ ] JWT ECOSIF → 401 no gateway
  6. [ ] Logs sem Bearer completo; appId + usuário técnico presentes

14. Referências

Documento Conteúdo
gateway_access_token_entra.md Modo B — access token de usuário
gateway_hibrido_ecosif_azure.md Híbrido ECOSIF + Azure
variaveis_autenticacao_baseline.md Catálogo ECOSIF_*
runbook_azure_entra.md App Registration Entra
autenticacao.md Guia integradores — seção B2B + API onboarding
checklist_b2b_entra_app_only.md Checklist ops Entra + gateway (AUTH-14.6)
manual_implantacao_b2b_entra_app_only.md Runbook de implantação (ops)
auth14_entrega_tecnica_b2b_app_only.md Descrição técnica da entrega
spike_auth14_claims_app_only.md Claims app-only e diagnóstico 403
integracao_b2b_itau_sts_cc.md AUTH-14.10 — perfil Itaú CC + scope
checklist_vars_terraform_b2b_java.md Vars/Terraform — 5 APIs Java