Scope. This document describes how an application authenticates and is authorized to access the Retinex FHIR API certified to §170.315(g)(10). It is published as developer-facing technical documentation under §170.315(g)(10)(viii) and the API Transparency conditions at §170.404(a)(2), via a publicly accessible hyperlink with no preconditions.
1. Standards Supported
| Capability | Standard / Value |
|---|---|
| Authorization framework | OAuth 2.0 (RFC 6749, RFC 6750) |
| SMART App Launch | SMART App Launch 1.0.0 and 2.0.0 (SVAP-approved) |
| FHIR version | HL7® FHIR® R4 (4.0.1) |
| US Core IG | US Core 6.1.0 (USCDI v3) — see US Core Support |
| PKCE | RFC 7636, S256 challenge method (required) |
| OpenID Connect | OIDC Core 1.0 (id_token, fhirUser claim) |
| Dynamic Client Registration | RFC 7591 — see App Registration |
| Token revocation | RFC 7009 |
Both standalone launch and EHR launch sequences are supported, for patient/, user/, and system/ (backend services, SMART Backend Services) contexts.
2. Authorization Endpoints
The FHIR service base URL (the endpoint against which all interactions in the API Reference are issued) is:
https://retinex-apim.azure-api.net/smart
The associated SMART/OAuth 2.0 endpoints are:
| Endpoint | URL |
|---|---|
| SMART discovery | https://retinex-apim.azure-api.net/smart/.well-known/smart-configuration |
| Authorization | https://retinex-apim.azure-api.net/auth/authorize |
| Token | https://retinex-apim.azure-api.net/auth/token |
| Token (refresh) | https://retinex-apim.azure-api.net/auth/token |
| Registration (DCR) | https://retinex-apim.azure-api.net/auth/register |
| Revocation | https://retinex-apim.azure-api.net/auth/revoke |
| Capability statement | https://retinex-apim.azure-api.net/smart/metadata |
Always resolve endpoints from discovery, not from this table, so your application keeps working if an endpoint moves.
3. Discovery — .well-known/smart-configuration
A GET on the discovery URL returns the server's authorization metadata. No authentication is required.
GET https://retinex-apim.azure-api.net/smart/.well-known/smart-configuration
Accept: application/json
{
"issuer": "https://retinex-apim.azure-api.net/smart",
"authorization_endpoint": "https://retinex-apim.azure-api.net/auth/authorize",
"token_endpoint": "https://retinex-apim.azure-api.net/auth/token",
"registration_endpoint": "https://retinex-apim.azure-api.net/auth/register",
"revocation_endpoint": "https://retinex-apim.azure-api.net/auth/revoke",
"grant_types_supported": ["authorization_code", "refresh_token", "client_credentials"],
"response_types_supported": ["code"],
"code_challenge_methods_supported": ["S256"],
"token_endpoint_auth_methods_supported": ["none", "client_secret_basic", "private_key_jwt"],
"scopes_supported": [
"openid", "fhirUser", "profile", "offline_access", "online_access",
"launch", "launch/patient",
"patient/*.rs", "user/*.rs", "system/*.rs"
],
"capabilities": [
"launch-standalone", "launch-ehr",
"client-public", "client-confidential-symmetric", "client-confidential-asymmetric",
"context-standalone-patient", "context-ehr-patient",
"permission-patient", "permission-user", "permission-v2", "authorize-post",
"sso-openid-connect", "permission-offline"
]
}
4. Authorization Code Flow (with PKCE)
Browser and mobile apps use the Authorization Code grant. PKCE (S256) is required for all clients, public and confidential — this is required for SVAP SMART v2.0.0 and mitigates authorization-code interception.
- Generate PKCE values. Create a high-entropy
code_verifier(43–128 chars) and derivecode_challenge = BASE64URL(SHA256(code_verifier)). - Redirect to the authorization endpoint:
GET https://retinex-apim.azure-api.net/auth/authorize
?response_type=code
&client_id=YOUR_CLIENT_ID
&redirect_uri=https://app.example.com/callback
&scope=openid%20fhirUser%20offline_access%20launch/patient%20patient/Patient.rs%20patient/Observation.rs
&state=RANDOM_STATE
&aud=https://retinex-apim.azure-api.net/smart
&code_challenge=CODE_CHALLENGE
&code_challenge_method=S256
- The user authenticates (password + MFA — see MFA) and approves the requested scopes. Retinex redirects back to your
redirect_uriwithcodeandstate. - Exchange the code at the token endpoint, presenting the original
code_verifier:
POST https://retinex-apim.azure-api.net/auth/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=AUTH_CODE
&redirect_uri=https://app.example.com/callback
&client_id=YOUR_CLIENT_ID
&code_verifier=CODE_VERIFIER
Confidential clients additionally authenticate with client_secret_basic (HTTP Basic) or private_key_jwt. Public/native clients send no secret and rely on PKCE.
Token response
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "openid fhirUser offline_access launch/patient patient/Patient.rs patient/Observation.rs",
"refresh_token": "rt_8f2b1c9d4a7e...",
"id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"patient": "example-patient"
}
Send the access token as a Bearer token on every FHIR request: Authorization: Bearer <access_token>.
5. Refresh Tokens & Secure Issuance to Native Applications
Request the offline_access scope (or online_access for session-bound refresh) to receive a refresh_token. Refresh:
POST https://retinex-apim.azure-api.net/auth/token
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token
&refresh_token=REFRESH_TOKEN
&client_id=YOUR_CLIENT_ID
Refresh tokens are rotated on each use (the prior token is invalidated) and are bound to the issuing client.
Method for secure issuance of an initial refresh token to native applications
Per §170.315(g)(10)(v)(A)(1)(iii), Retinex publishes the method by which a refresh token is securely issued to a native application (a "public" client that cannot protect a client secret):
- Public client + PKCE. Native apps register as public clients with
token_endpoint_auth_method = noneand obtain tokens using the Authorization Code grant with PKCES256. No client secret is distributed in the app binary. - Authenticated, scoped request. The initial refresh token is issued only after the user completes interactive authentication (password + MFA) and explicitly grants the
offline_accessscope, over TLS 1.2+. - Registered, app-claimed redirect URIs. The redirect URI must exactly match a value registered for the client. Native apps use either an OS-registered private-use URI scheme (e.g.
com.example.app:/oauth2redirect) or an HTTPS claimed (App Links / Universal Links) redirect, so the authorization response is delivered only to the legitimate app. - On-device secure storage. Applications are expected to store the issued refresh token using platform-provided secure storage — the iOS Keychain or Android Keystore / EncryptedSharedPreferences — never in plaintext.
- Rotation & revocation. Refresh tokens rotate on use and can be revoked by the user (from their Retinex account) or by the app via the revocation endpoint.
Confidential clients that can protect a secret may instead use client_secret_basic or private_key_jwt; the affirmation of an application's ability to protect a secret is treated in good faith per §170.404(a)(4).
6. PKCE Requirement
Retinex requires PKCE on every Authorization Code request, documented here per §170.315(g)(10)(viii) and §170.404(a)(2):
code_challenge_methodmust beS256(plain is not accepted).code_verifier: 43–128 characters from the unreserved set[A-Z a-z 0-9 - . _ ~].- Authorization requests missing a
code_challengeare rejected withinvalid_request. - Token requests whose
code_verifierdoes not hash to the originalcode_challengeare rejected withinvalid_grant.
7. Supported Scopes
Published per the API Transparency conditions at §170.404(a)(2). Retinex supports SMART v2 (.rs/.cruds) scope syntax and accepts SMART v1 (.read/.write/.*) for backward compatibility.
OpenID & launch context
| Scope | Meaning |
|---|---|
openid, fhirUser, profile | OpenID Connect sign-in; fhirUser claim identifies the authorized user as a FHIR resource. |
launch | EHR launch — inherit the launch context (patient, encounter). |
launch/patient | Standalone launch — prompt to select / confirm the patient context. |
offline_access | Issue a refresh token usable when the user is offline. |
online_access | Issue a refresh token valid only while the user's session is active. |
Resource scopes
Format: <context>/<Resource>.<permissions> where context is patient, user, or system, and permissions are r/s (read/search, v2) or read (v1). Wildcards patient/*.rs, user/*.rs, and system/*.rs grant the permission across every supported resource.
Per-resource scopes are offered for each of the 26 US Core 6.1.0 resource types exposed by the API:
patient/AllergyIntolerance.rs user/AllergyIntolerance.rs system/AllergyIntolerance.rs
patient/CarePlan.rs patient/CareTeam.rs patient/Condition.rs
patient/Coverage.rs patient/Device.rs patient/DiagnosticReport.rs
patient/DocumentReference.rs patient/Encounter.rs patient/Goal.rs
patient/Immunization.rs patient/Location.rs patient/Medication.rs
patient/MedicationDispense.rs patient/MedicationRequest.rs patient/Observation.rs
patient/Organization.rs patient/Patient.rs patient/Practitioner.rs
patient/PractitionerRole.rs patient/Procedure.rs patient/Provenance.rs
patient/QuestionnaireResponse.rs patient/RelatedPerson.rs patient/ServiceRequest.rs
patient/Specimen.rs
(The same list is available under the user/ and system/ contexts. The live API Reference scope picker is generated from this same catalog.)
8. Backend Services (system scopes)
Server-to-server clients use the SMART Backend Services profile: register a confidential asymmetric client with a public key (JWK Set URL), then exchange a signed client_assertion for an access token using grant_type=client_credentials and system/*.rs scopes. No user is present and no refresh token is issued.
9. Token Revocation
POST https://retinex-apim.azure-api.net/auth/revoke
Content-Type: application/x-www-form-urlencoded
token=REFRESH_OR_ACCESS_TOKEN
&token_type_hint=refresh_token
&client_id=YOUR_CLIENT_ID
10. Error Handling
| Error | When |
|---|---|
invalid_request | Missing/duplicate parameter, or missing PKCE code_challenge. |
invalid_client | Unknown client, or failed client authentication. |
invalid_grant | Expired/used auth code, PKCE verifier mismatch, or revoked refresh token. |
invalid_scope | A requested scope is not supported or not granted to the client. |
unauthorized_client | The client is not permitted to use the requested grant type. |
access_denied | The user declined the authorization request. |
FHIR API requests with a missing/expired token return 401 Unauthorized; a token lacking the required scope returns 403 Forbidden with an OperationOutcome (see the API Reference).
11. Change Log
| Date | Version | Change |
|---|---|---|
| 2026-06-03 | 1.0 | Initial public SMART on FHIR authorization documentation for §170.315(g)(10). |