OAuth2 API for RESTful access to patient data
Before an app connects to a BlueButton+ provider, it registers with that provider as an OAuth2 Client. The registration process informs the provider what the app is called, where to find it, what permissions it might ask patients for, and how to display an authorization screen to patients.
BB+ apps register with providers using OAuth2 Dynamic Client Registration. This guide walks through the process of registering a client using open registration, as well as an example of using trusted registration which leverages BB+ Registries.
BlueButton+ allows for several different types of clients and use cases that are differentiated along two axes: whether or not a client can reasonably guard its client_secret
at runtime, and whether or not it can guard its registration_jwt
at runtime (if it has one). The OAuth 2 specification defines the former class distinction as "confidential" and "public" clients, but there is not existing terminology for the latter classification. This combination leads us to a matrix of six possible application styles:
Can guard client_secret |
Can NOT guard client_secret |
|
---|---|---|
Can guard registration_jwt |
Trusted Confidential App | Trusted Public App |
Can NOT guard registration_jwt |
Semi-Trusted Confidential App | Semi-Trusted Public App |
No registration_jwt |
Experimental Confidential App | Experimental Public App |
When deciding whether and how to register with a Registry Service, a client application developer MUST determine which of the above categories are appropriate for their application. Application developers must take care to select the appropriate category for their application based on its runtime capabilites as well as policies around the data. A Registry should help enable these distinctions by explicitly asking application develoeprs about the capabilities of their applications in order to determine which policies to apply.
registration_jwt
(using mechanisms outside the scope of this specification)registration_jwt
or client_secret
are exposed, it should be considered a major security breach and system-wide invalidation of the application's instances (and re-provisioning of its registration_jwt
) are reasonable and expected countermeasures.redirect_uri
s at the Registry Service and the authorization server MUST enforce them.registration_jwt
is exposed, it should be considered a major security breach and system-wide invalidation of the application's instances (and re-provisioning of its registration_jwt
) are reasonable and expected countermeasures.registration_jwt
is packaged in with the application and is therefore available to all end-users. This means that any holder of this JWT could try to register with this application's parameters and it must not be trusted for access without a user's explicit consent.redirect_uri
s at the Registry Service and the authorization server MUST enforce them.registration_jwt
is packaged in with the application and is therefore available to all end-users. This means that any holder of this JWT could try to register with this application's parameters and it must not be trusted for access without a user's explicit consent.redirect_uri
redirect_uri
https POST
to the registration endpoint,
supplying registration parameters as a JSON object.
Title: BlueButton+ Open Registration Process participant App participant Provider App->Provider: Registration Request (no JWT) Provider->App: client_id + secret + registration token
Parameter | Type | Description |
---|---|---|
client_name |
string |
Human-readable name of the Client to be presented to the user. |
client_uri |
string |
URL of the homepage of the Client. |
logo_uri |
string |
URL that references a logo for the Client. If present, the server SHOULD display this image to the end user during approval |
tos_uri |
string |
URL that points to a human-readable Terms of Service for the Client. The Authorization Server SHOULD display this URL to the End-User if it is given. |
redirect_uris |
array |
Array of redirect URIs for use in the Authorization Code and Implicit grant types. An Authorization Server SHOULD require registration of valid redirect URIs for all clients that use these grant types in order to protect against token and credential theft attacks. |
response_types |
array |
Array of the OAuth 2.0 response types that the
Client may use.
|
token_endpoint_auth_method |
string |
The requested authentication type for the Token
Endpoint.
|
grant_types |
array |
Array of OAuth 2.0 grant types that the Client
may use.
|
scope |
string |
Space separated list of scope values (as
described in OAuth 2.0 Section 3.3 [RFC6749]) that
the client is declaring that it may use when
requesting access tokens. A valid BB+ scope must
include one or both of:
search
and
summary |
contacts |
array |
Array of email addresses for people responsible for this Client. The Authorization Server MAY make these addresses available to end users for support requests for the Client. An Authorization Server MAY use these email addresses as identifiers for an administrative page for this client. |
POST /register HTTP/1.1 Content-Type: application/json Accept: application/json Host: bbplus-provider.org { "client_name": "Blood Pressure Grapher", "client_uri": "https://bpgrapher.org", "logo_uri": "http://bpgrapher.org/images/logo.png", "contacts": [ "plot-master@bpgrapher.org" ], "tos_uri": "https://bpgrapher.org/tos", "redirect_uris": [ "https://bpgrapher.org/after-auth" ], "response_types": ["token"], "grant_types": ["implicit"], "token_endpoint_auth_method": "none", "scope": "summary" }
POST /register HTTP/1.1 Content-Type: application/json Accept: application/json Host: bbplus-provider.org { "client_name": "Blood Pressure Grapher", "client_uri": "https://bpgrapher.org", "logo_uri": "http://bpgrapher.org/images/logo.png", "contacts": [ "plot-master@bpgrapher.org" ], "tos_uri": "https://bpgrapher.org/tos", "redirect_uris": [ "https://bpgrapher.org/after-auth" ], "response_types": ["code"], "grant_types": ["authorization_code"], "token_endpoint_auth_method": "client_secret_basic", "scope": "summary" }
The client will receive a unique
client_id
for that service provider,
a client_secret
that it can use to authorize at
the token endpoint, as well as a
registration_access_token
that it can use
to maintain its registration at the registration endpoint, as described in
OAuth Dynamic Client Registration.
In addition to open registration, BlueButton+ providers support a Trusted Registration option. When performing a Trusted Registration, apps follow the protocol described above, with the addition of a bearer token from a BB+ Registry that the provder trusts. This trusted bearer token is a JSON Web Token signed with JSON Web Signature and is verifiable by the service provider.
BB+ expects that most apps will connect to multiple BB+ providers, and many apps will run directly from multiple end-user devices. This specification balances a desire for fine-grained app registration (e.g. auditing each device's access history independently) with a need for coarser-grained permissions (e.g. disabling a rogue app across all providers, or all devices). We achieve this balance by enabling two points of control: Client Classes and Client Instances.
As an example to illustrate this distinction, let's consider an iOS-based Blood Pressure Grapher. This app is considered a "Client Class" because it's associated with a single author, homepage, logo, Terms of Service agreement, etc. As we'll describe below, this class has instances for each BB+ Provider the Blood Pressure Grapher registers with, and possibly for each device it runs on.
Client Instances per-Provider In
the OAuth sense, the BP Grapher app has a separate
"instance" for each (app, provider) pair it
participates in. For example, it will likely use a distinct
client_id
to communicate with each BB+
provider. (Since client_id
is assigned
by each provider as part of the normal OAuth Dynamic
Registration process, apps generally will
have have distinct identifiers for each provider.)
Still, since these instances all represent "the same
app", they belong to the same Client
Class.
Client Instances per-Device
Similarly, if the Blood Pressure Grapher is installed
on multiple iOS devices, it will be assigned a
separate client_id
for each
(app-on-device, provider) pair it participates in.
(Since client_id
is assigned by each
provider as part of the normal OAuth Dynamic
Registration process, apps MUST be assigned
distinct identifiers per-device by each provider.)
Again, since these instances all represent "the same
app," they belong to the same Client
Class.
Instance- and Class-level
Controls In this way, BB+ Providers MUST assign
each instance of a Client Class a different
client_id
, as stated in OAuth 2 Dynamic Registration section 5.1. Apps
MUST simply use the identifiers assigned. No matter which instance
identifiers are assigned, BB+ Providers maintain the
ability to make decisions at the Class level, so if
the Blood Pressure Grapher loses trust, all of its
instances can be disabled in one fell swoop.
Title: BlueButton+ Trusted Registration Process participant App participant Provider participant "BB+ Registry" note over App,"BB+ Registry": Manual pre-registration App-->BB+ Registry: Register for membership BB+ Registry-->BB+ Registry: Verify app claims BB+ Registry-->BB+ Registry: Store app claims,\nmake available for discovery BB+ Registry-->App: Signed Registration JWT note over App: Time passes... time to register App->BB+ Registry: Lookup provider BB+ Registry->App: Provider information App->Provider: Registration Request + JWT Provider->Provider: Validate issuer Provider->BB+ Registry: Get Registry discovery information BB+ Registry->Provider: Registry discovery information Provider->BB+ Registry: Lookup signing key BB+ Registry->Provider: JWK Set Provider->Provider: Validate signature Provider->BB+ Registry: Lookup app claims BB+ Registry->Provider: Pre-registered app claims Provider->Provider: Verify reg matches claims Provider->App: client_id, secret, registration token
client_name
and
client_uri
iss
field of the token (and/or doing
token introspection from the iss
).iss
and sub
fields
of the token to find the apps.json entry for this
applicationclient_id
and client_secret
as well as a
registration_access_token
First, the Client class is manually pre-registered
with a BB+ Registry. This process may fix any of the
client parameters listed above, such as the
client_name
and
redirect_uris
. At a minimum,
client_name
and client_uri
must be fixed at pre-registration time. All classes of
a client will share any fixed parameters.
The pre-registered client class will receive:
client_uri
).iss
: issuer is the URL of the
BB+ Registrysub
: subject is an identifier
from the BB+ Registry (as described above)iat
: timestamp of when this
pre-registration information was last updated
(and this token was issued)exp
: OPTIONAL timestamp of when
this pre-registration token is no longer
validkid
: identifier for which key was
used to sign this token at the BB+ RegistrayThe registration JWT from the previous step is used by instances of the client during the initial call to the registration endpoint for each service provider. The service provider now needs to validate the registration JWT, compare the client's pre-registered attributes to the ones requested dynamically, and proceed with the registration.
For example, a client may send:
POST /register HTTP/1.1 Content-Type: application/json Accept: application/json Authorization: Bearer ey...{JWT token omitted for brevity}...q48d Host: bbplus-provider.org { "client_name": "Blood Pressure Grapher", "contacts": [ "plot-master@bpgrapher.org" ], "tos_uri": "https://bpgrapher.org/tos", "response_types": ["code"], "grant_types": ["authorization_code"], "token_endpoint_auth_method": "client_secret_basic", "scope": "summary" }
All service providers within a BB+ Registry must be
able to validate the signature and validity of any
registration tokens. First, the provdier must parse
the JWT and extract its iss
(issuer) field
and determine if the issuer is trusted. This trust decision
will generally be a configuration option on the provider, which
will have a list of Registry Services that it trusts (identified
by their issuer URLs).
If the iss
is from a trusted source, the provider
then does discovery against the registry
and finds the jwks_uri
of the registry. The provider
downloads the key set from this URL (using an HTTPS GET request)
and extracts the key with the kid
that is named in the
JWT. Using that key, the provider can then validate the signature
of the JWT as described in JSON Web Signatures.
It's recommended that BB+ Registries publish a token introspection endpoint to facilitate token validation by service providers. In addition to being able to check the validity of the signature on a JWT, an introspection endpoint can let a service provider determine if a JWT is still valid (or has been revoked).
The service provider does BB+
App Discovery based on the sub
(subject) and iss
(issuer) in
the registration token to determine which client
metadata fields have been set ahead of time by the
pre-registration process. The provider
MUST abide by and enforce all pre-registered client values.
The Service provider will
compare these pre-registered fields with any
dynamically provided fields and determine the validity
of the registration. It is RECOMMENDED that providers
perform a direct string comparison between pre-registered
values and the values provided by the client. If there is a
mismatch in any field, the provider MUST either return
an invalid_client_metadata
error (as described
in OAuth 2 Dynamic Registration) or replace the dynamically
requested value with the pre-registered value.
Once the provider has determined that the client registration
request is valid, it will generate and issue a unique client_id
and registration_access_token
, and if applicable, a unique client_secret
.
The provider MUST NOT re-use the same client_id
, registration_access_token
,
or client_secret
for different client instances, even
if these instances are part of the same client class.
The provider MAY link together different client instances that are part of the same Client Class to facilitate logging, auditing, and emergency deprovisioning of compromised applications. The method for the provider doing this is out of scope of this specification.
client_id
for each registered instance and MUST NOT re-use
the same client_id
for
other instances in the class.client_id
issued
to them by each service provider when talking to
that service provider.client_id
's from every service
provider, which means that even a single
installation of a client could have multiple
"instances" as far as the network is
concerned.BlueButton+ Pull uses BB+ Registry
Servers to enable discovery of BB+ Providers
and BB+ Apps. Each BB+ Registry Server exposes a set of
trusted providers and a set of trusted apps at
well-known https
URLs, using simple
JSON-LD arrays. In
the BB+ ecosystem, apps and providers must be
pre-configured with a BB+ Registry Server (or Servers).
All discovery endpoints are organized beneath
.well-known/bb/
at the server's root.
JSON-LD provides a nice balance between idiomatic JSON and well-structured data representations. By combining a JSON payload with a JSON-LD context, properties can be expanded to full URIs so that data can be mixed and matched with other services. For example, the BB+ Provider element below can automatically be:
A BB+ Registry Server exposes a BB+ Registry
discovery endpoint at:
/.well-known/bb/registry.json
registry.json
is a single
Registry element expressed as JSON-LD with a default
@vocab
from http://schema.org and
additional properties defined in the BB+ JSON-LD Context
While any vocabulary from schema.org may be used in describing a BB+ Registry, the following fields are required:
Property | Description |
---|---|
name |
Human-readable Name of the BB+ Registry |
url |
Root URL of the BB+ Registry (to which .well-known/bb/registry.json can be appended) |
jwks_uri |
URL of the JSON Web Key (JWK) set for this BB+ Registry. This URL MUST be protected by HTTPS or equivalent. |
trust_bundle_uri |
URL of the BB+ Push Trust Bundle (.p7b , .p7c , or .p7m file).
Only used for BB+ Registries that also act as BB+ Push Trust Bundles.
|
oauth2 |
Collection of OAuth2 endpoints |
introspect |
RECOMMENDED OAuth2 introspection endpoint (As described in this draft) |
Content-Type: application/json Link: <http://jmandel.github.com/blue-button-plus-pull/context-json-ld.js>; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" { "name": "The BB+ Registry Foundation", "url": "http://registry.org/", "jwks_uri": "https://registry.org/public_key.jwks", "oauth2": { "introspect": "https://registry.org/oauth/introspect", } }
A BB+ Provider is defined as the unique combination of a data service provider (a service that serves the BB+ data API) and an authorization provider (an OAuth2 authorization server and related components). Therefore, if a given data provider allows for two different authorization services, there will be two different BB+ Providers listed at the Registry Server. Likewise, if an authorization server is used for two different data providers, it will be listed separately in two different BB+ Providers at the Registry.
A BB+ Registry Server exposes a BB+ Provider
Discovery endpoint at:
/.well-known/bb/providers.json
providers.json
is a JSON array of
Provider elements, expressed as JSON-LD with a default
@vocab
from http://schema.org and
additional properties defined in the BB+ JSON-LD Context
While any vocabulary from schema.org may be used in describing a provider, the following fields are required:
Property | Description |
---|---|
name |
Name of the BB+ Provider organization |
url |
Primary url of the provider organization |
Each provider also exposes its endpoint URIs via the following properties:
Property | Description |
---|---|
patient_signin |
URL where a patient can sign in to the Provider |
oauth2 |
Collection of OAuth2 endpoints |
registration_uri |
OAuth2 dynamic registration endpoint |
authorize_uri |
OAuth2 authorization endpoint |
token_uri |
OAuth2 token endpoint |
introspect_uri |
OAuth2 introspection endpoint (As described in this draft) |
bb_api |
Collection of BB+ API endpoints |
summary |
BlueButton+ Clinical Summary endpoint |
search |
BlueButton+ Document Search endpoint |
Content-Type: application/json Link: <http://blue-button.github.com/blue-button-plus-pull/context-json-ld.js>; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" [ { "name": "Good Health Clinic", "description": "Serving your health needs since 1999", "url": "http://goodhealthclinic.org", "patient_signin": "http://portal.goodhealthclinic.org", "location": { "geo": { "latitude": 42.3591, # just making the point that we can "longitude": -71.0934 # use arbitrary schema.org properties } }, "oauth2": { "registration_uri": "http://portal.goodhealthclinic.org/register", "authorize_uri": "http://portal.goodhealthclinic.org/authorize", "token_uri": "http://portal.goodhealthclinic.org/token" }, "bb_api":{ "summary": "http://api.goodhealthclinic.org/patient/documents/summary", "search": "http://api.goodhealthclinic.org/patient/documents/search" } }, { ... (more providers here) ... } ]
A BB+ Registry Server exposes a BB+ App
Discovery endpoint at:
/.well-known/bb/apps.json
apps.json
is a JSON array of App
elements, expressed as JSON-LD with a default
@vocab
from http://schema.org and
additional properties defined in the BB+ JSON-LD Context
While any vocabulary from schema.org may be used in describing a provider, the following fields are required:
Property | Description |
---|---|
name |
Name of the BB+ App |
url |
Primary url of the BB+ App |
fixed_registration_parameters |
JSON structure containing any registration
parameters that are fixed across all
instances of this app. A Trusted Registration
request must match on all parameters
defined in the fixed_registration_parameters
structure. (Note: matching requires
that JSON.stringify return identical
values for a given fixed registration parameter.
Failure to match on any parameter results in a rejected
Trusted Registration request.) This structure may include
any valid client metadata reistration parameters,
and the following properties MUST be
locked down.
|
client_name |
client_name must be fixed across all app instances and must match the schema.org name above |
client_uri |
client_uri must be fixed across all app instances and must match the schema.org url above |
redirect_uris |
the set of allowable redirect_uris must be fixed across all app instances |
Content-Type: application/json Link: <http://blue-button.github.com/blue-button-plus-pull/context-json-ld.js>; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" [ { "url": "https://bpgrapher.org", "name": "Blood Pressure Grapher", "fixed_registration_parameters": { "client_name": "Blood Pressure Grapher", "client_uri": "https://bpgrapher.org", "logo_uri": "http://bpgrapher.org/images/logo.png", "contacts": [ "plot-master@bpgrapher.org" ], "tos_uri": "https://bpgrapher.org/tos", "redirect_uris": [ "https://bpgrapher.org/after-auth" ], "response_types": ["token"], "grant_types": ["implicit"], "token_endpoint_auth_method": "none", "scope": "summary" }, }, { ... (more apps here) ... } ]
In APIs protected by OAuth 2, the scope
mechanism allows the Authorization Server, Client, and Protected Resource to communicate the level of access that the client has been authorized for. These scopes are expressed at different times throughout the process:
The scope
paramter in OAuth 2 is a space-separated list of strings, and the values, syntax, and sematnics of the individual scopes are service specific. In the context of Blue Button+, scopes that are defined by the BB+ Push and Pull protocols.
In BB+, structured scopes have two parts: a root scope value and a (potentially empty) parameter value, separated by the colon ":" character. In general, the root scope value is used in the pre-registration and registration steps above (steps 1-3), and the fully-specified scope value (root:parameter) is used in the authorization, token, and access steps above (4-7). Registration for the root scope value implies permission for the client instance to ask for authorization for any fully parameterized scope based on that root. (Note that as with all OAuth 2 scopes, the permission to ask for authorization in no way guarantees or implies that authorization will be granted.)
Root Value | Parameter | Description |
---|---|---|
search |
patient record pseudo-id |
In the Pull authorization protocol, An authorization server need not understand the value of the parameter in order to let the user grant access, but the resource server MUST understand and enforce the value. At this time, each BB+ Pull authorization MUST be associated with exactly one patient record identifier. In other words, a given access token may only be used to access one patient's data. In step 5, the authorization server MAY prompt the user to choose a single patient record: for example, by asking the end-user to enter a code directly or choose from a list. The details of how these parameter values are defined and communicated between the protected resource, client, end-user, and authorization server are out of scope for this proposal and often out of band. (For example a pediatrician's office may provide a parent with a print-out including a short code to type in at authorization time for authorizing access to each child's record.) |
summary |
||
send-email-to |
DIRECT email address |
In the Push authorization protocol, this scope represents end-user authorization for the provider to send email to the DIRECT email address stated in the parameter value, such as send-email-to:oip8erjoiaewjoi@direct.bp-grapher.org . When used without a parameter in step 4, the authorization server MUST prompt the end user for the DIRECT email address to use. In steps 5-7, the semantics of this scope without the parameter value are undefined and out of scope for this protocol. |
bb_api.*
properties are used for the operations described below.
GET
for read-only operations), including its OAuth
bearer token using an Authorization
header or any valid
mechanism from RFC6750.
Content-type
of the responseAccept
HTTP header with the
request. Apps that are unable to set an Accept
header
may alternatively supply a _format
query parameter.
Valid content-type
values are listed for each endpoint below.
bb_api
.summary
Accept header | description |
---|---|
text/xml |
DEFAULT. A Consolidated CDA in XML. |
text/plain |
A document rendered as a simple text file (as in the VA's "classic" BlueButton implementation. |
text/html |
A document rendered as HTML (as might be obtained by applying a generic stylesheet to a C-CDA). |
To retrieve the most recent clinical summary document, an app
issues an http GET
to the summary
endpoint, including its bearer token using an Authorization
header or any valid mechanism from RFC6750.
GET /bb/record/summary HTTP/1.1 Accept: application/xml Authorization: Bearer i8hweunweunweofiwweoijewiwe Host: bbplus-provider.org
bb_api
.search
The clinical document search endpoint allows an app to search for clinical documents that match a set of search parameters. A core set of features are described here.
The following five fields of a DocumentReference
must be defined to support
BB+ Document Search:
location
to support Document Retrievalformat
to distinguish CCDA vs. CCD 1.0 vs. CCR (see below)type
to describe clinical contentperiod
to describe period of careFor full details, see FHIR's DocumentReference definition.
Query parameter | Description |
---|---|
format |
Filter by information model (e.g. CCD 1.0, C-CDA, CCR).
format , separate values with ,
E.g. format=CCD,CCR
|
type |
Filter the available documents by document type. The following values are supported:
type , separate values with ,
E.g. type=Summary,Discharge
|
period:before |
Filter for documents describing a period before a given time. If no time zone is specified, the server may assume local time, local time of the querying system, or some other fixed time zone. Example: |
period:after |
Filter for documents describiing a period after a given time. |
Accept header | description |
---|---|
text/xml |
DEFAULT. An Atom feed representation of DocumentReference elements.
|
application/json |
A JSON bundle of DocumentReference elements (like Atom, but in FHIR-defined JSON) |
To search for relevant documents, an app
issues an http GET
to the search
endpoint, including its bearer token using an Authorization
header or any valid mechanism from RFC6750.
The following query obtains a JSON feed of C-CDA Operative notes and Procedure notes pertaining to episodes of care that ended prior to 2013.
Newlines and indentation added around URL parameters for clarity
GET /bb/record/DocumentReference? # provider-supplied endpoint format=CCDA& # limit to Consolidated CDAs type=Operative,Procedure& # limit to Operative notes *or* Procedure notes period:before=2013-01-01T00:00Z # limit to care episodes that HTTP/1.1 # (partially) occurred pre-2013 Accept: application/json # return a JSON feed # (rather than Atom XML) Authorization: Bearer i8hweunweunweofiwweoijewiwe Host: bbplus-provider.org
Note: FHIR's DocumentReference
document
descriptor includes a lot more metadata than BB+ may need for the
document search endpoint. FHIR defines many of these fields
(including patientId) as required. The representation below is
simplified.
In practice, to retrieve actual document contents, app
developers will want to examine the entry
array of
the search result, and for each DocumentReference
in that
array, the location
can be extracted and
dereferenced. For more details, see Document Retrieval.
{ "title": "Search results for resource type DocumentReference", "entry": [ { "id": "https://bbplus-provider.org/bb/record/DocumentReference/123", "updated": "2013-04-22T04:15:10Z", "content": { "resourceType": "DocumentReference", "type": { "coding": [{ "code": "Consult", "display": "Consultation Note" }] }, "format": { "coding": [{ "code": "CCDA", "display": "Consolidated CDA" }] }, "created": "2005-12-24T09:35:00+11:00", "indexed": "2005-12-24T09:43:41+11:00", "status": "current", "mimeType": "text/xml", "location": "https://bbplus-provider.org/bb/record/Binary/9n9283829f" } } ] }
By performing a Document Search,
an app obtains an Atom feed (or JSON equivalent) of
DocumentReference
document descriptors. To fetch actual
document contents, the app issues an authenticated, HTTPS
GET
to dereference the document URI. This URI is
available in the ./location/@value
xpath for an Atom entry,
or the location
field of a JSON feed
entry.
GET /bb/record/Binary/9n9283829f HTTP/1.1 Authorization: Bearer i8hweunweunweofiwweoijewiwe Host: bbplus-provider.org