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

## Proposal 002: OpenID Federation Issuers

Status: Draft for discussion | Author: Josh Mandel | Date: April 15, 2026

### Summary

This proposal details how Permission Ticket issuers can participate in OpenID Federation 1.0 to publish their ticket-signing keys, and how Data Holders consume that publication when verifying tickets. It builds on the issuer key publication requirements in the main specification.

### Issuer Leaf Entity Configuration

An issuer that participates in OpenID Federation SHALL publish its leaf entity configuration at:

    ${iss}/.well-known/openid-federation

The leaf entity statement's `iss` and `sub` SHALL equal the `PermissionTicket`'s `iss` claim. This is the structural binding that ties the `PermissionTicket` to its OpenID Federation identity. A Data Holder that has verified the federation chain can derive the issuer's identity from the leaf entity ID itself; no separate `iss → entity` map is required.

### `smart_permission_ticket_issuer` Metadata Type

The leaf entity statement SHALL include the custom OpenID Federation metadata type `smart_permission_ticket_issuer` in its `metadata` object. The identifier is a project-defined bare snake_case OpenID Federation metadata type.

The `smart_permission_ticket_issuer` metadata object SHALL contain:

- `jwks`: an inline JWK Set carrying the public keys that sign `PermissionTicket` JWTs.

The `jwks` field is inline rather than a URI reference so that the federation chain itself cryptographically attests to the ticket-signing keys. A verifier that has walked the federation chain to a configured trust anchor does not need an additional HTTP fetch at ticket-verification time.

### Two Key Sets, Two Roles

A leaf entity statement carries two distinct JWK sets:

- Top-level `jwks` of the entity statement is the federation-signing key set. It is used to validate the entity statement itself as part of trust-chain verification.
- `metadata.smart_permission_ticket_issuer.jwks` is the ticket-signing key set. It is used to verify `PermissionTicket` signatures after the federation chain validates.

These two key sets are independent and rotate independently. A Data Holder SHALL NOT treat the leaf entity statement's top-level `jwks` as the ticket-signing key set.

### Verifier Pipeline

When a Data Holder receives a `PermissionTicket` whose `iss` is to be verified through OpenID Federation:

1. Fetch `${iss}/.well-known/openid-federation` to obtain the leaf entity configuration.
2. Discover and verify a trust chain from the leaf to a configured trust anchor.
3. Apply metadata policy.
4. Confirm `verified_chain.leaf.entity_id === permission_ticket.iss`.
5. Read the ticket-signing keys from `metadata.smart_permission_ticket_issuer.jwks`.
6. Verify the `PermissionTicket` signature against those keys.

The Data Holder needs only its configured trust anchors. No per-issuer pre-configuration is required.

### Relationship to Direct JWKS Publication

Every `PermissionTicket` issuer SHALL publish its verification keys as a JWK Set at `${iss}/.well-known/jwks.json` regardless of whether it also participates in OpenID Federation. See Issuer Key Publication in the main specification.

When an issuer participates in OpenID Federation, the OpenID Federation publication is the federation-discovery counterpart to the direct JWKS publication. An issuer that participates in OpenID Federation SHOULD publish the same ticket-signing key set through both paths so that any Data Holder can resolve its keys regardless of which trust path the holder is configured for.

### Example Leaf Entity Statement Payload

    {
      "iss": "https://issuer.example/issuer/example",
      "sub": "https://issuer.example/issuer/example",
      "jwks": {
        "keys": [
          {
            "kid": "fed-2026-04",
            "kty": "EC",
            "crv": "P-256",
            "x": "...",
            "y": "..."
          }
        ]
      },
      "metadata": {
        "federation_entity": {
          "organization_name": "Example Health Issuer"
        },
        "smart_permission_ticket_issuer": {
          "jwks": {
            "keys": [
              {
                "kid": "tickets-2026-04",
                "kty": "EC",
                "crv": "P-256",
                "x": "...",
                "y": "..."
              }
            ]
          }
        }
      }
    }

The top-level `jwks` (`fed-2026-04`) signs the entity statement and is the federation-signing key set. The metadata `jwks` (`tickets-2026-04`) signs `PermissionTicket` JWTs and is the ticket-signing key set.