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)¶
- Integrador:
POST https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token grant_type=client_credentialsclient_id/client_secret(ou certificado)scope=api://{api-client-id}/.default- Integrador chama
/ecosif-*comAuthorization: Bearer <access_token>. - Gateway valida assinatura RS256,
iss,aud,exp. - eCosif classifica o token; se app-only, resolve identidade técnica e aplica ACL.
Onboarding (uma vez)¶
- App Registration da API eCosif: App role de aplicação (ex.
Ecosif.Api.Access). - App Registration do integrador: confidential client + secret/cert no vault do cliente.
- Application permission + admin consent.
- No eCosif: usuário técnico + vínculo service principal (
oid/appid) +UserCompanyBranch— APIPOST /api/admin/b2b-app-linksou seed homolog. - (Recomendado) Allowlist do
appidno 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/sub → identity_provider_link → gr_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.15aplicado - [ ] Imagens
0.7.08.202608301publicadas - [ ]
ECOSIF_ENABLE_AZURE_APP_ONLY=trueapó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.)
- 403 do gateway ou do backend?
- Claims:
aud,iss,roles,appid/azp,oid,idtyp(payload, sem secret) - Mesmo tenant/API do Enterprise humano?
- App Registration + consent de aplicação já existem?
- Empresas/filiais e tipo de uso (ler / gravar / importar)
- Homolog no mesmo modo gateway da produção?
13. Checklist de homologação (alvo)¶
- [ ] Humano Azure Enterprise → APIs 200 (regressão)
- [ ] App vinculado → endpoint piloto 200 com empresa correta
- [ ] App sem vínculo → 401
- [ ] Token sem role /
auderrado → 401 - [ ] JWT ECOSIF → 401 no gateway
- [ ] 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 |