Authorization

PrevHealth uses SMART App Launch 2.2.0 on top of OAuth 2.0 and OpenID Connect. Always read endpoint URLs from <base>/.well-known/smart-configuration; the paths below are illustrative.

Endpoint Typical location
Authorization https://clinic.prevhealth.us/api/auth/oauth2/authorize
Token https://clinic.prevhealth.us/api/auth/oauth2/token
Revocation https://clinic.prevhealth.us/api/auth/oauth2/revoke
Introspection https://clinic.prevhealth.us/api/auth/oauth2/introspect
User info https://clinic.prevhealth.us/api/auth/oauth2/userinfo
JSON Web Key Set https://clinic.prevhealth.us/api/auth/jwks

1. Register your application

Applications are registered by PrevHealth; dynamic client registration is not supported. To register, send the information below through the channel in Implementation notes.

Attribute Required Notes
Application name Yes Shown to users on the consent screen.
Redirect URIs Yes Exact match. Use https URIs.
Client type Yes public (no secret), confidential-symmetric (client_secret_basic or client_secret_post), or confidential-asymmetric (private_key_jwt).
JWK Set URL For private_key_jwt A public https URL serving your public keys. The server honours the response's Cache-Control and never uses a cached key set longer than it allows (capped at 5 minutes).
Grant types Yes authorization_code and refresh_token for user-facing apps; client_credentials for backend services.
Scopes Yes The maximum set the app may request, from the list below.

The server returns a client_id and, for symmetric clients, a client_secret.

2. Choose a launch type

Standalone launch (patient)

  1. Redirect the user to the authorization endpoint with response_type=code, your client_id, redirect_uri, scope, a state, aud=<FHIR base URL>, and PKCE (code_challenge, code_challenge_method=S256).
  2. Include launch/patient to receive a patient in context, plus openid fhirUser.
  3. The user signs in and sees a consent screen listing each requested permission as a separate checkbox. The user can allow all, some or none; only the permissions the user leaves selected are granted, so your app must handle a narrower grant than it asked for.
  4. Exchange the returned code at the token endpoint (with code_verifier).

The token response includes access_token, token_type, expires_in, scope, id_token (when openid was granted), refresh_token (when offline_access was granted), and patient, the ID of the patient in context.

EHR launch (clinician)

A clinician starts the launch from PrevHealth. Your app's registered launch URL receives iss (the FHIR base URL) and launch (an opaque, short-lived launch identifier). Then:

  1. Read the server's configuration from iss.
  2. Redirect to the authorization endpoint as above, adding launch=<launch value> and the launch scope (instead of launch/patient).
  3. After consent, the token response includes patient and encounter (the encounter the clinician launched from).

A launch value expires after ten minutes. Even with a patient in context, the clinician's own PrevHealth permissions and care relationship to that patient are enforced on every request.

Backend services (population access)

Authenticate with client_credentials and a signed JWT client assertion (private_key_jwt):

POST https://clinic.prevhealth.us/api/auth/oauth2/token
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials
&scope=system/*.read
&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer
&client_assertion=<signed JWT>

The assertion's iss and sub are your client_id, aud is the token endpoint URL, exp is no more than 5 minutes ahead (a small clock skew is tolerated), and jti is unique (replays are rejected). Sign with a key published at your registered JWK Set URL. The returned access token lasts 5 minutes and cannot be refreshed; request a new one. See Bulk data export.

3. Scopes

PrevHealth supports both SMART v1 (.read) and v2 (.rs) scope syntax.

Scope Meaning
openid, fhirUser Sign-in and a reference to the signed-in person (Patient/<id> or Practitioner/<id>).
launch/patient Standalone launch with a patient in context.
launch EHR launch.
offline_access Allows a refresh token.
patient/<Resource>.rs or .read Read and search a resource for the patient in context. patient/*.rs covers all supported resources.
user/<Resource>.rs or .read Read and search resources the signed-in user is permitted to access.
system/*.read Backend services bulk export.

The supported resource names are those on the resources page.

Granular scopes. Condition and Observation can be requested by category:

If an app requests the parent patient/Condition.rs or patient/Observation.rs, the user may be offered the category scopes on the consent screen and can grant any subset. Access is then limited to the categories that were granted.

4. Tokens

Token Lifetime Notes
Access token 1 hour (backend services: 5 minutes) A signed JWT (RS256) whose audience is the FHIR base URL. Send as Authorization: Bearer.
ID token With openid RS256; contains fhirUser when that scope is granted.
Refresh token 91 days, renewed on every refresh See below.

PKCE with S256 is required for all registered applications' authorization-code requests.

Refresh tokens

POST https://clinic.prevhealth.us/api/auth/oauth2/token
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token&refresh_token=<token>&client_id=<client_id>

5. Revoking access

An access token that has been revoked is rejected by the FHIR API with HTTP 401.

6. Token introspection

POST a token to the introspection endpoint with your client credentials to learn whether it is active and what it grants (RFC 7662).