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

### 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.