Auth y scoping
El modelo
Sección titulada «El modelo»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:
- Auth contra Aidbox — usando una de las strategies pluggables.
- Scoping por tenant — URL
/Organization/{id}/fhir/*automático. - Tagging audit —
meta.tag = [{ system: 'urn:ema:tenant', code: tenant.id }]en cada resource creado.
Capas de control de acceso
Sección titulada «Capas de control de acceso»1. Producto (emaclinic, etc.) autentica al usuario (Supabase Auth, etc.)2. Producto resuelve el tenant del request → Tenant object3. Producto pasa el tenant al SDK4. 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 tenant8. Aidbox valida via ORGBAC + RLSEstrategias de auth
Sección titulada «Estrategias de auth»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,});bearerAuth(token)
Sección titulada «bearerAuth(token)»Para casos donde el token ya viene del request del usuario (legacy).
import { bearerAuth } from 'emafhir/auth';
const auth = bearerAuth(userAccessToken);Scoping por tenant
Sección titulada «Scoping por tenant»Una vez creado el cliente, todas las requests salen con la URL scoped:
POST /Organization/{tenant.aidbox.organizationId}/fhir/PatientGET /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.
Tagging para audit
Sección titulada «Tagging para audit»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.
Diferencia entre cliente humano y job
Sección titulada «Diferencia entre cliente humano y job»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
userImpersonationque firme el bearer del usuario y lo pase a Aidbox. - Un campo extra en
meta.tagconurn:ema:user.
Hoy no hay caso de uso que lo justifique.