SMART Permission Tickets
0.1.0 - ci-build

SMART Permission Tickets, published by . This guide is not an authorized publication; it is the continuous build for version 0.1.0 built by the FHIR (HL7® FHIR® Standard) CI Build. This version is based on the current content of https://github.com/jmandel/smart-permission-tickets-wip/ and changes regularly. See the Directory of published versions

Implementer Quickstart

This page tells you what to build, by role. It is non-normative; the architecture page governs. Read this first to scope the work, then implement against the normative sections it links to.

If you operate a FHIR server (Data Holder)

You are adding one grant type to your existing SMART-on-FHIR token endpoint. No new endpoints, no user-facing screens.

  1. Accept token exchange. Handle grant_type=urn:ietf:params:oauth:grant-type:token-exchange with a subject_token_type of https://smarthealthit.org/token-type/permission-ticket. Advertise both in .well-known/smart-configuration, along with the ticket_type URIs you accept. (Transport)
  2. Authenticate the client exactly as you already do for SMART Backend Services or UDAP. The ticket never replaces client authentication. (Trust and Client Registration)
  3. Validate the ticket through the pipeline: signature against the issuer's published JWKS, issuer trust for this ticket type, exp/aud, revocation check, presenter binding, identity evidence, must-understand, subject resolution. Implement it as written — the step order matters for correct error responses. (Server-Side Validation)
  4. Resolve the subject to one local patient record from subject.patient demographics, corroborated by identity evidence when present. Zero or multiple matches → reject with invalid_grant. (Subject Resolution)
  5. Issue a scoped access token: the intersection of requested scopes, the ticket's access, client eligibility, ticket-type rules, and your own policy. Enforce data_period by applying each resource type's designated date search parameter as an implicit filter — searches your server already supports. (Access Calculation, Data Period Enforcement)
  6. Configure trusted issuers per ticket type. Trusting an issuer for patient self-access does not trust it for delegated access.

Start with UC1 (patient self access): it has no requester, no context fields, and a single policy question — can you match the patient. The conformance section is your checklist; the signed examples are your test vectors.

If you want to mint tickets (Issuer)

You verify real-world facts and sign a JWT. The Data Holder relies on your verification, so most requirements are about what you do before signing.

  1. Publish keys at {iss}/.well-known/jwks.json. (Issuer Key Publication)
  2. Run your verification workflow for the ticket type: identity proofing for the patient (UC1), the patient and the delegate plus delegation authority (UC2), organizational and event context (UC3). What you must verify per type is in the Use Case Catalog.
  3. Mint the ticket with the required claims (iss, aud, exp, iat, jti, ticket_type, subject, access), presenter binding for individual-access types, and identity evidence for each person whose verification is the basis of the grant. (Issuer Requirements)
  4. Host a revocation status list for any ticket that outlives a session, and give the authorizing person a revocation URL they can reach later without the app. (Revocation)
  5. Keep your records. The ticket carries facts; you keep the evidence behind them, retrievable by jti. (Issuer vs. Data Holder Responsibility, Proposal 007)

One deployable kickoff for delivering tickets to clients — a standard SMART App Launch whose token response carries tickets — is drafted in Proposal 003.

If you build an app (Client)

  1. Get a ticket from an issuer (out of band, or via Proposal 003).
  2. Check support in each Data Holder's .well-known/smart-configuration (grant_types_supported, smart_permission_ticket_types_supported).
  3. Present the ticket at the token endpoint: one POST with your client assertion and the ticket as subject_token, requesting SMART v2 scopes within what the ticket allows. (Request)
  4. Use the access token for FHIR reads as usual. Re-present the ticket when the token expires; get a fresh ticket from the issuer when the ticket expires.
  5. Handle interaction_required if you can: it means the Data Holder could not match the patient and wants one interactive disambiguation. Background clients can treat it as an error and move on. (Proposal 001)

The signing and verification code in the source bundle generates all the worked examples and is a working reference for each role.