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
| Official URL: http://smarthealthit.org/ig/permission-tickets/ImplementationGuide/smart.permission-tickets | Version: 0.1.0 | |||
| Draft as of 2026-03-06 | Computable Name: SmartPermissionTickets | |||
A Permission Ticket is an issuer-signed, sender-constrained JWT presented inside a SMART Backend Services client_assertion. It allows a client to redeem a portable authorization grant at any eligible Data Holder within the ticket's audience, without requiring the issuer to know where the subject has received care.
Each ticket conveys a common authorization structure: a subject (whose data), an optional requester (on whose behalf), and access constraints (what, and how much). Ticket-type-specific business semantics live in an optional details object whose schema is selected by ticket_type. SMART scopes provide the coarse access ceiling. Structured access constraints express finer limits — time range, jurisdiction, source organization.
When present, a cnf.jkt claim cryptographically binds the ticket to the presenting client's key. A Data Holder verifies the client assertion, verifies the ticket signature against the issuer's published keys, enforces key binding if present, and grants access scoped to the intersection of requested and authorized access. No user login is required at the Data Holder.
This specification defines:
client_assertion at the token endpointcnf.jktThis specification does not define:
sequenceDiagram
participant Trigger as Trigger Event
participant Issuer as Trusted Issuer
participant Client as Client App
participant Server as Data Holder (FHIR)
Note over Trigger, Client: 1. Context Established
Trigger->>Issuer: Event (e.g. Referral, Case Report)
Issuer->>Issuer: Verify Context & Identity
Issuer->>Client: Mint Permission Ticket (JWT)
Note over Client, Server: 2. Redemption
Client->>Client: Generate Client Assertion (JWT)
Client->>Client: Embed Ticket in Assertion
Client->>Server: POST /token (client_credentials + assertion)
Note over Server: 3. Validation
Server->>Server: Verify Client Signature
Server->>Server: Verify Ticket Signature (Issuer Trust)
Server->>Server: Enforce Ticket Constraints
Server-->>Client: Access Token (Down-scoped)
Note over Client, Server: 4. Access
Client->>Server: GET /Patient/123/Immunization
Server-->>Client: FHIR Resources
A trusted issuer mints a Permission Ticket and delivers it to the client. The client embeds the ticket in a signed client_assertion and presents it to the Data Holder's token endpoint. The Data Holder authenticates the client (standard SMART Backend Services), then validates the ticket: signature, issuer trust, audience, key binding, and access constraints. If valid, it issues an access token scoped to the intersection of requested and ticket-authorized access.
This architecture reuses SMART Backend Services client authentication and token endpoint conventions (which themselves profile RFC 7523), and adds Permission Ticket presentation and validation semantics. The client_assertion continues to authenticate the client. Permission Tickets contribute authorization context only; they do not replace client authentication.
This specification is designed so that client identity does not need to be universally understood. The Permission Ticket carries the authorization context; the client only needs to prove it holds the key bound to the ticket. Data Holders need to authenticate clients, but do not need to maintain a shared global client registry.
Several client registration models are compatible with this architecture:
trust_chain in the header of its client_assertion, allowing the Data Holder to verify the client's metadata and trust status dynamically via a common Trust Anchor — no pre-registration required.Client ID format and registration details are determined by the chosen registration model. Client-to-Issuer issuance protocol details are out of scope for this specification; profile-specific guides may define them.
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=patient/Observation.rs
Here is what the client_assertion looks like when decoded. Note the embedded Permission Ticket.
{
"alg": "ES256",
"kid": "nvOGRCsTz2QIQLsbl0ZQ_ux0tfyh5iave-jvNsANWv8",
"trust_chain": [
"eyJhbGciOiJFUzI1NiIs... (Signed Entity Statement for Client)",
"eyJhbGciOiJFUzI1NiIs... (Signed Entity Statement for Intermediate)",
"eyJhbGciOiJFUzI1NiIs... (Signed Entity Statement for Trust Anchor)"
]
}
{
"iss": "https://app.client.id",
"sub": "https://app.client.id",
"aud": "https://network.org/token",
"jti": "assertion-jti-123",
"iat": 1772827301,
"exp": 1772827601,
"permission_ticket_profile": "https://smarthealthit.org/permission-ticket-profile/network-patient-access-v1",
"permission_tickets": [
"eyJhbGciOiJFUzI1NiIsImtpZCI6Im52T0dSQ3NUejJRSVFMc2JsMFpRX3V4MHRmeWg1aWF2ZS1qdk5zQU5XdjgifQ.eyJpc3MiOiJodHRwczovL3RydXN0ZWQtaXNzdWVyLm9yZyIsInN1YiI6ImdyYW50LWV4YW1wbGUtcGF0aWVudC1hY2Nlc3MiLCJhdWQiOiJodHRwczovL25ldHdvcmsub3JnIiwiZXhwIjoxNzcyODMwOTAxLCJ0aWNrZXRfdHlwZSI6Imh0dHBzOi8vc21hcnRoZWFsdGhpdC5vcmcvcGVybWlzc2lvbi10aWNrZXQtdHlwZS9uZXR3b3JrLXBhdGllbnQtYWNjZXNzLXYxIiwiY25mIjp7ImprdCI6Ikp1STZpYlpIY01QUUlDYUlaNTVQYlhwbnN1ZFFtS3QwMEQwQmlFWE5yTWMifSwiYXV0aG9yaXphdGlvbiI6eyJzdWJqZWN0Ijp7InR5cGUiOiJyZWZlcmVuY2UiLCJyZXNvdXJjZVR5cGUiOiJQYXRpZW50IiwiaWQiOiIxMjMifSwiYWNjZXNzIjp7InNjb3BlcyI6WyJwYXRpZW50LyoucnMiXX19LCJpYXQiOjE3NzI4MjczMDF9.UppoGXpzD37H3NKn0ORaZPLI_OC92uRyQOlUkK2rtZNDmwvB_LhK-FwPS_OH4RMuygdhIQh4rajwd7ehnLWMvQ"
]
}
eyJhbGciOiJFUzI1NiIsImtpZCI6Im52T0dSQ3NUejJRSVFMc2JsMFpRX3V4MHRmeWg1aWF2ZS1qdk5zQU5XdjgiLCJ0cnVzdF9jaGFpbiI6WyJleUpoYkdjaU9pSkZVekkxTmlJcy4uLiAoU2lnbmVkIEVudGl0eSBTdGF0ZW1lbnQgZm9yIENsaWVudCkiLCJleUpoYkdjaU9pSkZVekkxTmlJcy4uLiAoU2lnbmVkIEVudGl0eSBTdGF0ZW1lbnQgZm9yIEludGVybWVkaWF0ZSkiLCJleUpoYkdjaU9pSkZVekkxTmlJcy4uLiAoU2lnbmVkIEVudGl0eSBTdGF0ZW1lbnQgZm9yIFRydXN0IEFuY2hvcikiXX0.eyJpc3MiOiJodHRwczovL2FwcC5jbGllbnQuaWQiLCJzdWIiOiJodHRwczovL2FwcC5jbGllbnQuaWQiLCJhdWQiOiJodHRwczovL25ldHdvcmsub3JnL3Rva2VuIiwianRpIjoiYXNzZXJ0aW9uLWp0aS0xMjMiLCJpYXQiOjE3NzI4MjczMDEsImV4cCI6MTc3MjgyNzYwMSwicGVybWlzc2lvbl90aWNrZXRfcHJvZmlsZSI6Imh0dHBzOi8vc21hcnRoZWFsdGhpdC5vcmcvcGVybWlzc2lvbi10aWNrZXQtcHJvZmlsZS9uZXR3b3JrLXBhdGllbnQtYWNjZXNzLXYxIiwicGVybWlzc2lvbl90aWNrZXRzIjpbImV5SmhiR2NpT2lKRlV6STFOaUlzSW10cFpDSTZJbTUyVDBkU1EzTlVlakpSU1ZGTWMySnNNRnBSWDNWNE1IUm1lV2cxYVdGMlpTMXFkazV6UVU1WGRqZ2lmUS5leUpwYzNNaU9pSm9kSFJ3Y3pvdkwzUnlkWE4wWldRdGFYTnpkV1Z5TG05eVp5SXNJbk4xWWlJNkltZHlZVzUwTFdWNFlXMXdiR1V0Y0dGMGFXVnVkQzFoWTJObGMzTWlMQ0poZFdRaU9pSm9kSFJ3Y3pvdkwyNWxkSGR2Y21zdWIzSm5JaXdpWlhod0lqb3hOemN5T0RNd09UQXhMQ0owYVdOclpYUmZkSGx3WlNJNkltaDBkSEJ6T2k4dmMyMWhjblJvWldGc2RHaHBkQzV2Y21jdmNHVnliV2x6YzJsdmJpMTBhV05yWlhRdGRIbHdaUzl1WlhSM2IzSnJMWEJoZEdsbGJuUXRZV05qWlhOekxYWXhJaXdpWTI1bUlqcDdJbXByZENJNklrcDFTVFpwWWxwSVkwMVFVVWxEWVVsYU5UVlFZbGh3Ym5OMVpGRnRTM1F3TUVRd1FtbEZXRTV5VFdNaWZTd2lZWFYwYUc5eWFYcGhkR2x2YmlJNmV5SnpkV0pxWldOMElqcDdJblI1Y0dVaU9pSnlaV1psY21WdVkyVWlMQ0p5WlhOdmRYSmpaVlI1Y0dVaU9pSlFZWFJwWlc1MElpd2lhV1FpT2lJeE1qTWlmU3dpWVdOalpYTnpJanA3SW5OamIzQmxjeUk2V3lKd1lYUnBaVzUwTHlvdWNuTWlYWDE5TENKcFlYUWlPakUzTnpJNE1qY3pNREY5LlVwcG9HWHB6RDM3SDNOS24wT1JhWlBMSV9PQzkydVJ5UU9sVWtLMnJ0Wk5EbXd2Ql9MaEstRndQU19PSDRSTXV5Z2RoSVFoNHJhandkN2VobkxXTXZRIl19.Z4UHCToF6VBcMILAcuUCGY4FabH6CvgFwJQ4pom97sDmrfoPcah6OL8NfcoyVpTc-8v6CB6Seip4x8IZZlaquA
The client_assertion serves two roles: it authenticates the client (standard SMART Backend Services) and acts as the cryptographic presentation envelope for one or more Permission Tickets.
A Permission Ticket authorizes access only when presented inside a valid client_assertion. When the ticket includes cnf.jkt, the Data Holder SHALL verify that the thumbprint matches the key used to sign the assertion — this ensures the ticket can only be redeemed by the client it was issued to. When cnf is absent, the Data Holder relies on aud validation and standard client authentication.
The Data Holder SHALL NOT rely on any cross-party-stable client identifier inside the Permission Ticket itself. Client identity is established by the outer client_assertion (iss/sub). The ticket's sub claim is issuer-local and opaque — it identifies the authorization grant, not the client.
The ticket payload is a JWT. It carries a common authorization claim with subject, access constraints, and optional requester. Ticket-type-specific business semantics (reason codes, case identifiers, etc.) live in an optional details object whose schema is defined by the ticket_type URI.
{
"iss": "https://trusted-issuer.org",
"sub": "grant-example-patient-access",
"aud": "https://network.org",
"exp": 1735689600,
"ticket_type": "https://smarthealthit.org/permission-ticket-type/network-patient-access-v1",
"cnf": {
"jkt": "0ZcOCORZNYy-DWpqq30jZyJGHTN0d2HglBV3uiguA4I"
},
"authorization": {
"subject": {
"type": "match",
"traits": {
"resourceType": "Patient",
"name": [
{
"family": "Smith",
"given": [
"John"
]
}
],
"birthDate": "1980-01-01",
"identifier": [
{
"system": "urn:oid:2.16.840.1.113883.4.1",
"value": "***-**-1234"
}
],
"telecom": [
{
"system": "phone",
"value": "555-867-5309"
}
],
"address": [
{
"state": "IL"
}
]
}
},
"access": {
"scopes": [
"patient/Immunization.rs",
"patient/AllergyIntolerance.rs"
]
}
}
}
See the Logical Model for formal definitions.
Every Permission Ticket SHALL include ticket_type. The ticket_type identifies the ticket's schema and processing rules. In single-ticket flows, the Data Holder uses ticket_type to select validation and access logic. In multi-ticket flows, ticket_type identifies each component ticket's role within a composition profile.
A Permission Ticket MAY bind redemption to a specific client key using the cnf (Confirmation, RFC 7800) claim:
cnf.jkt: JWK Thumbprint (RFC 7638) of the authorized client's public keyWhen cnf is present, the Data Holder SHALL compute the JWK Thumbprint of the key used to verify the client_assertion signature and compare it to cnf.jkt. The ticket SHALL be rejected if the thumbprints do not match.
When cnf is absent, the ticket does not constrain which client may present it. The Data Holder still authenticates the client via the client_assertion and validates the ticket's aud claim. This mode is appropriate for B2B flows where the issuer may not know the recipient's specific client key at ticket-minting time — for example, when a ticket accompanies a referral or case report and the recipient organization's app is not yet determined.
cnf is RECOMMENDED. Individual ticket types define whether it is required or optional:
| Ticket Type | cnf |
Rationale |
|---|---|---|
| UC1: Patient Access | Required | Issuer has direct relationship with client |
| UC2: Authorized Rep | Required | Issuer has direct relationship with client |
| UC3: Public Health | Optional | B2B; aud + client auth sufficient |
| UC4: Social Care | Optional | B2B; aud + client auth sufficient |
| UC5: Payer Claims | Optional | B2B; aud + client auth sufficient |
| UC6: Research | Required | Issuer has direct relationship with client |
| UC7: Provider Consult | Optional | B2B; strictly better than status quo even without key binding |
The Data Holder SHALL perform a two-layer validation:
client_assertion signature using the Client's registered public key (JWK).permission_tickets array from the assertion.permission_ticket_profile from the assertion and select composition rules. Otherwise, select processing rules based on the ticket's ticket_type.iss (Trusted Issuer) public key.iss in the Data Holder's trusted list?ticket_type SHALL be present and recognized.cnf is present, does the JWK Thumbprint of the client_assertion signing key match the ticket's cnf.jkt?authorization.access rules.The authorization.subject identifies whose data the ticket authorizes access to. Every subject SHALL include a type field that declares how the Data Holder should resolve the subject to a local patient. The three modes are:
| Mode | Required Fields | Prohibited Fields | Description |
|---|---|---|---|
match |
traits |
id, reference, identifier |
Data Holder matches by demographics (name, DOB, identifiers in traits) |
identifier |
identifier |
traits, id, reference |
Data Holder looks up by business identifier (MRN, MPI ID, etc.) |
reference |
reference or id |
traits, identifier |
Data Holder resolves a local resource reference directly |
If subject resolution yields zero matches, or more than one match, the Data Holder SHALL reject the request with invalid_grant and an appropriate error_description.
authorization.requester and details are issuer-attested facts. The Data Holder uses them for policy evaluation and audit, unless a specific profile requires additional holder-side verification.
If requester is absent, the ticket does not assert a separate third-party requester. This does not mean anonymous access — the presenting client is still authenticated by the outer client_assertion.
If details is absent, the ticket type has no business-specific fields beyond the common authorization claims.
The Data Holder calculates granted access through the intersection of:
scope parameter in the token requestauthorization.accessIf the intersection yields no valid access, return invalid_scope error.
Requested scopes SHALL use SMART scope grammar. This specification allows either patient/* or system/* scopes depending on profile. For single-patient Permission Ticket profiles, clients SHOULD request SMART v2 CRUDS suffix scopes (for example, patient/Observation.rs).
The authorization.access object defines what access the ticket authorizes:
| Field | Type | Description |
|---|---|---|
scopes |
string[] | SMART scopes (e.g., patient/*.rs). Wildcard scopes expand to match specific requests. |
periods |
Period[] | Time restrictions. Data Holder SHALL filter results to resources with relevant dates within these periods. |
jurisdictions |
object[] | Jurisdictional restrictions at state granularity (country, state/subdivision). Street, city, and postal code are not used. If present, Data Holder SHALL limit results to data whose jurisdiction of care or source data matches one of the listed jurisdictions. |
organizations |
object[] | Source organization restrictions. If present, Data Holder SHALL limit results to data from matching organizations. Matching SHALL be by identifier when available; name is display-only and SHALL NOT be the sole matching key when an identifier is present. |
Different access dimensions are combined conjunctively (AND): returned data must satisfy every present constraint. Multiple values within the same dimension are combined disjunctively (OR): data matching any listed value within a dimension satisfies that dimension. An absent dimension means no restriction for that dimension.
For example, a ticket with jurisdictions: [{state: "CA"}, {state: "NY"}] and organizations: [{identifier: [...npi: "123"]}] means: data from (CA or NY) and from the organization with NPI 123.
| Dimension | What it restricts | Matching basis |
|---|---|---|
scopes |
Coarse SMART authorization ceiling (resource types and actions) | SMART scope grammar |
periods |
Relevant clinical or service dates of returned data | Date comparison against resource date elements |
jurisdictions |
Jurisdiction of care or source data, at state granularity | Country and state/subdivision codes |
organizations |
Source organization of returned data | Organization identifier (NPI, etc.) |
Data Holders that cannot enforce a presented constraint SHALL reject the ticket with invalid_grant and error_description indicating the unsupported constraint.
Example Access Constraints:
{
"access": {
"scopes": [
"patient/Condition.rs",
"patient/Procedure.rs"
],
"periods": [
{
"start": "2023-01-01",
"end": "2024-12-31"
}
],
"jurisdictions": [
{
"state": "CA"
},
{
"state": "NY"
}
],
"organizations": [
{
"identifier": [
{
"system": "http://hl7.org/fhir/sid/us-npi",
"value": "1234567890"
}
]
}
]
}
}
This ticket authorizes read access to Conditions and Procedures, but only for data:
Some access constraints — especially periods, jurisdictions, and organizations — may require filtering at the Resource Server rather than at the token endpoint. If a constraint cannot be fully enforced at token issuance, the Authorization Server SHALL carry the normalized constraint set forward in the issued access token (or make it available via token introspection) so the Resource Server can enforce it.
If a component responsible for enforcing a constraint cannot do so, the request SHALL be rejected rather than silently ignoring the constraint.
aud) and Recipient SetFor Permission Tickets, aud identifies the set of eligible Data Holders that may honor the ticket. It does not imply that the issuer knows where the subject has received care or where data is actually held. This recipient set may be expressed as one or more enumerated recipient URLs, or as a network / trust framework identifier whose membership can be validated by the Data Holder.
This is distinct from aud in the outer client_assertion, which remains the Data Holder's token endpoint URL per SMART Backend Services.
The aud is a specific URL or array of URLs:
{ "aud": "https://fhir.hospital.com" }
// or
{ "aud": ["https://fhir.hospital-a.com", "https://fhir.hospital-b.com"] }
Validation: The Data Holder's base URL SHALL exactly match one of the enumerated values.
The aud references a trust framework identifier:
{ "aud": "https://tefca.hhs.gov" }
Validation: The Data Holder SHALL be a verified participant in the referenced trust framework. Verification mechanisms are trust-framework-specific (e.g., the Data Holder's Entity ID appears in the framework's federation).
| Scenario | Recommended aud |
|---|---|
| Ticket for known single recipient | Specific Data Holder URL |
| Ticket valid across a network | Trust framework identifier |
| Ticket for multiple known recipients | Array of Data Holder URLs |
Data Holders SHALL reject tickets where aud validation fails with error invalid_grant and error_description: "Ticket not valid for this server".
Note: Multi-ticket composition is informative in this version. It is outside the minimum conformance requirements unless a specific profile explicitly requires it. Implementations MAY support multi-ticket composition, but single-ticket flows are the normative center of this specification.
A client MAY present multiple tickets in the permission_tickets array to compose authorization from multiple sources.
Multi-ticket scenarios are defined by use case profiles. Each profile specifies:
For the current single-ticket catalog in this specification, use case and profile are 1:1:
| Use Case | Profile URI | Canonical ticket_type URI |
|---|---|---|
| Use Case 1: Network-Mediated Patient Access | https://smarthealthit.org/permission-ticket-profile/network-patient-access-v1 | https://smarthealthit.org/permission-ticket-type/network-patient-access-v1 |
| Use Case 2: Authorized Representative (Proxy) | https://smarthealthit.org/permission-ticket-profile/authorized-representative-v1 | https://smarthealthit.org/permission-ticket-type/authorized-representative-v1 |
| Use Case 3: Public Health Investigation | https://smarthealthit.org/permission-ticket-profile/public-health-investigation-v1 | https://smarthealthit.org/permission-ticket-type/public-health-investigation-v1 |
| Use Case 4: Social Care (CBO) Referral | https://smarthealthit.org/permission-ticket-profile/social-care-referral-v1 | https://smarthealthit.org/permission-ticket-type/social-care-referral-v1 |
| Use Case 5: Payer Claims Adjudication | https://smarthealthit.org/permission-ticket-profile/payer-claims-adjudication-v1 | https://smarthealthit.org/permission-ticket-type/payer-claims-adjudication-v1 |
| Use Case 6: Research Study | https://smarthealthit.org/permission-ticket-profile/research-study-v1 | https://smarthealthit.org/permission-ticket-type/research-study-v1 |
| Use Case 7: Provider-to-Provider Consult | https://smarthealthit.org/permission-ticket-profile/provider-consult-v1 | https://smarthealthit.org/permission-ticket-type/provider-consult-v1 |
When presenting multiple tickets, the client SHALL include the permission_ticket_profile claim in the client_assertion:
{
"permission_ticket_profile": "https://smarthealthit.org/permission-ticket-profile/identity-authorization-v1"
}
permission_ticket_profile selects a multi-ticket composition profile when multiple tickets are presented or when a profile defines cross-ticket combination rules. In single-ticket flows, the Data Holder selects processing rules based on the ticket's ticket_type, and permission_ticket_profile MAY be omitted.
Unknown profile, unknown ticket_type, or profile/type mismatch SHALL be rejected with invalid_grant.
Profile: Identity + Designated Representative
Two tickets are required:
requester identitysubject, access, and a reference linking to the requesterTicket 1 (Identity Provider - e.g., Clear):
{
"iss": "https://clear.me",
"sub": "grant-identity-789",
"aud": "https://tefca.hhs.gov",
"exp": 1735689600,
"ticket_type": "https://smarthealthit.org/permission-ticket-type/identity-v1",
"cnf": {
"jkt": "0ZcOCORZNYy-DWpqq30jZyJGHTN0d2HglBV3uiguA4I"
},
"authorization": {
"requester": {
"resourceType": "RelatedPerson",
"identifier": [
{
"system": "https://clear.me/id",
"value": "CLR-789"
}
],
"name": [
{
"family": "Smith",
"given": [
"Jane"
]
}
]
}
}
}
Ticket 2 (Trusted Issuer):
{
"iss": "https://trusted-issuer.org",
"sub": "grant-authorization-456",
"aud": "https://tefca.hhs.gov",
"exp": 1735689600,
"ticket_type": "https://smarthealthit.org/permission-ticket-type/authorization-v1",
"cnf": {
"jkt": "0ZcOCORZNYy-DWpqq30jZyJGHTN0d2HglBV3uiguA4I"
},
"authorization": {
"subject": {
"type": "match",
"traits": {
"resourceType": "Patient"
}
},
"access": {
"scopes": [
"patient/*.rs"
]
}
},
"details": {
"requesterReference": "https://clear.me/id|CLR-789"
}
}
The Data Holder, implementing this profile:
requesterReference in Ticket 2's details matches the requester.identifier in Ticket 1Note: The
requesterReferencefield used in this multi-ticket example lives in the ticket'sdetailsand is defined by the composition profile, not by the base ticket schema. Multi-ticket profile schemas may define additionaldetailsfields beyond the base model.
Profile: Base + Sensitive Category
A network-level ticket provides baseline access; an additional ticket from a specialized authority grants access to sensitive categories (e.g., behavioral health, substance use).
Regardless of profile, these rules always apply:
cnf, the JWK Thumbprint of the authenticated client_assertion signing key SHALL match that ticket's cnf.jktticket_typepermission_ticket_profile is present, all tickets SHALL match the declared profile rulesexp (expiration) claim| Use Case | Recommended exp |
|---|---|
| Interactive/real-time | 1-4 hours |
| Batch processing | 24 hours |
| Standing authorization | Up to 1 year (with revocation) |
For scenarios requiring access beyond a single session (e.g., ongoing care relationships, research studies), two approaches are supported:
Approach 1: Refresh via Issuer
The client periodically obtains fresh tickets from the issuer. Suitable when:
Approach 2: Long-Lived Tickets with Revocation
The issuer mints a ticket with extended validity (weeks to months) and supports revocation. Suitable when:
Issuers MAY support revocation of individual tickets before expiration. If a ticket includes a revocation claim, it SHALL also include a jti (unique ticket ID).
Revocation Identifier
Tickets supporting revocation include a revocation claim:
{
"iss": "https://trusted-issuer.org",
"sub": "grant-revocable-example",
"aud": "https://tefca.hhs.gov",
"exp": 1735689600,
"ticket_type": "https://smarthealthit.org/permission-ticket-type/network-patient-access-v1",
"cnf": {
"jkt": "0ZcOCORZNYy-DWpqq30jZyJGHTN0d2HglBV3uiguA4I"
},
"jti": "ticket-unique-id",
"revocation": {
"url": "https://trusted-issuer.org/.well-known/crl/patient-access.json",
"rid": "abc123xyz"
},
"authorization": {
"subject": {
"type": "match",
"resourceType": "Patient"
},
"access": {
"scopes": [
"patient/*.rs"
]
}
}
}
| Field | Description |
|---|---|
revocation.url |
URL of the issuer's Credential Revocation List (CRL) for this category of tickets |
revocation.rid |
Revocation identifier for this ticket. SHALL be opaque (not contain PII). |
Generating rid: Issuers SHOULD use a one-way transformation to prevent correlation:
rid = base64url(hmac-sha-256(issuer_secret || kid, ticket_jti)[0:8])
Revocation List Format
The CRL is a JSON file served at the URL specified in the ticket:
{
"kid": "issuer-signing-key-id",
"method": "rid",
"ctr": 42,
"rids": [
"abc123xyz",
"def456uvw.1710460800"
]
}
| Field | Description |
|---|---|
kid |
Key ID used to sign tickets covered by this CRL |
method |
Revocation method identifier. Value "rid" indicates the method defined in this specification. |
ctr |
Monotonic counter incremented on each update. Verifiers use this to detect changes. |
rids |
Array of revoked rid values. Optional .timestamp suffix (Unix seconds) revokes only tickets issued before that time. |
Timestamp Suffix Example:
The entry "def456uvw.1710460800" revokes tickets with rid = def456uvw that were issued (iat) before March 15, 2024 00:00:00 UTC. Tickets with that rid issued after this timestamp remain valid.
Revocation Checking
Issuers:
ctr on every updateData Holders:
revocation is present in ticket, SHALL check the CRLrid appears in the CRL (respecting timestamp suffix if present)Grouping for Privacy
Issuers MAY use multiple CRL URLs to group tickets by category, preventing correlation across ticket types when checking revocation.
Here are seven scenarios demonstrating how FHIR resources are used to model diverse authorization needs. Each use case maps to a single ticket_type.
The table below summarizes required and optional fields for each use case profile:
| Use Case | cnf |
Subject Mode | Requester | Details | Access Dimensions |
|---|---|---|---|---|---|
| UC1: Patient Access | Required | match |
— | — | scopes (required) |
| UC2: Authorized Rep | Required | identifier |
RelatedPerson (required) |
basis, verifiedAt, jurisdiction |
scopes (required) |
| UC3: Public Health | Optional | reference |
Organization (required) |
condition (Coding), case (Reference) |
scopes, periods |
| UC4: Social Care | Optional | reference |
PractitionerRole (required) |
concern (Coding), referral (Reference) |
scopes |
| UC5: Payer Claims | Optional | reference |
Organization (required) |
service (Coding), claim (Reference) |
scopes |
| UC6: Research | Required | identifier |
Organization (required) |
condition (Coding), study (Reference) |
scopes, periods |
| UC7: Provider Consult | Optional | reference |
Practitioner (required) |
reason (Coding), request (Reference) |
scopes |
A patient uses a high-assurance Digital ID wallet to authorize an app to fetch their data from multiple hospitals.
Patient (type=match, matched by demographics: Name, DOB, Identifier).scopes = patient/Immunization.rs, patient/AllergyIntolerance.rs.{
"alg": "ES256",
"kid": "nvOGRCsTz2QIQLsbl0ZQ_ux0tfyh5iave-jvNsANWv8"
}
{
"iss": "https://trusted-issuer.org",
"sub": "grant-uc1-patient-access",
"aud": "https://network.org",
"exp": 1772830901,
"ticket_type": "https://smarthealthit.org/permission-ticket-type/network-patient-access-v1",
"cnf": {
"jkt": "JuI6ibZHcMPQICaIZ55PbXpnsudQmKt00D0BiEXNrMc"
},
"authorization": {
"subject": {
"type": "match",
"traits": {
"resourceType": "Patient",
"name": [
{
"family": "Smith",
"given": [
"John"
]
}
],
"birthDate": "1980-01-01"
}
},
"access": {
"scopes": [
"patient/Immunization.rs",
"patient/AllergyIntolerance.rs"
]
}
},
"iat": 1772827301
}
eyJhbGciOiJFUzI1NiIsImtpZCI6Im52T0dSQ3NUejJRSVFMc2JsMFpRX3V4MHRmeWg1aWF2ZS1qdk5zQU5XdjgifQ.eyJpc3MiOiJodHRwczovL3RydXN0ZWQtaXNzdWVyLm9yZyIsInN1YiI6ImdyYW50LXVjMS1wYXRpZW50LWFjY2VzcyIsImF1ZCI6Imh0dHBzOi8vbmV0d29yay5vcmciLCJleHAiOjE3NzI4MzA5MDEsInRpY2tldF90eXBlIjoiaHR0cHM6Ly9zbWFydGhlYWx0aGl0Lm9yZy9wZXJtaXNzaW9uLXRpY2tldC10eXBlL25ldHdvcmstcGF0aWVudC1hY2Nlc3MtdjEiLCJjbmYiOnsiamt0IjoiSnVJNmliWkhjTVBRSUNhSVo1NVBiWHBuc3VkUW1LdDAwRDBCaUVYTnJNYyJ9LCJhdXRob3JpemF0aW9uIjp7InN1YmplY3QiOnsidHlwZSI6Im1hdGNoIiwidHJhaXRzIjp7InJlc291cmNlVHlwZSI6IlBhdGllbnQiLCJuYW1lIjpbeyJmYW1pbHkiOiJTbWl0aCIsImdpdmVuIjpbIkpvaG4iXX1dLCJiaXJ0aERhdGUiOiIxOTgwLTAxLTAxIn19LCJhY2Nlc3MiOnsic2NvcGVzIjpbInBhdGllbnQvSW1tdW5pemF0aW9uLnJzIiwicGF0aWVudC9BbGxlcmd5SW50b2xlcmFuY2UucnMiXX19LCJpYXQiOjE3NzI4MjczMDF9.dBJbKDUDCNOoQkYjEyHwfUPu5hofgIPvJ2NgH1H5w3EF7_jXayVwgazxRfSn4zlkfmAk6OhppOzdWIhiEMa_sA
An adult daughter accesses her elderly mother's records. The relationship is verified by a Trusted Issuer, not the Hospital.
Patient (type=identifier, matched by MPI identifier).RelatedPerson (Name, Telecom, Relationship Code).basis = patient-designated, verifiedAt, jurisdiction (optional).scopes = patient/*.rs.{
"alg": "ES256",
"kid": "nvOGRCsTz2QIQLsbl0ZQ_ux0tfyh5iave-jvNsANWv8"
}
{
"iss": "https://trusted-issuer.org",
"sub": "grant-uc2-representative",
"aud": "https://network.org",
"exp": 1772830901,
"ticket_type": "https://smarthealthit.org/permission-ticket-type/authorized-representative-v1",
"cnf": {
"jkt": "JuI6ibZHcMPQICaIZ55PbXpnsudQmKt00D0BiEXNrMc"
},
"authorization": {
"subject": {
"type": "identifier",
"resourceType": "Patient",
"identifier": [
{
"system": "https://national-mpi.net",
"value": "pt-555"
}
]
},
"requester": {
"resourceType": "RelatedPerson",
"name": [
{
"family": "Doe",
"given": [
"Jane"
]
}
],
"telecom": [
{
"system": "email",
"value": "jane.doe@example.com"
}
],
"relationship": [
{
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/v3-RoleCode",
"code": "DAU",
"display": "Daughter"
}
]
}
]
},
"access": {
"scopes": [
"patient/*.rs"
]
}
},
"details": {
"basis": "patient-designated",
"verifiedAt": "2026-03-06T15:04:05Z",
"jurisdiction": [
{
"state": "IL"
}
]
},
"iat": 1772827301
}
eyJhbGciOiJFUzI1NiIsImtpZCI6Im52T0dSQ3NUejJRSVFMc2JsMFpRX3V4MHRmeWg1aWF2ZS1qdk5zQU5XdjgifQ.eyJpc3MiOiJodHRwczovL3RydXN0ZWQtaXNzdWVyLm9yZyIsInN1YiI6ImdyYW50LXVjMi1yZXByZXNlbnRhdGl2ZSIsImF1ZCI6Imh0dHBzOi8vbmV0d29yay5vcmciLCJleHAiOjE3NzI4MzA5MDEsInRpY2tldF90eXBlIjoiaHR0cHM6Ly9zbWFydGhlYWx0aGl0Lm9yZy9wZXJtaXNzaW9uLXRpY2tldC10eXBlL2F1dGhvcml6ZWQtcmVwcmVzZW50YXRpdmUtdjEiLCJjbmYiOnsiamt0IjoiSnVJNmliWkhjTVBRSUNhSVo1NVBiWHBuc3VkUW1LdDAwRDBCaUVYTnJNYyJ9LCJhdXRob3JpemF0aW9uIjp7InN1YmplY3QiOnsidHlwZSI6ImlkZW50aWZpZXIiLCJyZXNvdXJjZVR5cGUiOiJQYXRpZW50IiwiaWRlbnRpZmllciI6W3sic3lzdGVtIjoiaHR0cHM6Ly9uYXRpb25hbC1tcGkubmV0IiwidmFsdWUiOiJwdC01NTUifV19LCJyZXF1ZXN0ZXIiOnsicmVzb3VyY2VUeXBlIjoiUmVsYXRlZFBlcnNvbiIsIm5hbWUiOlt7ImZhbWlseSI6IkRvZSIsImdpdmVuIjpbIkphbmUiXX1dLCJ0ZWxlY29tIjpbeyJzeXN0ZW0iOiJlbWFpbCIsInZhbHVlIjoiamFuZS5kb2VAZXhhbXBsZS5jb20ifV0sInJlbGF0aW9uc2hpcCI6W3siY29kaW5nIjpbeyJzeXN0ZW0iOiJodHRwOi8vdGVybWlub2xvZ3kuaGw3Lm9yZy9Db2RlU3lzdGVtL3YzLVJvbGVDb2RlIiwiY29kZSI6IkRBVSIsImRpc3BsYXkiOiJEYXVnaHRlciJ9XX1dfSwiYWNjZXNzIjp7InNjb3BlcyI6WyJwYXRpZW50LyoucnMiXX19LCJkZXRhaWxzIjp7ImJhc2lzIjoicGF0aWVudC1kZXNpZ25hdGVkIiwidmVyaWZpZWRBdCI6IjIwMjYtMDMtMDZUMTU6MDQ6MDVaIiwianVyaXNkaWN0aW9uIjpbeyJzdGF0ZSI6IklMIn1dfSwiaWF0IjoxNzcyODI3MzAxfQ.ptqDxRpHGpT15zGKSF5j8_DX5don_2jNKaV6G8L7TeE3PvQ1ZYJ3GZalBPDqxkvZGQLxKGHkmPrpOz9sazVKcg
A Hospital creates a Case Report. The Public Health Agency (PHA) uses the report as a ticket to query for follow-up data.
Patient (type=reference, by local resource ID).Organization (Name, Identifier, Type).condition = Tuberculosis (SCT 56717001), case = Reference (by identifier to PHA case).scopes = patient/*.rs, periods (Start Date).{
"alg": "ES256",
"kid": "nvOGRCsTz2QIQLsbl0ZQ_ux0tfyh5iave-jvNsANWv8"
}
{
"iss": "https://hospital-a.com",
"sub": "grant-uc3-pubhealth-case999",
"aud": "https://hospital-a.com",
"exp": 1772830901,
"ticket_type": "https://smarthealthit.org/permission-ticket-type/public-health-investigation-v1",
"authorization": {
"subject": {
"type": "reference",
"resourceType": "Patient",
"id": "local-patient-123"
},
"requester": {
"resourceType": "Organization",
"name": "State Dept of Health",
"identifier": [
{
"system": "urn:ietf:rfc:3986",
"value": "https://doh.state.gov"
}
],
"type": [
{
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/organization-type",
"code": "govt"
}
]
}
]
},
"access": {
"scopes": [
"patient/*.rs"
],
"periods": [
{
"start": "2025-01-01",
"end": "2026-01-01"
}
]
}
},
"details": {
"condition": {
"system": "http://snomed.info/sct",
"code": "56717001",
"display": "Tuberculosis"
},
"case": {
"identifier": {
"system": "https://doh.wa.gov/cases",
"value": "CASE-2024-999"
},
"display": "TB investigation, Case 2024-999"
}
},
"iat": 1772827301
}
eyJhbGciOiJFUzI1NiIsImtpZCI6Im52T0dSQ3NUejJRSVFMc2JsMFpRX3V4MHRmeWg1aWF2ZS1qdk5zQU5XdjgifQ.eyJpc3MiOiJodHRwczovL2hvc3BpdGFsLWEuY29tIiwic3ViIjoiZ3JhbnQtdWMzLXB1YmhlYWx0aC1jYXNlOTk5IiwiYXVkIjoiaHR0cHM6Ly9ob3NwaXRhbC1hLmNvbSIsImV4cCI6MTc3MjgzMDkwMSwidGlja2V0X3R5cGUiOiJodHRwczovL3NtYXJ0aGVhbHRoaXQub3JnL3Blcm1pc3Npb24tdGlja2V0LXR5cGUvcHVibGljLWhlYWx0aC1pbnZlc3RpZ2F0aW9uLXYxIiwiYXV0aG9yaXphdGlvbiI6eyJzdWJqZWN0Ijp7InR5cGUiOiJyZWZlcmVuY2UiLCJyZXNvdXJjZVR5cGUiOiJQYXRpZW50IiwiaWQiOiJsb2NhbC1wYXRpZW50LTEyMyJ9LCJyZXF1ZXN0ZXIiOnsicmVzb3VyY2VUeXBlIjoiT3JnYW5pemF0aW9uIiwibmFtZSI6IlN0YXRlIERlcHQgb2YgSGVhbHRoIiwiaWRlbnRpZmllciI6W3sic3lzdGVtIjoidXJuOmlldGY6cmZjOjM5ODYiLCJ2YWx1ZSI6Imh0dHBzOi8vZG9oLnN0YXRlLmdvdiJ9XSwidHlwZSI6W3siY29kaW5nIjpbeyJzeXN0ZW0iOiJodHRwOi8vdGVybWlub2xvZ3kuaGw3Lm9yZy9Db2RlU3lzdGVtL29yZ2FuaXphdGlvbi10eXBlIiwiY29kZSI6ImdvdnQifV19XX0sImFjY2VzcyI6eyJzY29wZXMiOlsicGF0aWVudC8qLnJzIl0sInBlcmlvZHMiOlt7InN0YXJ0IjoiMjAyNS0wMS0wMSIsImVuZCI6IjIwMjYtMDEtMDEifV19fSwiZGV0YWlscyI6eyJjb25kaXRpb24iOnsic3lzdGVtIjoiaHR0cDovL3Nub21lZC5pbmZvL3NjdCIsImNvZGUiOiI1NjcxNzAwMSIsImRpc3BsYXkiOiJUdWJlcmN1bG9zaXMifSwiY2FzZSI6eyJpZGVudGlmaWVyIjp7InN5c3RlbSI6Imh0dHBzOi8vZG9oLndhLmdvdi9jYXNlcyIsInZhbHVlIjoiQ0FTRS0yMDI0LTk5OSJ9LCJkaXNwbGF5IjoiVEIgaW52ZXN0aWdhdGlvbiwgQ2FzZSAyMDI0LTk5OSJ9fSwiaWF0IjoxNzcyODI3MzAxfQ.mfz7DOJymeQSbmJdgoFIazIq7yANNM6PzZycjWpNM85P9nU78u8-b_tY0ulae26ZLyHMErZ5ydZlxloC-i8jXA
A transactional/ad-hoc user. A Food Bank volunteer needs to update a referral status. She does not have an NPI or a user account.
Patient (type=reference, by resource reference).PractitionerRole (Contained Practitioner + Organization).concern = Food insecurity (SCT 733423003), referral = Reference to local ServiceRequest.scopes = patient/ServiceRequest.rsu, patient/Task.rsu.{
"alg": "ES256",
"kid": "nvOGRCsTz2QIQLsbl0ZQ_ux0tfyh5iave-jvNsANWv8"
}
{
"iss": "https://referring-ehr.org",
"sub": "grant-uc4-referral-555",
"aud": "https://referring-ehr.org",
"exp": 1772830901,
"ticket_type": "https://smarthealthit.org/permission-ticket-type/social-care-referral-v1",
"authorization": {
"subject": {
"type": "reference",
"resourceType": "Patient",
"reference": "Patient/123"
},
"requester": {
"resourceType": "PractitionerRole",
"contained": [
{
"resourceType": "Practitioner",
"id": "p1",
"name": [
{
"family": "Volunteer",
"given": [
"Alice"
]
}
],
"telecom": [
{
"system": "email",
"value": "alice@foodbank.org"
}
]
},
{
"resourceType": "Organization",
"id": "o1",
"name": "Downtown Food Bank"
}
],
"practitioner": {
"reference": "#p1"
},
"organization": {
"reference": "#o1"
}
},
"access": {
"scopes": [
"patient/ServiceRequest.rsu",
"patient/Task.rsu"
]
}
},
"details": {
"concern": {
"system": "http://snomed.info/sct",
"code": "733423003",
"display": "Food insecurity"
},
"referral": {
"reference": "ServiceRequest/555",
"identifier": {
"system": "https://referring-ehr.org/referrals",
"value": "REF-555"
},
"display": "Food insecurity referral"
}
},
"iat": 1772827301
}
eyJhbGciOiJFUzI1NiIsImtpZCI6Im52T0dSQ3NUejJRSVFMc2JsMFpRX3V4MHRmeWg1aWF2ZS1qdk5zQU5XdjgifQ.eyJpc3MiOiJodHRwczovL3JlZmVycmluZy1laHIub3JnIiwic3ViIjoiZ3JhbnQtdWM0LXJlZmVycmFsLTU1NSIsImF1ZCI6Imh0dHBzOi8vcmVmZXJyaW5nLWVoci5vcmciLCJleHAiOjE3NzI4MzA5MDEsInRpY2tldF90eXBlIjoiaHR0cHM6Ly9zbWFydGhlYWx0aGl0Lm9yZy9wZXJtaXNzaW9uLXRpY2tldC10eXBlL3NvY2lhbC1jYXJlLXJlZmVycmFsLXYxIiwiYXV0aG9yaXphdGlvbiI6eyJzdWJqZWN0Ijp7InR5cGUiOiJyZWZlcmVuY2UiLCJyZXNvdXJjZVR5cGUiOiJQYXRpZW50IiwicmVmZXJlbmNlIjoiUGF0aWVudC8xMjMifSwicmVxdWVzdGVyIjp7InJlc291cmNlVHlwZSI6IlByYWN0aXRpb25lclJvbGUiLCJjb250YWluZWQiOlt7InJlc291cmNlVHlwZSI6IlByYWN0aXRpb25lciIsImlkIjoicDEiLCJuYW1lIjpbeyJmYW1pbHkiOiJWb2x1bnRlZXIiLCJnaXZlbiI6WyJBbGljZSJdfV0sInRlbGVjb20iOlt7InN5c3RlbSI6ImVtYWlsIiwidmFsdWUiOiJhbGljZUBmb29kYmFuay5vcmcifV19LHsicmVzb3VyY2VUeXBlIjoiT3JnYW5pemF0aW9uIiwiaWQiOiJvMSIsIm5hbWUiOiJEb3dudG93biBGb29kIEJhbmsifV0sInByYWN0aXRpb25lciI6eyJyZWZlcmVuY2UiOiIjcDEifSwib3JnYW5pemF0aW9uIjp7InJlZmVyZW5jZSI6IiNvMSJ9fSwiYWNjZXNzIjp7InNjb3BlcyI6WyJwYXRpZW50L1NlcnZpY2VSZXF1ZXN0LnJzdSIsInBhdGllbnQvVGFzay5yc3UiXX19LCJkZXRhaWxzIjp7ImNvbmNlcm4iOnsic3lzdGVtIjoiaHR0cDovL3Nub21lZC5pbmZvL3NjdCIsImNvZGUiOiI3MzM0MjMwMDMiLCJkaXNwbGF5IjoiRm9vZCBpbnNlY3VyaXR5In0sInJlZmVycmFsIjp7InJlZmVyZW5jZSI6IlNlcnZpY2VSZXF1ZXN0LzU1NSIsImlkZW50aWZpZXIiOnsic3lzdGVtIjoiaHR0cHM6Ly9yZWZlcnJpbmctZWhyLm9yZy9yZWZlcnJhbHMiLCJ2YWx1ZSI6IlJFRi01NTUifSwiZGlzcGxheSI6IkZvb2QgaW5zZWN1cml0eSByZWZlcnJhbCJ9fSwiaWF0IjoxNzcyODI3MzAxfQ.2-a46_72EXhOIGnQaxqvysUFY2CcXseuVMA6UWro9c9KxqXu_uz3ezUozpc1-sEBAawlFSJ7RSriE-fdIRHWgg
A Payer requests clinical documents to support a specific claim.
Patient (type=reference, by resource reference).Organization (Payer NPI).service = Appendectomy (SCT 80146002), claim = Reference (by identifier to payer claim).scopes = patient/DocumentReference.rs, patient/Procedure.rs.{
"alg": "ES256",
"kid": "nvOGRCsTz2QIQLsbl0ZQ_ux0tfyh5iave-jvNsANWv8"
}
{
"iss": "https://provider.com",
"sub": "grant-uc5-claim-xyz",
"aud": "https://provider.com",
"exp": 1772830901,
"ticket_type": "https://smarthealthit.org/permission-ticket-type/payer-claims-adjudication-v1",
"authorization": {
"subject": {
"type": "reference",
"resourceType": "Patient",
"reference": "Patient/456"
},
"requester": {
"resourceType": "Organization",
"identifier": [
{
"system": "http://hl7.org/fhir/sid/us-npi",
"value": "9876543210"
}
],
"name": "Blue Payer Inc"
},
"access": {
"scopes": [
"patient/DocumentReference.rs",
"patient/Procedure.rs"
]
}
},
"details": {
"service": {
"system": "http://snomed.info/sct",
"code": "80146002",
"display": "Appendectomy"
},
"claim": {
"identifier": {
"system": "http://payer.com/claims",
"value": "CLAIM-2024-XYZ"
},
"display": "Appendectomy claim"
}
},
"iat": 1772827301
}
eyJhbGciOiJFUzI1NiIsImtpZCI6Im52T0dSQ3NUejJRSVFMc2JsMFpRX3V4MHRmeWg1aWF2ZS1qdk5zQU5XdjgifQ.eyJpc3MiOiJodHRwczovL3Byb3ZpZGVyLmNvbSIsInN1YiI6ImdyYW50LXVjNS1jbGFpbS14eXoiLCJhdWQiOiJodHRwczovL3Byb3ZpZGVyLmNvbSIsImV4cCI6MTc3MjgzMDkwMSwidGlja2V0X3R5cGUiOiJodHRwczovL3NtYXJ0aGVhbHRoaXQub3JnL3Blcm1pc3Npb24tdGlja2V0LXR5cGUvcGF5ZXItY2xhaW1zLWFkanVkaWNhdGlvbi12MSIsImF1dGhvcml6YXRpb24iOnsic3ViamVjdCI6eyJ0eXBlIjoicmVmZXJlbmNlIiwicmVzb3VyY2VUeXBlIjoiUGF0aWVudCIsInJlZmVyZW5jZSI6IlBhdGllbnQvNDU2In0sInJlcXVlc3RlciI6eyJyZXNvdXJjZVR5cGUiOiJPcmdhbml6YXRpb24iLCJpZGVudGlmaWVyIjpbeyJzeXN0ZW0iOiJodHRwOi8vaGw3Lm9yZy9maGlyL3NpZC91cy1ucGkiLCJ2YWx1ZSI6Ijk4NzY1NDMyMTAifV0sIm5hbWUiOiJCbHVlIFBheWVyIEluYyJ9LCJhY2Nlc3MiOnsic2NvcGVzIjpbInBhdGllbnQvRG9jdW1lbnRSZWZlcmVuY2UucnMiLCJwYXRpZW50L1Byb2NlZHVyZS5ycyJdfX0sImRldGFpbHMiOnsic2VydmljZSI6eyJzeXN0ZW0iOiJodHRwOi8vc25vbWVkLmluZm8vc2N0IiwiY29kZSI6IjgwMTQ2MDAyIiwiZGlzcGxheSI6IkFwcGVuZGVjdG9teSJ9LCJjbGFpbSI6eyJpZGVudGlmaWVyIjp7InN5c3RlbSI6Imh0dHA6Ly9wYXllci5jb20vY2xhaW1zIiwidmFsdWUiOiJDTEFJTS0yMDI0LVhZWiJ9LCJkaXNwbGF5IjoiQXBwZW5kZWN0b215IGNsYWltIn19LCJpYXQiOjE3NzI4MjczMDF9.fCUysSgWy3zd6L0hp4qK78aXCHGR7Yy4Wexw3TJl7IQtrJmw1P78XsjSJYCxRwlGgwYImyADXXtz4_n2ML4xxw
A patient consents to a study. The ticket proves consent exists without requiring the researcher to be a "user" at the hospital.
Patient (type=identifier, by MRN).Organization (Research Institute ID).condition = Malignant tumor of lung (SCT 363358000), study = Reference (by identifier to research study).scopes = patient/*.rs, periods (Start/End Date).{
"alg": "ES256",
"kid": "nvOGRCsTz2QIQLsbl0ZQ_ux0tfyh5iave-jvNsANWv8"
}
{
"iss": "https://consent-platform.org",
"sub": "grant-uc6-study-proto22",
"aud": "https://hospital.com",
"exp": 1772830901,
"ticket_type": "https://smarthealthit.org/permission-ticket-type/research-study-v1",
"cnf": {
"jkt": "JuI6ibZHcMPQICaIZ55PbXpnsudQmKt00D0BiEXNrMc"
},
"authorization": {
"subject": {
"type": "identifier",
"resourceType": "Patient",
"identifier": [
{
"value": "MRN-123"
}
]
},
"requester": {
"resourceType": "Organization",
"name": "Oncology Research Institute",
"identifier": [
{
"value": "research-org-id"
}
]
},
"access": {
"scopes": [
"patient/*.rs"
],
"periods": [
{
"start": "2020-01-01",
"end": "2025-01-01"
}
]
}
},
"details": {
"condition": {
"system": "http://snomed.info/sct",
"code": "363358000",
"display": "Malignant tumor of lung"
},
"study": {
"identifier": {
"system": "https://clinicaltrials.gov",
"value": "NCT-12345"
},
"display": "Lung cancer immunotherapy trial"
}
},
"iat": 1772827301
}
eyJhbGciOiJFUzI1NiIsImtpZCI6Im52T0dSQ3NUejJRSVFMc2JsMFpRX3V4MHRmeWg1aWF2ZS1qdk5zQU5XdjgifQ.eyJpc3MiOiJodHRwczovL2NvbnNlbnQtcGxhdGZvcm0ub3JnIiwic3ViIjoiZ3JhbnQtdWM2LXN0dWR5LXByb3RvMjIiLCJhdWQiOiJodHRwczovL2hvc3BpdGFsLmNvbSIsImV4cCI6MTc3MjgzMDkwMSwidGlja2V0X3R5cGUiOiJodHRwczovL3NtYXJ0aGVhbHRoaXQub3JnL3Blcm1pc3Npb24tdGlja2V0LXR5cGUvcmVzZWFyY2gtc3R1ZHktdjEiLCJjbmYiOnsiamt0IjoiSnVJNmliWkhjTVBRSUNhSVo1NVBiWHBuc3VkUW1LdDAwRDBCaUVYTnJNYyJ9LCJhdXRob3JpemF0aW9uIjp7InN1YmplY3QiOnsidHlwZSI6ImlkZW50aWZpZXIiLCJyZXNvdXJjZVR5cGUiOiJQYXRpZW50IiwiaWRlbnRpZmllciI6W3sidmFsdWUiOiJNUk4tMTIzIn1dfSwicmVxdWVzdGVyIjp7InJlc291cmNlVHlwZSI6Ik9yZ2FuaXphdGlvbiIsIm5hbWUiOiJPbmNvbG9neSBSZXNlYXJjaCBJbnN0aXR1dGUiLCJpZGVudGlmaWVyIjpbeyJ2YWx1ZSI6InJlc2VhcmNoLW9yZy1pZCJ9XX0sImFjY2VzcyI6eyJzY29wZXMiOlsicGF0aWVudC8qLnJzIl0sInBlcmlvZHMiOlt7InN0YXJ0IjoiMjAyMC0wMS0wMSIsImVuZCI6IjIwMjUtMDEtMDEifV19fSwiZGV0YWlscyI6eyJjb25kaXRpb24iOnsic3lzdGVtIjoiaHR0cDovL3Nub21lZC5pbmZvL3NjdCIsImNvZGUiOiIzNjMzNTgwMDAiLCJkaXNwbGF5IjoiTWFsaWduYW50IHR1bW9yIG9mIGx1bmcifSwic3R1ZHkiOnsiaWRlbnRpZmllciI6eyJzeXN0ZW0iOiJodHRwczovL2NsaW5pY2FsdHJpYWxzLmdvdiIsInZhbHVlIjoiTkNULTEyMzQ1In0sImRpc3BsYXkiOiJMdW5nIGNhbmNlciBpbW11bm90aGVyYXB5IHRyaWFsIn19LCJpYXQiOjE3NzI4MjczMDF9.Za-uCyPGlEYMbBMzttIEu6hgRR_FlBHuJRUZSbTji202uRRma63JC4wqtZdedv77suq8Tp-JhUVSYwW_FtIcFg
A Specialist (Practitioner) requests data from a Referring Provider.
Patient (type=reference, by resource reference).Practitioner (NPI).reason = Atrial fibrillation (SCT 49436004), request = Reference to local ServiceRequest.scopes = patient/*.rs.{
"alg": "ES256",
"kid": "nvOGRCsTz2QIQLsbl0ZQ_ux0tfyh5iave-jvNsANWv8"
}
{
"iss": "https://referring-ehr.org",
"sub": "grant-uc7-consult-req111",
"aud": "https://referring-ehr.org",
"exp": 1772830901,
"ticket_type": "https://smarthealthit.org/permission-ticket-type/provider-consult-v1",
"authorization": {
"subject": {
"type": "reference",
"resourceType": "Patient",
"reference": "Patient/999"
},
"requester": {
"resourceType": "Practitioner",
"identifier": [
{
"system": "http://hl7.org/fhir/sid/us-npi",
"value": "1112223333"
}
],
"name": [
{
"family": "Heart",
"given": [
"A."
]
}
]
},
"access": {
"scopes": [
"patient/*.rs"
]
}
},
"details": {
"reason": {
"system": "http://snomed.info/sct",
"code": "49436004",
"display": "Atrial fibrillation"
},
"request": {
"reference": "ServiceRequest/ref-req-111",
"identifier": {
"system": "https://referring-ehr.org/requests",
"value": "ref-req-111"
},
"display": "Cardiology consult for atrial fibrillation"
}
},
"iat": 1772827301
}
eyJhbGciOiJFUzI1NiIsImtpZCI6Im52T0dSQ3NUejJRSVFMc2JsMFpRX3V4MHRmeWg1aWF2ZS1qdk5zQU5XdjgifQ.eyJpc3MiOiJodHRwczovL3JlZmVycmluZy1laHIub3JnIiwic3ViIjoiZ3JhbnQtdWM3LWNvbnN1bHQtcmVxMTExIiwiYXVkIjoiaHR0cHM6Ly9yZWZlcnJpbmctZWhyLm9yZyIsImV4cCI6MTc3MjgzMDkwMSwidGlja2V0X3R5cGUiOiJodHRwczovL3NtYXJ0aGVhbHRoaXQub3JnL3Blcm1pc3Npb24tdGlja2V0LXR5cGUvcHJvdmlkZXItY29uc3VsdC12MSIsImF1dGhvcml6YXRpb24iOnsic3ViamVjdCI6eyJ0eXBlIjoicmVmZXJlbmNlIiwicmVzb3VyY2VUeXBlIjoiUGF0aWVudCIsInJlZmVyZW5jZSI6IlBhdGllbnQvOTk5In0sInJlcXVlc3RlciI6eyJyZXNvdXJjZVR5cGUiOiJQcmFjdGl0aW9uZXIiLCJpZGVudGlmaWVyIjpbeyJzeXN0ZW0iOiJodHRwOi8vaGw3Lm9yZy9maGlyL3NpZC91cy1ucGkiLCJ2YWx1ZSI6IjExMTIyMjMzMzMifV0sIm5hbWUiOlt7ImZhbWlseSI6IkhlYXJ0IiwiZ2l2ZW4iOlsiQS4iXX1dfSwiYWNjZXNzIjp7InNjb3BlcyI6WyJwYXRpZW50LyoucnMiXX19LCJkZXRhaWxzIjp7InJlYXNvbiI6eyJzeXN0ZW0iOiJodHRwOi8vc25vbWVkLmluZm8vc2N0IiwiY29kZSI6IjQ5NDM2MDA0IiwiZGlzcGxheSI6IkF0cmlhbCBmaWJyaWxsYXRpb24ifSwicmVxdWVzdCI6eyJyZWZlcmVuY2UiOiJTZXJ2aWNlUmVxdWVzdC9yZWYtcmVxLTExMSIsImlkZW50aWZpZXIiOnsic3lzdGVtIjoiaHR0cHM6Ly9yZWZlcnJpbmctZWhyLm9yZy9yZXF1ZXN0cyIsInZhbHVlIjoicmVmLXJlcS0xMTEifSwiZGlzcGxheSI6IkNhcmRpb2xvZ3kgY29uc3VsdCBmb3IgYXRyaWFsIGZpYnJpbGxhdGlvbiJ9fSwiaWF0IjoxNzcyODI3MzAxfQ.HNLSRrrfNAlilHoEIqMRI3-dqQ1zIZGEBxJQw-CwAvivz19lgp2TUPYwWTHS395ppIpFFT2IsFD1eXKdrBozZA
The following TypeScript interfaces define the structure of the Permission Ticket and the Client Assertion.
export interface PermissionTicket {
iss: string; // Issuer URL (Trusted Issuer)
sub: string; // Issuer-defined subject of the authorization grant (issuer-local, not a cross-party client identifier)
aud: string | string[]; // Audience: recipient URL(s) or network / trust framework identifier
exp: number; // Expiration Timestamp
ticket_type: string; // Ticket type URI identifying the ticket schema and processing rules
cnf?: {
jkt: string; // JWK Thumbprint (RFC 7638) of the authorized client key
};
iat?: number; // Issued-at timestamp
jti?: string; // Unique Ticket ID
revocation?: {
url: string; // CRL URL
rid: string; // Revocation ID
};
authorization: {
subject: {
type: "match" | "identifier" | "reference"; // Subject resolution mode
resourceType?: string;
id?: string;
identifier?: any[];
traits?: {
resourceType: "Patient";
name?: { family?: string; given?: string[] }[];
birthDate?: string;
identifier?: any[];
};
reference?: string;
};
access: {
scopes?: string[];
periods?: { start?: string; end?: string; }[];
jurisdictions?: Address[];
organizations?: Organization[];
};
requester?: {
resourceType: "PractitionerRole" | "RelatedPerson" | "Organization" | "Practitioner";
name?: any;
identifier?: any[];
telecom?: any[];
type?: any[];
relationship?: any[];
contained?: any[];
practitioner?: { reference: string };
organization?: { reference: string };
};
};
details?: Record<string, any>; // Schema defined by ticket_type
}
export interface ClientAssertion {
iss: string; // Client ID
sub: string; // Client ID
aud: string; // Token Endpoint URL
jti: string; // Unique Assertion ID
iat?: number; // Issued-at Timestamp
exp?: number; // Expiration Timestamp
permission_ticket_profile?: string; // Composition profile (required for multi-ticket; optional for single-ticket)
permission_tickets: string[];
}
alg and kid (Key ID) to facilitate key rotation.PermissionTicket. Public keys SHALL be exposed via a JWK Set URL (e.g., https://trusted-issuer.org/.well-known/jwks.json).ClientAssertion. Public keys SHALL be registered with the Data Holder or exposed via JWKS.PermissionTicket.cnf.jkt binds redemption to a specific client key via its JWK Thumbprint (RFC 7638). Data Holders compute the thumbprint of the client_assertion signing key and verify it matches. When cnf is absent, aud + client authentication provide the trust boundary.When ticket validation fails, the Data Holder SHALL return an OAuth 2.0 error response per RFC 6749.
| Scenario | error |
error_description |
|---|---|---|
| No tickets in assertion | invalid_request |
"No permission tickets provided" |
| Multiple tickets without profile | invalid_request |
"Missing permission ticket profile for multi-ticket request" |
| Malformed ticket (not valid JWT) | invalid_grant |
"Malformed permission ticket" |
Missing ticket_type |
invalid_grant |
"Missing ticket type" |
| Ticket signature invalid | invalid_grant |
"Ticket signature verification failed" |
| Issuer not trusted | invalid_grant |
"Ticket issuer not trusted: {iss}" |
| Issuer JWKS unavailable | invalid_grant |
"Unable to retrieve issuer keys" |
| Ticket expired | invalid_grant |
"Ticket expired" |
Client key binding mismatch (cnf present) |
invalid_grant |
"Ticket not bound to client key" |
aud mismatch |
invalid_grant |
"Ticket not valid for this server" |
Unknown ticket_type |
invalid_grant |
"Unsupported ticket type" |
| Profile/ticket type mismatch | invalid_grant |
"Ticket type not valid for profile" |
| Subject not resolvable | invalid_grant |
"Unable to resolve ticket subject" |
| Ambiguous subject match | invalid_grant |
"Ambiguous ticket subject match" |
| Subject type / field mismatch | invalid_grant |
"Subject type inconsistent with populated fields" |
| Ticket revoked | invalid_grant |
"Ticket has been revoked" |
Revocable ticket missing jti |
invalid_grant |
"Revocable ticket missing jti" |
| Unsupported constraint | invalid_grant |
"Unsupported access constraint: {field}" |
| No valid scopes after intersection | invalid_scope |
"No authorized scopes" |
This section defines requirements using RFC 2119 keywords (SHALL, SHOULD, MAY).
SHALL:
permission_tickets claim in client assertionsticket_type, aud, and exp; if cnf is present, verify cnf.jkt bindingticket_type is recognized and select processing rules accordinglypermission_ticket_profilepermission_ticket_profile is present, validate all tickets match the declared profile rulestype) and ensure populated fields are consistent with the declared modeaccess constraints (scopes, periods, jurisdictions, organizations) or reject with invalid_grantrevocation is present, verify jti is also present; perform revocation checking before issuing a token; if revocation status cannot be determined, reject the requestSHOULD:
authorization.requester and details for audit trailMAY:
SHALL:
permission_tickets claimpermission_ticket_profile when presenting more than one ticketiss and sub in client assertion (the Client ID URL)MAY:
permission_ticket_profile when presenting exactly one ticketSHOULD:
patient/Observation.rs)jti in client assertion for replay protectionSHALL:
{iss}/.well-known/jwks.jsoniss, sub, aud, exp, ticket_type, authorizationcnf, bind the ticket to a specific client key via cnf.jkt (JWK Thumbprint)revocation is present, include jti and publish CRL at the URL specified in ticketsSHOULD:
jti for unique ticket identificationrid values that do not leak PII