What a node is told about the caller
The gateway authenticates to each node as itself, with that endpoint’s own
credentials (OAuth 2.0 to a node),
and tells the node who asks in a header of its own (§13.1, N24, N25, §12.4).
Where a node’s authorization server exchanges tokens (RFC 8693), the gateway
also asks it for a token per caller, with the caller’s verified token as the
subject and only the caller’s scopes that cover the operation; the caller’s
token reaches that authorization server alone, never the node, and a caller
the edge mode asserted has no token to exchange, so that node is
node-error with nothing sent
(A token per caller).
Every request it sends a node carries openEHR-federation-client: the
federated query, every routed read and write, a definition request, the
ask-all probe and the read of an EHR by subject. The value is a compact JWS
the gateway signs for that one node with the current key of [signing],
the key whose public half it publishes at {base}/.well-known/jwks.json and
declares as federation.auth.jwks_uri in OPTIONS {base}/. Its JOSE header
names alg, ES256 for a P-256 key and ES384 for a P-384 key, the
key’s kid and typ openehr-federation-client+jwt. Its claims:
| Claim | Value |
|---|---|
iss | the gateway: the client_id of the endpoint’s oauth2 grant, the iss of its client assertions there, or else federation.id |
aud | the node’s endpoint id in the registry |
iat, exp | seconds since the epoch; exp is 60 seconds after iat |
jti | a fresh version 4 UUID per token |
sub | the caller’s sub, as the gate verified it |
iss_upstream | the issuer that vouched for the caller |
verified_by | signature, introspection, or edge for an identity the edge asserted |
subject_organization_id | the caller’s organisation, when its token names one (IHE IUA) |
purpose_of_use | each purpose of use the token declares, as {"system", "code"} (IHE IUA, HL7 v3 PurposeOfUse) |
scope | the caller’s scopes as granted; under a patient grant, only the patient/ scopes that cover the operation |
ehrId | under a patient grant only: the patient’s own ehr_id at this node (SMART on openEHR master04 §Capabilities) |
acting | person when the caller’s sub names a natural person, client when it names a client application (Professionals and assurance) |
subject_name | the professional’s name, when the caller’s token states extensions.ihe_iua.subject_name (IHE IUA) |
national_provider_identifier | the professional’s identifier from their national authority, when the caller’s token states it (IHE IUA) |
national_provider_identifier_authority | the name of the agency that issued that identifier, when the caller’s issuer sets professional_issuing_authority and the token states it (Implementing Regulation (EU) 2026/2099 Annex Table 1 issuing_authority_name) |
subject_role | the professional’s roles, each as {"system", "code"}, when the caller’s token states extensions.ihe_iua.subject_role (IHE IUA) |
assurance_level | low, substantial or high, the level the caller’s authentication reached, when its issuer declares [auth.issuer.assurance] and the token states a value it lists |
national_contact_point | for a caller a national contact point vouched for: health_professional, every row of Implementing Regulation (EU) 2026/2099 Annex Table 1 under its data identifier, healthcare_provider, every row of Table 2, and asserted_by, the contact point’s issuer, which marks them as its assertion |
With subject_organization_id, the provider’s identifier, the professional
claims let a node record the professional and the provider of every
registration or update it makes on the caller’s behalf, which Regulation (EU)
2025/327 Art 13(4) asks an electronic health record to identify. Declare
professional_issuing_authority for an issuer whose tokens state the agency
that issued the identifier.
The token never carries the caller’s own token, its client_id, or a
patient identifier: an IUA person_id is never read (N33). The outbound
gate reads every caller claim as it reads the rest of a request, and a
request whose caller claims would carry the identifier its query was
resolved on is refused with nothing sent, logged as a security event and
answered 500 (§5.4.1). Under client credentials the node’s grant to the
gateway is a system/ scope, so the caller’s narrower scopes travel in
scope for the node to apply (N26).
A request that reaches the dispatcher without a verified caller is the
gateway’s own failure: it is answered 500 and no node is sent anything.
The gateway’s own requests for its operator, the
admission check and the redistribution of a held stored
query on the admin listener, carry the header with the gateway as sub and
no caller claim.
[signing] is therefore required whenever a registry is configured, by
registry.document or by [registry.mcsd]: a federating gateway without a
signing key refuses to start, naming the key
(Signing keys). The
specification leaves end-user conveyance open (§13.1), so the header and its
claims are FerroFED’s own design.
Verifying it at the node
A node that admits FerroFED reads the header before it decides what to release, and audits who asked:
- At admission, record the gateway’s
jwks_uri(itsOPTIONS {base}/declares it asfederation.auth.jwks_uri) and theissit will see: theclient_idthe node’s authorization server registered for the gateway, or the federation id where the gateway sends no OAuth 2.0 grant. - Read exactly one
openEHR-federation-clientvalue; refuse a request with none or with more than one. - Check the JOSE header:
typisopenehr-federation-client+jwtandalgis thealgthe gateway’s JWK Set publishes for the key the header names,ES256orES384. Refuse any other algorithm,noneincluded (RFC 8725 §3.1). - Verify the signature with the key of the gateway’s JWK Set whose
kidthe header names. Fetch the set again when thekidis unknown: during a rotation the set publishes the current and the previous key. - Check
issagainst step 1,audagainst the node’s own endpoint id, andexp, allowing a few seconds of clock skew (RFC 7519 §4.1.4). - Where the gateway authenticates with an OAuth 2.0 grant, check that
issequals theclient_idof the access token on the same request. - Apply
scopeandpurpose_of_useto what you release, confine apatient/scope to the EHR theehrIdclaim names, and recordsub,iss_upstream,verified_byandsubject_organization_idin your audit trail, withnational_contact_pointwhere a contact point relays the request: its professional and provider are the contact point’s assertion, whichasserted_bynames. Consent stays your own check (§13.2, N27).