# SMART App Launch & Aidbox
telegram - https://t.me/c/1525984429/25903
## What is SMART?
SMART on FHIR - is a standard way to reuse applications between FHIR systems and a way to extend FHIR systems.
Essentially it is about how to delegate access to clinical data to third party applications on behalf of patient or provider.
* Techinically it is OAuth spec extension, which introduces scopes, jwt claims, launch flows, app registration etc.
* There are apps acting on behalf of the user (patient or provider) or backend services operating at the system level
* There are 2 versions v1 and v2 (v2 is not yet released)
## SMART & Aidbox
* SMART as a way to extend Aidbox. (i.e. unify with aidbox apps)
* SMART as a way to extend system on top of Aidbox
* SMART as key component for das Portal
## Customer Use Cases
* Implement Portal with FHIR
* For regulation like G10 or Member API
* To extend system with third-party apps
There are two important options:
1. Customer has its own identity management and we have to integrate
2. Customer does not have identity management
## Why?
- FHIR server SHALL support Smart app launch
- G10
- Health Plans patient access API regulations
- PHR & EHR needs SMART support (future)
- Patient portal as HS product (future)
- Aidbox APP as Smart APP (Backend Services)
## Why now?
- SMART App Launch is reauired by customers and leads
- We have 2 separate implementations in Aidbox forks (SMARTbox and Healthplans)
## DOD
- [ ] Review all related tasks
- [ ] Inferno tests
- [ ] Documentation how to connect smart app
## What we do not like about current solution?
- Нет экспертизы - Заставить всех пройти Inferno тесты
- Форк - не мейнтейнится - Начать мейнтейнить
## Research
- All multitenancy combinations
- FHIR Consent as Grant = (set of scopes with user_id/patient_id and client_id )
- Scopes and Security Labels as params on scope
- Aidbox APP as Smart APP
- How to link Patient with User? (Practioner)
- Multiple linked Patients/Practitioners
- Session per each linked resource
- Patient.link?
### What is Patient API?
Patient API refers to a group of FHIR server operations that allow access to a specific Patient resource and related resources. Implicitly, when the term `Patient API` is mentioned, it means there should be an additional access control layer that restricts access to resources not belonging to the specified patient.
SMART on FHIR doesn't state anything about access control, and this is actually an end resource-server concern. Instead, SOF defines utilities to help an end-resource server perform access control. Scopes are actually one of the main 'utilities' provided by SOF. Scopes have different levels, which are:
- Patient
- User
- System
In the context of Patient API, we are only interested in `patient`-level scopes. Patient-level scopes signal that the client (a.k.a. Smart App) will act on behalf of a user associated with that Patient resource. For example, a client requested access with the following scope `patient/Patient.read` (or `patient/Patient.r` for SmartV1 scope style) and the authorization server issued an access token. All requests made by the client will be in the context of one specific patient. Next, the client issues the following request:
```rest
GET [base]/Patient
```
What should happen next? Obviously, only the Patient resource on whose behalf the client acts should be returned, not every possible Patient resource on the server. Once again, the specification doesn't provide implementation guidance on how to perform such access control, nor should it. Similarly, OAuth 2.0 doesn't specify how resource servers should implement end access control.
All implementations of such access control in Aidbox rely on the following steps:
1. Construct wrappers for generic FHIR Read interactions. These wrappers enforce (inject) specific search parameters to be present in the request, namely `_id` and `patient` parameters. These parameters are supplied with the user's patient ID, which is gathered from the session, ensuring that the resulting data set will contain only data instances related to the specific patient.
2. Hide these wrapped operations under a prefix at the routing level, for example, `[base]/patient`.
3. Add a frontend middleware (first in the middleware stack) or AccessPolicy that checks that the client has sufficient authority with their scopes to perform the interaction.
Solution we see:
#### Middleware to rule them all
##### Why not AccessPolicy?
AccessPolicies are beneficial as they provide an opaque mechanism for an Aidbox platform end-user that can be easily tweaked and manipulated.
This means we don't need to provide an additional pile of environment configurations in case of emergencies.
However, Patient API access control can't be solved using AccessPolicies alone.
###### Why?
With `GET [base]/Patient` fashioned requests, we can easily check that the client **has** the appropriate scope for performing such interactions, but AccessPolicies **can't do anything about the resulting data set**.
For example, when a client performs a GET `[base]/Observation` request, we can use a pre-generated access policy to check that the client actually has the appropriate scopes to perform this interaction. However, what about the resulting set? It will obviously contain all possible observation resources present on the server.
Someone might say, *"Oh, just enforce the search parameters to be present via AccessControl"*, for example:
```yaml
resourceType: AccessPolicy
id: test-policy
engine: matcho
matcho:
params:
patient: some-patient-id
```
This AccessPolicy requires the search parameter `patient` to be present and equal to a pre-defined id, basically you put part of the AccessControl on the clients's shoulders.
But the immediate answer would be *"no"*.
Because even simple smart apps such as **Billirubin** or **GrowthChart** send just generic requests such as:
```rest
GET [base]/Observation?code=6505-5
```
to an end-resource server. They don't specify specific search parameters with a specific patient ID, *nor do they need to*.
##### What's the solution?
Add the middleware into the Aidbox web stack that:
1. checks whether the client referenced in the current session is a smart app.
2. It checks client scopes and decides if it's possible to perform the requested interaction.
3. If the interaction is possible, it injects search parameters such as `_id`/`Patient` to constrain the resulting data set.
This raises the question, *"Can I add include/revinclude in my request and exploit it to access data that doesn't belong to me, for example:*
```rest
GET [base]/Provenance?_id=prov1&_include=Provenance:based-on
```
*How can we limit that?"*
4. The answer is to perform post-filtration using CompartmentDefinitions. We can filter out resources from the resulting bundle that don't actually belong to the `Patient` using known scopes AND CompartmentDefinitions or FHIRSchema as a meta source for possible reference locations.
If a resource doesn't pass this filtration check, we can either omit it or replace it with an OperationOutcome resource, indicating that some resource is not accessible.
Middleware pseudo-code:
```:
Request issued from client
->
Naive access control:
Is the client attempting to access an interaction a smart app?
if yes:
Gather scopes granted to this client.
if the accessed interaction is allowed:
Pass the request to the next step.
else:
Respond with 403 Forbidden.
else:
Skip this access engine.
->
Inject search parameters:
if the interaction is not with a Patient resource:
Add `patient` search parameter to the initial request parameters.
else if it is a Patient read interaction:
Add `_id` search parameter to the initial request parameters.
->
Check for join-semantics search parameters (include, revinclude, has, etc.):
if such parameters are present in the initial request:
Filter the resulting dataset:
if the resource is accessible based on the current client scopes:
Gather all possible patient reference locations in the resource using patient-CompartmentDefinition or FHIRSchema.
Check these references on the resource instance.
if all references point to the user patient:
Keep the resource.
else:
Filter out the resource OR replace it with an OperationOutcome.
else:
Filter out the resource OR replace it with an OperationOutcome.
else:
Skip filtering.
->
```
- What to do with all legacy??
## What to do ???
- [ ] Client UI impruvements (for SMART)
- [ ] Patient API ??
- [ ] Promo
- [ ] Smart app example on Aidbox SDK in https://github.com/Aidbox/app-examples
- [ ] Patient / Provider
- [ ] Review (write) docs How to connect existing SMART App into Aidbox
## Current state
- No documentation on how to create a client for a FHIR Smart App / launch a Smart App
- The old Grant UI is displayed
- Access control does not consider the allowed scopes for the FHIR Smart App client
- allowed to create any resources
- allowed to retrieve arbitrary resources
## Competitors!!!
### Pairs
- SMILE - @all
- Mput & Rublag - Medplum Google
- Panthevn & Evgen - Kodjin Firely
- Rost & Ken - MS AWS
- Mike & Marat - Keycloak OKTA Intersystem
### All cometitors
* SMILE - https://smilecdr.com/docs/smart/smart_on_fhir_introduction.html
* Medplum - https://www.medplum.com/docs/integration/smart-app-launch
* Google - https://cloud.google.com/healthcare-api/docs/smart-on-fhir
* SMART proxy - https://github.com/GoogleCloudPlatform/smart-on-fhir
### Firely
Link: [Firely Server Security Documentation](https://docs.fire.ly/projects/Firely-Server/en/latest/security/smart.html#)
#### Main Points:
* Supports external identity providers.
* As tokens use JWT, specific claims are expected to be present.
* First-class citizen SMART scopes, AccessControl engine mainly relies on them
* Patient-level scopes utilize CompartmentDefinitions and internal filters for access control.
* User/system-level scopes are processed differently, not relying on CompartmentDefinitions, allowing access to any resource permitted by the scope. These levels must be managed via Plugins (code extensions of Firely server) or AccessPolicies.
#### Proprietary Solutions for Authorization?
Firely Auth acts as an OAuth 2.0 provider with extensions for SmartOnFhir, including scope/launch context awareness and smart-specific endpoints. Can integrate with external identity providers, docs shows [example](https://docs.fire.ly/projects/Firely-Server/en/latest/security/firely-auth/firely-auth-sso.html#using-microsoft-entra-id-formerly-azure-active-directory) with Entra Id
#### External Auth Servers:
The documentation states, with guides on using Active Directory (AD), that Firely Auth can be replaced with any OAuth 2.0 provider capable of handling SOF extensions and passing JWTs with the required claims.

#### Setup:
To enable authorization, you need to install a specific [plugin](https://docs.fire.ly/projects/Firely-Server/en/latest/security/smart.html) into your Firely server.
The configuration points to the OAuth 2.0 Provider base URL and requires the user to provide filters per CompartmentDefinition, which will be covered in detail in the next section.
#### [Authorization Process](https://docs.fire.ly/projects/Firely-Server/en/latest/security/tokens_and_compartments.html) (Patient-Level Scopes Only):
1. Parse and validate the JWT.
2. Check that the requested interaction is covered by the scopes claim in the JWT.
3. CompartmentDefinition access control:
* Check `launch` scopes.
* For every mentioned launch scope, find the relevant claim in the token (e.g., `launch/patient` - `patient` claim).
* For every found `launch` scope, find the relevant CompartmentDefinition based on the `CompartmentDefinition.code` value.
* Based on the filter configuration provided during plugin setup and CompartmentDefinition parameters, inject filters into the initial request.


* For Create/Update operations, resources are checked against the filters mentioned earlier before insertion.
If a resource is not present in the CompartmentDefinition but there is a scope for this resource, all resources of this type will be returned, including those not belonging to the Patient.
Firely allows defining your own CompartmentDefinitions for any resource type, although this is prohibited by the FHIR Spec.
#### User/System-Level Scopes:
For user/system-level scopes, CompartmentDefinitions are not checked, only the scopes for required interactions. For additional access control, you need to define permissions (AccessPolicies).
For more information on access control permissions, refer to the [Firely Server Permissions Documentation](https://docs.fire.ly/projects/Firely-Server/en/latest/security/permissions.html#feature-accesscontrol-permissions).
* Microsoft - https://learn.microsoft.com/en-us/azure/healthcare-apis/fhir/smart-on-fhir
* AWS HealthLake - https://docs.aws.amazon.com/healthlake/latest/devguide/smart-on-fhir.html
* InterSystems - https://openexchange.intersystems.com/package/workshop-smart
* Keycloak - https://github.com/Alvearie/keycloak-extensions-for-fhir
* OKTA - https://github.com/oktadev/okta-smartfhir-demo
MATRIX?
## Kodjin
https://docs.kodjin.com/onc-smart-on-fhir-authorization/
Server natively supports the open-source OAuth 2.0 provider - Keycloak
PKCE flow doesn't supported
### Flow
- The application registers with the Kodjin Server
- The application requests authorization
- The Kodjin Authorization Server waits for the user's consent (scopes, login/pass)
- The application exchanges the code for an Access Token
- The application gains access to the user's data by presenting a Bearer token
- Searching for Patient scopes (patient/Observation.read) is always implied by the search parameter patient
### Scopes Evaluation
- Support Scope v1
- Expands wildcard * scope (patient/*.read -> patient/Patient.read ...)
- If the user denies all requested scopes, the request will fail.
- If the user grants one or more requested scopes, the request will succeed.
## Featrues
- [ ] App Launch (Standalone & EHR)
- [ ] App registration
- [ ] App authentication (secret & jwt) - unify with aidbox client (app)
- [ ] Concent & Scopes integration
- [ ] Patient API (Compartment API?)
### Patient API
Safe API to access patient data, no risk to expose more
than one patient.
It is very close to Compartment API ( `/Patient/[id]/Observation`) and probably can be configured with CompartmentDefinition ( the problem is with write access)
Technically, patient api injects filters with patient id.
For write it may validate or inject references.
## Read more
* spec - https://build.fhir.org/ig/HL7/smart-app-launch/index.html
* long talk about SMART by josh - https://youtu.be/lMJnw6YDz5E?si=f1Ec95UACJ54e6TD
* Samurai Seminar: [g10 discussion](https://samurai.zendoc.me/seminars.g10-discussion) (high-level discussion of how smartbox works, not much technical details).
## zulip topics
* Keycloak for SMART authz:168
* ✔ reasonable search assumptions on endpoints:109
* OAuth2 and Consent:102
* SMARTv2 Connectathon, Jan 2021:87
* Health Cards:79
* September 2020 Connectathon:77
* Web messaging, scratchpad:71
* system + patient scopes:62
* Granular scopes:57
* Backend Services - Public keys:55
* Argonaut Announcements:53
* SMART Patient Access Brands Organization Example & HTI-1:52
* Refresh tokens:48
* SMARTv2 Cerner Endpoint: aud error:47
* PKCE:46
* Connectathon 28:45
* C25 -- Testing with Cerner:44
* SMART scopes v2:41
* Launch Context:41
* SMART App Launch v2:39
* Dynamic URL Port:39
* C25 -- Testing with Epic:38
* How do apps convey error information:38
* Epic app registration:36
* Standalone Patient Initiated SMART:36
* ✔ Support for IDPs that do not support "/" in scopes:35
* Refresh token for online access:34
* reason for request:34
* patient/*.read vs Patient compartment:32
* can I get API from Epic in the same way I get from DrChrono?:32
* Authorization for transactions:32
* SMART Web Messaging - ballot comment reconciliation:31
* Deprecating authorize-post?:31
* Multiple Audiences:31
* Unable to get state from launch:31
* prefetch:30
* Granular Scope Approve / Deny Reporting:29
* Is `context-standalone-patient` required by the ONC rule?:29
* backend services oauth client audience input in the token...:29
* Client requesting combination of patient/* and user/* scopes:28
* online_access scope with SMART App Launch 2.0.0:28
* .well-known/smart-configuration registered with IETF?:27
* SMART on FHIR EHR Launch flow:27
* SMART for multiple patients:26
* Patient vs User Scope:26
* Token Validity:25
* security questions:24
* SMART on FHIR with Document Bundles:24
* codes and systems:24
* backend flow, ssi:24
* Launch Sequence:23
* Azure AD:23
* CapabilityStatement URIs:23
* C25 -- Testing with smart.argo.run:23
* SMART Apps: well_known or Capabilities?:22
* App launcher ISS JWT:21
* Context-dependent authorization / authz:21
* Volume of Smart Scopes:21
* Brands Connectathon:21
* US-Core profiles for validation:21
* online access not core?:20
* The custom claims of SMART App Launch:20
* Smart Framework Supported by Patient Apps:19
* Lauch within EPIC EHR:19
* 'Optional' launch parameters:19
* Capitalized headers in SMART:19
* revoke vs manage:18
* registering an app:18
* Access Token Payload: Endpoint Reference:18
* Newbie question re scopes:18
* "federating" FHIR for patient app:18
* launch context for standalone:18
* Constraining backend to a single patient?:17
* SMART web messaging: ballot URL:17
* App Launch Implementation:17
* Using SMART scopes for authorizing Operations:17
* Confidential client required for refresh tokens:17
* Web Messaging c10n:17
* SMARTv2 Epic Endpoint:16
* Resolving FHIR-32251:16
* ✔ SMARTv2 PKCE Requirements:16
* Epic App Orchard Hyperspace simulator:16
* fhirUser when >1 resource type applies:16
* Do you support PKCE in your OAuth2 server?:15
* Launch Context Type and Role:15
* pass patient:15
* SMART scopes for operations:15
* is `jwks_uri` support required for SMART server?:15
* Connectathon: SMART Launch JWKS:14
* Launching SMART app with parameters:14
* does the launch/encounter scope limit data access?:14
* SMART on FHIR app distributed architecture:14
* Patient group scopes:14
* HL7 Australia localization of SMART App Launch:14
* Where is the OperationDefinition for $HealthWallet.issueVC:14
* Double Login:14
* SMART app deployment for multiple clients:14
* client-js best practices:14
* Azure Server-ONC Certification (g)(10) -9.3 Token Revocation:14
* Smart and/or Security Guidance and/or IUA:14
* fhirContext and Inferno testing:14
* SMART App Launcher HTTP 401:14
* OAuth 2 with Smart on FHIR App:13
* should a launch fail if `launch/encounter` can't be met?:13
* Switching patient context for provider launch:13
* /metadata vs /.well-known:13
* Clarification of requiring nonce in id_token if provided:13
* Patient launch parameter vs fhirUser claim:13
* Token Introspection Endpoint:13
* Handling irrelevant scopes:13
* Registration with EHR:13
* FHIR OAuth Scopes Discussion:13
* SMART Launcher Request depercation of POST:12
* FHIR-32325:12
* launch.smarthealthit.org signing algorithms:12
* Consent to record user's authorization:12
* Patient Scope and User scopes:12
* Patient context with user-level scopes:12
* SMART Backend Services and SMART App Launch:12
* introspection:12
* Connectathon: Jan 2022:12
* Preferable SMART launch pathway:12
* adding parameters to the SMART launch string:12
* Standalone app and patient id:12
* ✔ SMART launch with user level scopes and launch/patient:11
* Server response when client sends bad launch parameter:11
* Question on FHIR API behavior with user level scopes:11
* Specs drift:11
* Security considerations for apps with a server component:11
* How can a SMART on FHIR App close itself?:11
* SMART on FHIR dotnet application:11
* ✔ Implementing `state` safely:11
* Patient launch context and multiple patient IDs:11
* Epic backend services:11
* Provider offline access:11
* context-ehr-encounter:11
* Jan Connectathon: Track Lead Volunteers?:11
* Asking patients for consent:11