Ir al contenido

Auth y scoping

EMA FHIR SDK no autentica usuarios finales. Asume que el producto consumidor ya autenticó al usuario y que el contexto del tenant ya está resuelto. El SDK se encarga de:

  1. Auth contra Aidbox — usando una de las strategies pluggables.
  2. Scoping por tenant — URL /Organization/{id}/fhir/* automático.
  3. Tagging auditmeta.tag = [{ system: 'urn:ema:tenant', code: tenant.id }] en cada resource creado.
1. Producto (emaclinic, etc.) autentica al usuario (Supabase Auth, etc.)
2. Producto resuelve el tenant del request → Tenant object
3. Producto pasa el tenant al SDK
4. SDK desencripta credenciales Aidbox del tenant (upstream)
5. SDK autentica contra Aidbox (clientCredentials o bearer)
6. SDK arma URL scoped a /Organization/{id}/fhir/*
7. SDK escribe el resource con meta.tag tenant
8. Aidbox valida via ORGBAC + RLS

clientCredentials(clientId, clientSecret, tokenUrl)

Sección titulada «clientCredentials(clientId, clientSecret, tokenUrl)»

OAuth2 Client Credentials — el patrón estándar para máquina-a-máquina. Cada tenant tiene su propio par, decryptado upstream del SDK por el orchestrator. El SDK obtiene el access token y lo cachea hasta vencer.

import { clientCredentials } from 'emafhir/auth';
const auth = clientCredentials({
clientId: tenant.aidbox.clientId,
clientSecret: tenant.aidbox.clientSecretDecrypted,
tokenUrl: tenant.aidbox.tokenUrl,
});

Para casos donde el token ya viene del request del usuario (legacy).

import { bearerAuth } from 'emafhir/auth';
const auth = bearerAuth(userAccessToken);

Una vez creado el cliente, todas las requests salen con la URL scoped:

POST /Organization/{tenant.aidbox.organizationId}/fhir/Patient
GET /Organization/{tenant.aidbox.organizationId}/fhir/Encounter?...

Aidbox del lado server aplica ORGBAC (Organization-based access control): aunque alguien tuviera el token de otro tenant, el path no le permitiría leer recursos fuera de su Organization.

meta.tag se inyecta automáticamente en cada create/update:

{
"resourceType": "Patient",
"meta": {
"profile": ["https://emahealth.io/fhir/cl/StructureDefinition/Patient"],
"tag": [
{ "system": "urn:ema:tenant", "code": "clinic-aurora" },
{ "system": "urn:ema:ig-version", "code": "2026.05" }
]
},
"name": [...]
}

Para qué sirve:

  • Auditoría: cualquier query “qué tenants tocaron este recurso” es trivial.
  • Migración: cuando un tenant migra de IG version, los recursos viejos conservan su tag y los nuevos llevan el actualizado.
  • Debugging: si una query devuelve recursos de otro tenant, se ve al toque en meta.tag.

Wave 1 no diferencia. Ambos casos usan clientCredentials con las credenciales del tenant. El SDK no le importa quién detrás del producto disparó la request.

Si en una iteración futura los productos necesitan distinguir (ej. para auditar por usuario individual), se puede agregar:

  • Una strategy userImpersonation que firme el bearer del usuario y lo pase a Aidbox.
  • Un campo extra en meta.tag con urn:ema:user.

Hoy no hay caso de uso que lo justifique.