Saltar a contenido

Entra ID + API Management — Finance (MVP)

Autenticación de usuarios del portal con Microsoft Entra ID, llamadas al Finance API preferentemente vía Azure API Management con JWT, rate limit y correlation id.


1. Visión: autenticación vs autorización

flowchart TB
  subgraph AuthN["Autenticación — ¿quién eres?"]
    U[Usuario] --> ENTRA[Entra ID login]
    ENTRA --> JWT[Access token<br/>aud = API app<br/>scp = access_as_user]
  end

  subgraph Gate["Quién puede entrar a la app"]
    JWT --> EA{Enterprise application<br/>Assignment required?}
    EA -->|No| ANY[Cualquier usuario del tenant]
    EA -->|Yes| GRP[Solo Users and groups<br/>Finance.Reviewer / Finance.User]
  end

  subgraph AuthZ["Autorización — ¿qué puedes hacer?"]
    ANY --> APIM[APIM validate-jwt]
    GRP --> APIM
    APIM -->|token OK| API[finance-api]
    API --> COARSE[MVP hoy: casi todo-o-nada]
    API -.-> FINE[Futuro: Reviewer-only en approve/reject]
  end
Capa Responsable Qué controla hoy
Login + token Entra Identidad
Assignment required Enterprise app SPA Quién puede autenticarse en el portal
validate-jwt APIM Token válido (issuer/audience)
Roles por endpoint finance-api Pendiente (grupos creados, no enforcement fino)

2. Arquitectura de llamadas

sequenceDiagram
  actor U as Usuario
  participant Entra as Entra ID
  participant Web as finance-web
  participant Proxy as Next /api/finance/*
  participant APIM as APIM
  participant API as finance-api ACA

  U->>Web: Abrir portal
  Web->>Entra: MSAL loginRedirect
  Entra-->>Web: id_token + access_token
  U->>Web: Acción (lista, PDF, chat, HITL)
  Web->>Proxy: fetch + Authorization Bearer
  Proxy->>APIM: APIM_GATEWAY_URL + Bearer + x-correlation-id
  Note over Proxy,API: Si APIM_GATEWAY_URL vacío → FINANCE_API_BASE_URL (ACA directo)
  APIM->>APIM: rate-limit IP · validate-jwt · rate-limit sub · correlation
  APIM->>API: forward
  API-->>Web: JSON / PDF

3. Apps y grupos (DEV creado)

Artifact Valor DEV
Tenant a1d4709b-8867-4715-bfb6-a60ac27ce3c3
SPA eai-finance-web-dev Client ID 57e764e2-e373-4d10-a17d-22112e6d7d27
API eai-finance-api-dev Client ID fedbcc05-e352-4a17-abbe-c84f0dd3d443
Audience / App ID URI api://fedbcc05-e352-4a17-abbe-c84f0dd3d443
Scope api://fedbcc05-…/access_as_user
Redirects SPA AFD ep-finance-dev-….azurefd.net/ (+ ACA + localhost)
Grupos Finance.Reviewer, Finance.User

La política del tenant exige App ID URI api://{appId} (no api://eai-finance-api-dev).

Authority: https://login.microsoftonline.com/{tenant_id}
Scopes SPA: openid, profile, api://{api-app-id}/access_as_user


4. Cómo restringir quién se conecta

  1. Entra ID → Enterprise applicationseai-finance-web-dev
  2. PropertiesAssignment required? = Yes
  3. Users and groups → asignar Finance.Reviewer y/o Finance.User (y usuarios puntuales)

Solo esos podrán completar el login. Ver miembros en Groups → Members.


5. Crear / actualizar apps (script)

Terraform SP suele no poder crear app registrations. Usar:

./scripts/create_entra_finance_apps.ps1 `
  -TenantId "a1d4709b-8867-4715-bfb6-a60ac27ce3c3" `
  -Environment dev `
  -WebRedirectUri "https://ep-finance-dev-gfayg5axbhd7h5hf.z02.azurefd.net/" `
  -SkipLogin

Añadir también en el SPA (Graph / Portal) el FQDN ACA y http://localhost:3000/ si hace falta debug local. Ver edge-front-door.md.

Salida local (gitignored): infra/environments/dev/entra-apps.dev.env

Admin consent (Global Admin / Privileged Role Admin):

Entra → App registrations → eai-finance-web-dev → API permissions → Grant admin consent
(access_as_user + User.Read).

Validación Graph: grants AllPrincipals para ambos scopes.


6. Terraform APIM

Archivos: infra/environments/dev/apim.tf, entra.tf, policies/apim-finance-api-policy.xml

Variable Rol
enable_apim true en pipeline para crear APIM Developer
entra_finance_spa_client_id SPA client id
entra_finance_api_audience Audience JWT
enable_entra_app_registrations false por defecto (usar script)

Pipeline eai-terraform-dev pasa enable_apim=true + IDs Entra. Apply: ManualValidation + environment (APIM ~30–60 min).

Policy inbound: rate-limit-by-key 120/min por IPvalidate-jwtgroups User|Reviewer (403) → HITL Reviewer → rate-limit-by-key 300/min por JWT subx-correlation-id + backend = FQDN finance-api.


7. Variables finance-web (ACA)

Variable Comportamiento
FINANCE_API_BASE_URL Fallback directo al ACA API
APIM_GATEWAY_URL Si está set → proxy usa gateway
NEXT_PUBLIC_ENTRA_* / runtime /api/auth/config Vacío = modo demo; con client id = login MSAL
FRONT_DOOR_ID Si set → solo tráfico con X-Azure-FDID (ver edge-front-door.md)

Config Entra se lee en runtime (/api/auth/config) para no depender solo del bake de Next.
Entrada pública del portal: Front Door (no el FQDN ACA).


8. Hardening posterior

  • Assignment required en Enterprise app
  • Exigir grupo Finance.Reviewer en POST …/approve|reject|returnimplementado (API + APIM + UI). Token debe incluir claim groups (app API: groupMembershipClaims=SecurityGroup).
  • Exigir Finance.User o Finance.Reviewer para entrar al portal / llamar al API — implementado (pantalla AccessDenied + APIM 403 + middleware API).
  • Borde ACA (DEV): con enable_apim=true, Terraform pone ip_security_restriction Allow solo a azurerm_api_management.public_ip_addresses (hoy 68.155.193.213/32). Sigue external_enabled=true porque CAE y APIM Developer no tienen VNet — external_enabled=false dejaría APIM sin ruta al backend.
  • Smoke: GET FQDN ACA → 403; vía APIM + JWT → backend (sin JWT → 401 en gateway).
  • Futuro: VNet + APIM en modo red → entonces sí external_enabled=false.
  • Validación JWT defensiva dentro de finance-api (refuerzo)

9. Relacionado