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

Technical Specification

Technical Specification

A. Transport: SMART Backend Services Profile

This architecture is a strict profile of SMART Backend Services (which itself profiles RFC 7523).

The key difference is the payload of the client_assertion. In standard SMART Backend Services, the assertion proves the client's identity. In this architecture, the assertion also carries the Permission Tickets as an extension claim.

The Request:

POST /token HTTP/1.1
Host: fhir.hospital.com
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials
&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer
&client_assertion=eyJhbGciOiJ... (Signed JWT containing tickets)
&scope=system/Patient.r

B. Server-Side Validation

The Data Holder must perform a two-layer validation:

  1. Layer 1: Client Authentication (Standard SMART)
    • Verify the client_assertion signature using the Client's registered public key (JWK).
    • Ensure the client is registered and active.
  2. Layer 2: Ticket Validation (Permission Ticket Specific)
    • Extract the https://smarthealthit.org/extension_tickets array from the assertion.
    • For each ticket:
      • Verify Signature: Use the iss (Trust Broker) public key.
      • Verify Trust: Is this iss in the Data Holder's trusted list?
      • Verify Binding: Does ticket.sub match assertion.sub (Client ID)?
    • Grant Access: If valid, grant the requested scopes constrained by the ticket's capability rules.

For detailed algorithms and TypeScript definitions, see the Developer Documentation.

B. The Artifact: Ticket Structure

The ticket payload wraps standard FHIR JSON objects.

{
  "iss": "https://trust-broker.org",  // Who vouches for this?
  "sub": "https://app.client.id",     // Which App can use this?
  "aud": "https://network.org",       // Where is it valid?
  "exp": 1710000000,
  
  "ticket_context": {
    // WHO is the data about? (Uses FHIR Patient shape)
    "subject": { "resourceType": "Patient", ... },

    // WHO is requesting it? (Uses FHIR Practitioner/Role/Org shapes)
    // Optional: If missing, implies the App Client is the sole actor.
    "actor": { "resourceType": "PractitionerRole", ... },

    // WHY is this allowed? (Trigger Context)
    "context": { 
      "type": { "system": "http://terminology.hl7.org/CodeSystem/v3-ActReason", "code": "REFER" },
      "focus": { "system": "http://snomed.info/sct", "code": "49436004", "display": "Atrial fibrillation" },
      "identifier": [
        { "system": "https://issuer.org/cases", "value": "CASE-123" }
      ]
    },

    // WHAT data is allowed?
    "capability": { "scopes": ["patient/Immunization.read", "patient/Condition.read"] }
  }
}

See the Logical Model for formal definitions.