Skip to content

Latest commit

 

History

History
260 lines (209 loc) · 11.4 KB

authorization.md

File metadata and controls

260 lines (209 loc) · 11.4 KB

SMART Backend Services: Authorization Guide

Profile audience and scope

This profile is intended to be used by developers of back-end services that need to access FHIR resources by requesting access tokens from OAuth 2.0 compliant authorization servers. This profile assumes that a backend service has been authorized up-front, at registration time, and describes the runtime process by which the service acquires an access token that can be used to communicate with a FHIR Resoure Server.

Use this profile when the following conditions apply:

  • The service runs automatically, without user interaction
  • The service is able to protect a private key

Examples

  • An analytics platform or data warehouse that periodically performs a bulk data export from an electronic health record system to provide insights into a population of patients.

  • A lab monitoring service that determines which patients are currently admitted to the hospital, reviews incoming laboratory results, and generates clinical alerts when specific trigger conditions are met.

  • A data integration service that periodically queries the EHR for newly registered patients and synchronizes these with an external database

  • A utilization tracking system that queries an EHR every minute for bed and room usage and displays statistics on a wall monitor.

Registering a SMART Backend Service

Before a SMART backend service can run against an EHR, the service must be registered with that EHR's authorization service. SMART does not specify a standards-based registration process, but we encourage EHR implementers to consider the OAuth 2.0 Dynamic Client Registration Protocol for an out-of-the-box solution.

No matter how a backend service registers with an EHR's authorization service, at registration time every SMART backend service must:

  • Register a fixed "issuer URL" with the EHR
  • Register a public RSA key with the EHR (for RSA SHA-256 signatures)

Upon registration, the server assigns a client_id, which the client uses when obtaining an access token.

Obtaining an access token

By the time a backend service has been registered with the EHR, the key elements of organizational trust are already established. That is, the app is considered "pre-authorized" to access clinical data. Then, at runtime, the backend service must obtain an access token in order to work with clinical data. Such access tokens can be issued automatically, without need for human intervention, and they are short-lived, with a recommended expiration time of five minutes.

To obtain an access token, the service uses an OAuth 2.0 client credentials flow, with a JWT assertion as its client authenticaiton mechanism. The exchange, depicted below, allows the backend service to authenticate to the EHR and request a short-lived access token:

Protocol details

Before a backend service can request an access token, it must generate a one-time-use JSON Web Token that will be used to authenticate the service to the EHR's authorization server. The authentication JWT is constructed with the following claims, and then signed with the backend service's private RSA key (RSA SHA-256 signature). For a practical reference on JWT, as well as debugging tools and client libraries, see http://jwt.io.

Authentication JWT Claims
iss required The service's issuer URI, as registered with the EHR's authorization server
sub required The service's client_id, as determined during registration with the EHR's authorization server
aud required The EHR authorization server's "token URL" (the same URL to which this authentication JWT will be posted -- see below)
exp required Expiration time integer for this authentication JWT, expressed in seconds since the "Epoch" (1970-01-01T00:00:00Z UTC). This time MUST be no more than five minutes in the future.
jti required A nonce string value that uniquely identifies this authentication JWT.

After generating an authentication JWT, the service requests a new access token via HTTP POST to the EHR authorization server's token endpoint URL, using content-type application/x-www-form-urlencoded with the following parameters:

Parameters
scope required The scope of access requested. See note about scopes below
grant_type required Fixed value: client_credentials
client_assertion_type required Fixed value: urn:ietf:params:oauth:client-assertion-type:jwt-bearer
client_assertion required Signed authentication JWT value (see above)

The access token response is a JSON object, with the following properties:

Access token response: property names
access_token required The access token issued by the authorization server.
token_type required Fixed value: bearer.
expires_in required The lifetime in seconds of the access token. The recommended value is 300, for a five-minute token lifetime.
scope required Scope of access authorized. Note that this can be different from the scopes requested by the app.

Server Obligations

TODO

Servers SHALL

  • validate the signature on the JWT
  • check that the JWT exp is valid
  • check that the JWT aud matches the server's OAuth token URL (the URL to which the token was POSTed)
  • check that this is not a jti value previously encountered for the given sub within the maximum allowed authentication JWT lifetime (5 minutes). This check prevents replay attacks.
  • ensure that the client_id provided is known and associated with the supplied iss

Scopes

As there is no user or launch context when performing backed services authorization, the existing SMART on FHIR scopes are not appropriate. Instead, applications use system scopes, which have the format system/(:resourceType|*).(read|write|*). These have the same meanings as their matching user/(:resourceType|*).(read|write|*) scopes, but associated with the permissions of the authorized client instead of a human end-user.

Worked example

Assume that a "bilirubin result monitoring" service has registered with the EHR's authorization server, establishing the following

  • JWT "issuer" URL: https://bili-monitoring-service.example.com/
  • OAuth2 client_id: bili_monitor
  • RSA public key

Separately, the service also maintains its RSA private key.

To obtain an access token at runtime, the bilirubin monitoring service wants to start monitoring some bilirubin values. It needs to obtain an OAuth2 token with the scopes system/*.read system/CommunicationRequest.write. To accomplish this, the service must first generate a one-time-use authentication JWT with the following claims:

1. Generate a JWT to use for client authentication:
{
  "iss": "https://bili-monitoring-service.example.com/",
  "sub": "bili_monitor",
  "aud": "https://authorize.smarthealthit.org/token",
  "exp": 1422568860,
  "jti": "random-non-reusable-jwt-id-123"
}
2. Generate an RSA SHA-256 signed JWT over these claims

Using the service's RSA private key, the signed token value is:

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2JpbGktbW9uaXRvcmluZy1zZXJ2aWNlLmNvbS8iLCJzdWIiOiJiaWxpX21vbml0b3IiLCJhdWQiOiJodHRwczovL2F1dGhvcml6ZS5zbWFydHBsYXRmb3Jtcy5vcmcvdG9rZW4iLCJleHAiOjE0MjI1Njg4NjAsImp0aSI6InJhbmRvbS1ub24tcmV1c2FibGUtand0LWlkLTEyMyJ9.Psqfs2IEw_1GcGiSZDdEZquS-iA_gVBpNSedAghL4R9FkJWdvReXvkeBFtgBIa2PjRIQQSLYR7p3XtaH3YERivuxOKCg7OCla8dkLrlaNujhfSdwEdvn-f1GTrytjNTJWEHg0jEDeRoZn7zYy7jFZBYmF0xsRwZe7wisyaCob1w

(Note: to inspect this example JWT, you can visit http://jwt.io, choose RS256, paste in the provided RSA keys, and then paste the JWT value into the "encoded" field.)

3. Obtain an access token

The service then calls the SMART EHR's "token endpoint" using the one-time use authentication JWT as its client authentication mechanism:

Request

POST /token HTTP/1.1
Host: authorize.smarthealthit.org
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials&scope=system%2F*.read%20system%2FCommunicationRequest.write&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer&client_assertion=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2JpbGktbW9uaXRvcmluZy1zZXJ2aWNlLmNvbS8iLCJzdWIiOiJiaWxpX21vbml0b3IiLCJhdWQiOiJodHRwczovL2F1dGhvcml6ZS5zbWFydHBsYXRmb3Jtcy5vcmcvdG9rZW4iLCJleHAiOjE0MjI1Njg4NjAsImp0aSI6InJhbmRvbS1ub24tcmV1c2FibGUtand0LWlkLTEyMyJ9.Psqfs2IEw_1GcGiSZDdEZquS-iA_gVBpNSedAghL4R9FkJWdvReXvkeBFtgBIa2PjRIQQSLYR7p3XtaH3YERivuxOKCg7OCla8dkLrlaNujhfSdwEdvn-f1GTrytjNTJWEHg0jEDeRoZn7zYy7jFZBYmF0xsRwZe7wisyaCob1w

Response

{
  "access_token": "m7rt6i7s9nuxkjvi8vsx",
  "token_type": "bearer",
  "expires_in": 300,
  "scope": "system/*.read system/CommunicationRequest.write"
}