Curriculum
Module 8·~45 min

Search, Security & SMART

Required search params, _include, SMART scopes, Bulk Data $export.

8.1

Required search parameters

Each US Core profile lists search parameter combinations a conformant server MUST answer. Patient-by-name, Condition-by-patient-and-clinical-status, Observation-by-patient-and-code, etc.

US Core's CapabilityStatement spells out which combos are mandatory. A few you'll use daily:

text
GET [base]/Patient?_id={id}
GET [base]/Patient?identifier={system|value}
GET [base]/Patient?name={string}&birthdate={date}

GET [base]/Condition?patient={id}&clinical-status={code}
GET [base]/Condition?patient={id}&category={code}

GET [base]/Observation?patient={id}&code={loinc}
GET [base]/Observation?patient={id}&category=vital-signs&date=ge2024-01-01

GET [base]/MedicationRequest?patient={id}&status=active&_include=MedicationRequest:medication

The _include=MedicationRequest:medication line shows how to resolve a medicationReference in one round trip — critical when the EHR uses contained Medication resources.

8.2

SMART on FHIR — auth in 3 minutes

SMART layers OAuth 2.0 + OpenID Connect on top of FHIR. Apps request scopes; users authorize them; apps get an access token tied to a patient or user context.

The dance

  1. App discovers the EHR's auth endpoints from /.well-known/smart-configuration.
  2. App redirects the user to authorize with: client_id, scope, launch, redirect_uri, state, code_challenge (PKCE).
  3. User logs in, picks a patient (if EHR launch), approves the scopes.
  4. App exchanges the code for a token at token.
  5. Every FHIR call: Authorization: Bearer {access_token}.

Scopes

Pattern: {patient|user|system}/{Resource}.{cruds}.

  • patient/Observation.rs — read & search Observations for the launch patient
  • user/*.read — read everything the user can read
  • system/Patient.read — backend-services (no human), Patient read for all patients

Old-style: patient/Observation.read. New (v2): patient/Observation.rs (c=create, r=read, u=update, d=delete, s=search). Servers should support both.

Launch contexts

  • EHR launch — EHR opens your app inside a frame, passes launch token; app gets patient/encounter context.
  • Standalone launch — user opens your app directly; app prompts for patient.
  • Backend services — no user; client uses a signed JWT to get a system-scoped token.
8.3

Bulk Data $export

When you need *every* patient's data (population analytics, payer-to-payer transfer), Bulk Data Access lets you ask the server to materialize NDJSON files asynchronously.

Three flavours of $export:

  • /$export — system-level (every patient on the server, requires system scope)
  • /Patient/$export — every Patient and their compartment
  • /Group/{id}/$export — a defined Group of patients (the common case)

Flow

  1. POST /Group/{id}/$export with Prefer: respond-async and a _type=Patient,Condition,Observation filter.
  2. Server returns 202 with Content-Location header pointing at a status URL.
  3. Poll the status URL until 200, which returns JSON listing NDJSON file URLs.
  4. Download NDJSON files (one per resource type), one resource per line.
  5. DELETE the status URL when done.

US Core 6+ recommends supporting Bulk Data v2 + the SMART Backend Services auth profile for the call.

8.4

Security checklist for production

TLS, scope minimization, refresh tokens, audit logging, redirect URI hygiene, and the trust framework you operate inside.

Quick production checklist — every box must be ticked:

  • TLS 1.2+ for every endpoint, including the well-known.
  • Strict redirect URI matching at the IdP — exact match, no wildcards.
  • PKCE for every public client (mobile, SPA).
  • Minimum scopes — never */*.* if you can avoid it.
  • Refresh tokens rotated; offline_access only when justified.
  • AuditEvent or external SIEM capturing every read/write.
  • JWT validation — verify iss, aud, exp, signature against the IdP's JWKS.
  • Per-user consent stored as Consent resources (US Core's Consent profile in newer versions).

You'll often plug into a trust framework (Carequality, TEFCA QHINs) — they layer additional identity-proofing requirements on top of SMART.

Checkpoint quiz

Answer all questions to check your understanding before moving on.

1. What does the SMART scope `patient/Observation.rs` mean?

2. How do you fetch a MedicationRequest and pull along its referenced Medication resource in one call?

3. What format does Bulk Data $export return the actual data in?