SMART on FHIR: Autorización para Apps de Salud
SMART on FHIR es el estándar abierto que define cómo las aplicaciones obtienen acceso autorizado a las APIs FHIR usando OAuth 2.0 y OpenID Connect. Especifica dos contextos de lanzamiento principales — EHR launch, donde la app se lanza desde dentro de una sesión EHR y recibe contexto de paciente y encuentro via parámetros de lanzamiento, y standalone launch, donde la app se lanza de forma independiente y negocia contexto a través de scopes — más una sintaxis de scope estandarizada que mapea las concesiones de autorización a tipos específicos de recursos FHIR e interacciones [1]. Implementar SMART correctamente no es opcional para apps de salud serias: en EE.UU., el framework SMART App Launch es parte del criterio de certificación ONC § 170.315(g)(10) para APIs de pacientes y poblaciones estandarizadas, y en nuestra experiencia los mercados de apps de proveedores de EHR lo esperan como requisito mínimo.
Una nota de versionado antes de profundizar: la sintaxis de scopes cambió entre SMART v1 y v2. El conocido estilo patient/Observation.read y user/Patient.* es SMART v1; SMART v2 reemplaza los sufijos con un subconjunto ordenado de .cruds (create, read, update, delete, search) — de modo que el acceso de lectura y búsqueda a observaciones se convierte en patient/Observation.rs, y v2 agrega scopes granulares con parámetros como patient/Observation.rs?category=...|vital-signs [2]. Los servidores anuncian qué sintaxis aceptan via las capacidades permission-v1 y permission-v2, y su app debe verificarlo antes de solicitarlos.
Paso 1: Descubrimiento
El flujo comienza con la app descubriendo la configuración SMART del servidor añadiendo /.well-known/smart-configuration a la URL base de FHIR. El servidor debe responder con un documento JSON que anuncia las URLs de los endpoints de autorización y token, capacidades soportadas, scopes y métodos de code challenge PKCE (transmitir esto via el CapabilityStatement está ahora deprecado) [3]:
GET https://ehr.example.com/fhir/.well-known/smart-configuration HTTP/1.1
Host: ehr.example.com
{
"authorization_endpoint": "https://ehr.example.com/auth/authorize",
"token_endpoint": "https://ehr.example.com/auth/token",
"capabilities": ["launch-ehr", "launch-standalone", "sso-openid-connect",
"context-ehr-patient", "permission-v2", "permission-offline"],
"code_challenge_methods_supported": ["S256"],
"scopes_supported": ["openid", "fhirUser", "launch", "launch/patient",
"patient/*.rs", "offline_access"]
}
Paso 2: Solicitud de Autorización
En un EHR launch, el EHR abre la URL de lanzamiento de la app con iss (la URL base de FHIR) y launch (un identificador de contexto opaco); la app debe reenviar ese identificador. Luego la app redirige el navegador al endpoint de autorización [1]:
GET https://ehr.example.com/auth/authorize?response_type=code&client_id=my-app-id&redirect_uri=https://myapp.example.org/callback&scope=launch+openid+fhirUser+patient/Observation.rs+patient/Patient.rs+offline_access&launch=xyz123&state=af0ifjsldkj&aud=https://ehr.example.com/fhir&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&code_challenge_method=S256 HTTP/1.1
Host: ehr.example.com
Dos de estos parámetros son donde viven las garantías de seguridad. PKCE es obligatorio: la especificación indica que todas las apps SMART DEBEN soportar Proof Key for Code Exchange, y los servidores DEBEN soportar el método de challenge S256 y NO DEBEN aceptar plain. El valor de state debe ser impredecible, de un solo uso y validado al retorno al URI de redirección para frustrar ataques CSRF e inyección de código — y el URI de redirección mismo debe coincidir exactamente con un valor pre-registrado. Para un standalone launch, omitir el parámetro launch y solicitar contexto explícitamente con scopes como launch/patient, que pide al servidor establecer un paciente (típicamente via un selector de pacientes) antes de completar la autorización.
Paso 3: Intercambio de Token y Contexto
El servidor de autorización autentica al usuario (o valida la sesión del EHR), obtiene consentimiento y redirige de vuelta con un código de autorización de corta duración, que la app intercambia via POST al endpoint de token junto con su code_verifier. La respuesta del token lleva el access token y el contexto de lanzamiento [2]:
{
"access_token": "eyJhbGciOi...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "openid fhirUser patient/Observation.rs patient/Patient.rs offline_access",
"refresh_token": "eyJraWQiOi...",
"id_token": "eyJhbGciOi...",
"patient": "e8675309",
"encounter": "enc-4021",
"need_patient_banner": true,
"smart_style_url": "https://ehr.example.com/styles/smart.json"
}
Los desarrolladores tropiezan habitualmente con los parámetros de contexto. patient indica en qué historial se está — nunca inferirlo de ningún otro lado. need_patient_banner indica a una app embebida si debe renderizar su propio banner de identificación del paciente (el EHR no está mostrando uno). Y siempre comparar el scope concedido con lo que se solicitó: los servidores pueden reducir las concesiones, y asumir lo contrario produce 403s confusos en las llamadas FHIR. Los errores comunes del endpoint de token siguen la semántica de OAuth 2.0 — invalid_grant (código expirado/reutilizado, o un code_verifier que no coincide), invalid_client (fallo de autenticación) e invalid_scope — y la app debe distinguirlos en lugar de mostrarlos como un fallo genérico de login. Para apps que necesitan acceso después de que la sesión termine, solicitar offline_access y usar el refresh token; mantener los tiempos de vida del access token cortos y rotarlos, especialmente en contextos clínicos.
Backend Services: Sin Usuario en el Proceso
No todos los clientes tienen un usuario al que redirigir. SMART Backend Services cubre el acceso autónomo sistema a sistema — extracción de datos en bloque hacia una plataforma de analítica, un servicio de monitoreo de laboratorio que genera alertas — usando el flujo de client credentials de OAuth 2.0 con una aserción JWT asimétrica en lugar de un paso de autorización orientado al usuario [4]. El cliente pre-registra su conjunto de claves públicas (JWKS), firma un JWT de autenticación de corta duración y uso único (RS384/ES384), lo envía al endpoint de token como client_assertion, y solicita scopes de tipo system/ (e.g., system/Observation.rs) que el servidor ha pre-autorizado para ese cliente. No viajan secretos por la red, y la repetición se previene mediante comprobaciones de unicidad de jti.
Patrones de Acceso SMART de un Vistazo
| Patrón | Prefijo de Scope | ¿Involucra Usuario? | Caso de Uso Típico | Mecanismo de Seguridad Clave |
|---|---|---|---|---|
| EHR Launch | patient/, user/ |
Sí — sesión EHR activa | App orientada al clínico embebida en el flujo de trabajo del EHR | Handle de lanzamiento + código de autorización + PKCE (S256) |
| Standalone Launch | patient/, user/ |
Sí — login iniciado por la app | App orientada al paciente conectándose a un portal/API | Código de autorización + PKCE; contexto via launch/patient |
| Backend Services | system/ |
No — cliente pre-autorizado | Exportación en bloque, analítica, servicios de monitoreo | Client credentials + aserción JWT asimétrica (JWKS) |
Cualquiera que sea el patrón que implemente, probarlo contra el Inferno SMART App Launch Test Kit [5], que proporciona suites para SMART STU1 y STU2 cubriendo descubrimiento, lanzamiento standalone y EHR, refresco de token, validación del ID token de OpenID Connect y autorización de Backend Services — incluyendo las rutas de error (scopes inválidos, estado no coincidente) que las pruebas ad-hoc tienden a omitir.
El Rol de CaboLabs
La autorización es donde los proyectos de apps de salud encuentran su primer obstáculo en producción: peculiaridades en la negociación de scopes entre servidores de proveedores, la fontanería de PKCE y JWKS, fallos en las pruebas de certificación dos semanas antes del plazo. CaboLabs construye y revisa integraciones SMART on FHIR — flujos OAuth del lado de la app, infraestructura de autorización del lado del servidor, y pipelines de conformidad impulsados por Inferno — como parte de nuestra práctica más amplia de ingeniería de interoperabilidad en salud en FHIR, HL7 y openEHR. El mismo patrón de delegación está llegando ahora al mundo openEHR a través de la especificación SMART on openEHR, y nuestro repositorio de datos clínicos nativo de openEHR Atomik ofrece una capa de persistencia basada en estándares para colocar detrás de esas APIs autorizadas.
Si está construyendo una app SMART, exponiendo una API FHIR segura, o diseñando la arquitectura de autorización para una plataforma de datos clínicos, contáctenos en cabolabs.com — le ayudaremos a pasar la certificación a la primera, no a la tercera.
