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)
- Redirect the user to the authorization endpoint with
response_type=code, yourclient_id,redirect_uri,scope, astate,aud=<FHIR base URL>, and PKCE (code_challenge,code_challenge_method=S256). - Include
launch/patientto receive a patient in context, plusopenid fhirUser. - 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.
- Exchange the returned
codeat the token endpoint (withcode_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:
- Read the server's configuration from
iss. - Redirect to the authorization endpoint as above, adding
launch=<launch value>and thelaunchscope (instead oflaunch/patient). - After consent, the token response includes
patientandencounter(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:
patient/Condition.rs?category=http://terminology.hl7.org/CodeSystem/condition-category|problem-list-item(alsoencounter-diagnosis, andhttp://hl7.org/fhir/us/core/CodeSystem/condition-category|health-concern)patient/Observation.rs?category=http://terminology.hl7.org/CodeSystem/observation-category|laboratory(alsovital-signs,social-history,survey, andhttp://hl7.org/fhir/us/core/CodeSystem/us-core-category|clinical-testand|sdoh)
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
- A refresh token is issued in the first token response whenever
offline_accessis granted. Native and mobile applications use the same flow as any other app: register as apublicclient, use authorization code with PKCE, requestoffline_access, and read the refresh token from the token response. No additional step is needed. - Each refresh token is valid for at least three months (91 days). Each use of a refresh token returns a new refresh token valid for a new 91-day period, without asking the user to sign in or consent again. Store the newest refresh token and discard the one you used.
- The consent screen tells the user when
offline_accessis being granted, as background access.
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
- By the patient. A patient signs in to their PrevHealth authorization account and revokes any connected application. Access tokens and refresh tokens stop working within moments, well inside one hour, at any time of day.
- By the application.
POSTthe token to the revocation endpoint.
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).