Security

SMART App Launch explained: scopes, launch contexts, PKCE and the patient-facing flow

SMART App Launch is the HL7 specification that lets an app run against any FHIR server with the user's authorisation: discovery, EHR and standalone launch, the authorisation code flow with PKCE, the scope syntax, launch context, refresh tokens, backend services, and what changed in SMART v2, with real requests.

by Published 6 min read

Short answer: SMART App Launch (SMART on FHIR) is HL7's specification for how an app gets authorised access to a FHIR server on behalf of a user. It is OAuth 2.0 and OpenID Connect with FHIR-specific conventions: a discovery document at .well-known/smart-configuration; two launch modes (EHR launch, where the EHR opens the app in context of the current patient, and standalone launch, where the user opens the app and picks a patient); scopes in a FHIR-aware syntax (patient/Observation.rs means read and search Observations of the patient in context); launch context returned with the token (which patient, which encounter); and backend services for system-to-system access with signed JWT client authentication. SMART v2 (2021 onwards) made PKCE mandatory, added granular scopes and asymmetric client authentication, and is the version national programmes require.

Step 1: discovery

SMART App Launch discovery starts with the app fetching the FHIR server's .well-known/smart-configuration document, which lists the authorisation and token endpoints, the capabilities supported (EHR launch, standalone launch, public and confidential clients, OpenID Connect, patient and user permissions), the scopes supported, the PKCE code challenge methods and the grant types, so the app knows where to send the user.

GET https://fhir.example-hospital.org/.well-known/smart-configuration
{
  "issuer": "https://auth.example-hospital.org",
  "authorization_endpoint": "https://auth.example-hospital.org/authorize",
  "token_endpoint": "https://auth.example-hospital.org/token",
  "introspection_endpoint": "https://auth.example-hospital.org/introspect",
  "capabilities": ["launch-ehr", "launch-standalone", "client-public", "client-confidential-asymmetric", "sso-openid-connect", "context-ehr-patient", "context-standalone-patient", "permission-patient", "permission-user", "permission-v2", "permission-offline"],
  "scopes_supported": ["openid", "fhirUser", "launch", "launch/patient", "patient/*.rs", "user/*.rs", "offline_access"],
  "code_challenge_methods_supported": ["S256"],
  "grant_types_supported": ["authorization_code", "client_credentials"]
}

The app learns where to send the user and what the server supports.

Step 2: launch

SMART App Launch has two launch modes. In an EHR launch, the clinician opens the app inside the EHR, which passes an iss parameter (the FHIR server) and an opaque launch token encoding the current patient and encounter. In a standalone launch, the user opens the app directly and the authorisation server asks them to pick or confirm a patient.

EHR launch. The clinician clicks the app inside the EHR; the EHR opens the app's launch URL with two parameters: iss (the FHIR server) and launch (an opaque token that encodes the current patient and encounter). The app must echo launch in its authorisation request with the launch scope.

Standalone launch. The user opens the app directly; the app knows the FHIR server (chosen or configured) and asks for context with launch/patient; the authorisation server asks the user to pick or confirm a patient.

Step 3: authorisation code with PKCE

The SMART authorisation step is the OAuth 2.0 authorisation code flow with PKCE: the app sends the user to the authorisation endpoint with its scopes, the aud of the FHIR server and a code challenge; the user authenticates and approves; the app exchanges the returned code and code verifier for an access token that arrives together with the launch context.

GET https://auth.example-hospital.org/authorize?
  response_type=code&client_id=my-app&redirect_uri=https://app.example.org/cb
  &scope=openid%20fhirUser%20launch%20patient/Observation.rs%20patient/Condition.rs%20offline_access
  &state=af0ifjsldkj&aud=https://fhir.example-hospital.org
  &launch=xyz123&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&code_challenge_method=S256

The user authenticates (with the hospital's identity provider) and approves the scopes; the server redirects back with a code; the app exchanges it:

POST https://auth.example-hospital.org/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&code=...&redirect_uri=https://app.example.org/cb&client_id=my-app&code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
{
  "access_token": "eyJhbGciOi...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "openid fhirUser launch patient/Observation.rs patient/Condition.rs offline_access",
  "refresh_token": "a8c3...",
  "id_token": "eyJhbGciOi...",
  "patient": "Patient/123",
  "encounter": "Encounter/456",
  "fhirUser": "Practitioner/42",
  "need_patient_banner": true,
  "smart_style_url": "https://ehr.example.org/styles/smart.json"
}

The launch context (patient, encounter, fhirUser) arrives alongside the token. Public clients (mobile and browser apps) use PKCE and no client secret; confidential clients authenticate with a secret or, preferably, with a signed JWT (private_key_jwt) under the asymmetric method.

Scopes

SMART on FHIR v2 scopes use the syntax context/resource.permissions, where context is patient, user or system and permissions combine c, r, u, d and s for create, read, update, delete and search, as in patient/Observation.rs. Granular scopes add a filter, openid and fhirUser give the user's identity, launch scopes request context, and offline_access requests a refresh token.

SMART v2 syntax: <context>/<resource>.<permissions> where context is patient (the patient in context), user (whatever the user may see) or system (backend), and permissions are any of c create, r read, u update, d delete, s search, in that order: patient/Observation.rs, user/Patient.rs, system/*.cruds. Granular scopes add a filter: patient/Observation.rs?category=http://terminology.hl7.org/CodeSystem/observation-category|vital-signs. openid fhirUser give an ID token and the user's FHIR identity; launch, launch/patient, launch/encounter request context; offline_access a refresh token for use when the user is away, online_access one valid only during the session. The v1 syntax (patient/Observation.read) is still accepted by many servers.

Backend services

For a system that acts without a user (a nightly export, an integration engine), SMART Backend Services uses client_credentials with a signed JWT assertion (the client's public keys published at a JWKS URL) and system/ scopes. No user, no launch context, strict registration.

Running it safely

Running SMART App Launch safely means registering exact redirect URIs, never using the implicit flow, storing and rotating refresh tokens securely, validating state and the aud of ID tokens, verifying tokens on every API call, enforcing both scopes and the patient context at the data layer so a token for one patient never reads another, and logging every access.

Register exact redirect URIs; never use the implicit flow; store refresh tokens securely and rotate them; validate state; check the aud of ID tokens; verify tokens on every API call (introspection or JWT validation) and enforce scopes and the patient context at the data layer, because a token for Patient/123 must never read Patient/124; log every access (Security and identity: the map). SMART also defines Patient Access Brands, a way for apps to show users which organisations they can connect to, and interoperates with SMART Health Links for patient-initiated sharing.

Where it is required

The US patient-access rules require certified EHRs to support SMART App Launch and Backend Services; national programmes in Europe adopt it for app access (the Netherlands' MedMij uses OAuth 2.0 with equivalent conventions); the IPS and many implementation guides assume it. See TEFCA and National electronic health records.

In Anpheros

Anpheros Platform uses OAuth 2.0 with PKCE for apps and signed-JWT client authentication for backends, with scopes derived from the person's consent and the patient context enforced on every request. The developer site documents authentication and the SMART on FHIR backend pattern for building on it.

Sources

Frequently asked questions

Is SMART on FHIR the same as OAuth 2.0?

It is OAuth 2.0 and OpenID Connect with FHIR-specific additions: discovery, launch context, the scope syntax and backend services. Any OAuth library works; the SMART conventions are what make an app portable across FHIR servers.

Do I need PKCE for a server-side app?

In SMART v2, yes, for all clients using the authorisation code flow. Confidential clients add client authentication (a secret or a signed JWT) on top.

What is the difference between patient/ and user/ scopes?

patient/ limits access to the single patient in the launch context; user/ grants whatever the authenticated user is allowed to see, across patients. Patient-facing apps use patient/; clinician apps often use user/ with context.

Can a SMART app write to the record?

If granted create or update permissions (c, u) in its scopes and the server allows it. Many deployments grant patient apps read and search only, and route writes through specific resources such as QuestionnaireResponse.

Anpheros Platform

Try it free in the sandbox

Your first FHIR call in 5 minutes. An HL7 FHIR R4 API with the patient's consent built in: try it in the sandbox, then connect your own app or clinic system.

Free · 30 synthetic patients · no card

This guide is for information and does not replace medical advice. For decisions about your health, talk to your doctor. In an emergency, call your local emergency number (112 in Europe).
Written by
The Anpheros team

Anpheros builds a medical record that the patient controls and can share with any clinic, on an HL7 FHIR R4 platform, based in the EU. These guides help you understand and organise your health; they do not replace your doctor.

About Anpheros →
Anpheros

The more you log, the clearer your record

Your health history in one place, under your control.

Symptom diaryPatterns show over time
Emergency cardQR in your phone's Wallet
All your resultsLabs and reports together
You decideWho sees what, and for how long
Create your free account

Free · Works in your browser · No card