Skip to contents

[Experimental]

Configure your Shiny app to access a hospital's FHIR API. Start with smart_discover(), then supply the app registration and requested permissions. The result is an OAuthClient for oauth_module_server() or oauth_connections(), with an approved API named "fhir".

Usage

smart_client(
  discovery,
  client_id,
  redirect_uri,
  scopes,
  required_scopes = scopes,
  launch = c("standalone", "ehr"),
  identity = c("none", "openid", "fhirUser"),
  allow_v1_scopes = FALSE,
  online_access_policy = c("online_only", "allow_offline"),
  token_auth_style = c("public", "header", "private_key_jwt"),
  client_secret = character(),
  client_assertion_private_key = NULL,
  client_assertion_private_key_kid = NULL,
  client_assertion_alg = "RS384",
  authorization_method = "GET",
  response_mode = NULL,
  authorization_server_mode = "single",
  authorization_server_redirect_uris = character(),
  initial_expires_in_fallback = NULL,
  label = "FHIR server",
  state_store = cachem::cache_mem(max_age = 300),
  state_key = random_urlsafe(128),
  state_payload_max_age = 300
)

Arguments

discovery

A plain snapshot returned by smart_discover(). Its metadata and endpoint policy are revalidated locally; this performs no network calls.

client_id, redirect_uri

App registration values. The callback must use HTTPS, except for the snapshot's explicit HTTP loopback development policy.

scopes

Permissions to request, without automatic wildcard/offline access. Standalone patient scopes require launch/patient. EHR clients add launch. online_access requires EHR launch and permission-online; offline_access requires permission-offline in either launch mode. Each resource scope spelling must be advertised through permission-v2 or, for v1 spellings, permission-v1 with allow_v1_scopes = TRUE. A SMART scope comparison supports at most 256 distinct scopes on each side and 64 KiB (65,536 bytes) of combined scope text. Larger comparisons are rejected, including during token acceptance and refresh.

required_scopes

Minimum permissions, defaulting to scopes. Pass a subset to accept reduced grants as limited connections. Identity scopes are always required when identity is enabled. Unsupported comparisons are rejected.

launch

"standalone" or "ehr", matching the registered app flow.

identity

"none" (default), "openid" for a validated OIDC subject, or "fhirUser" to also require the user's FHIR reference. "openid" does not interpret a fhirUser claim or populate smart_context()[["fhirUser"]]. A validated fhirUser claim may be an absolute URL or a supported resource instance reference relative to this client's FHIR base, such as "Practitioner/example" or "Practitioner/example/_history/2". Versioned references retain their version.

allow_v1_scopes

Explicit compatibility flag enabling .read, .write and .*. Requesting these spellings requires permission-v1; requesting v2 spellings requires permission-v2, including when this flag is enabled. Default FALSE.

online_access_policy

"online_only" (default) or "allow_offline". SMART permits an online_access request to negotiate offline_access. Opt in to "allow_offline" to accept that longer-lived permission in place of required online_access. The default rejects this substitution, even when online_access is optional. Explicitly requesting offline_access also authorizes offline persistence. Granted scopes retain their actual spelling; refresh responses cannot escalate an existing online grant.

token_auth_style

Registration type: "public", "header" for a symmetric secret using HTTP Basic, or "private_key_jwt". Selection must agree with advertised capabilities. Confidential methods must also agree with authentication metadata; public clients do not authenticate and need no "none" entry in that metadata. Later property edits must retain a supported SMART token authentication method. Asymmetric assertions require typ = "JWT", a key ID, an explicit RS384/ES384 algorithm, and the token endpoint as their audience.

client_secret

Secret for a symmetric registration; otherwise omit.

client_assertion_private_key, client_assertion_private_key_kid

Private signing key and registered key ID for asymmetric authentication; see oauth_client(). Required for "private_key_jwt".

client_assertion_alg

"RS384" (default) or "ES384"; the key and server metadata must support the selected algorithm. Other styles omit assertions.

authorization_method

"GET" (default) or "POST" for the outgoing browser request. POST requires the discovered authorize-post capability and uses the module's request_login() or prepare_authorization_request(). POST supports longer browser requests but retains the scope comparison limits described under scopes.

response_mode

NULL, "query", or "form_post". Advertised response modes, when present, must allow the selection.

authorization_server_mode, authorization_server_redirect_uris

See oauth_client(). Multiple clients use distinct registered callback routes.

initial_expires_in_fallback

Optional positive lifetime in seconds, supplied by the authorization server out of band for initial access tokens. Used only when an initial response omits expires_in; an explicit response value takes precedence. Default NULL requires the response to include a lifetime.

label

Display label, default "FHIR server".

state_store, state_key, state_payload_max_age

See oauth_client().

Value

An OAuthClient for the module or connection manager. Its resource_bases contains the approved fhir API; its smart property records the selected profile.

Details

launch = "standalone" describes an app opened directly; "ehr" describes one opened from an electronic health record. Each flow has its own app registration. Patient context identifies the chart being viewed; identity separately controls validation of the signed-in user. See vignette("smart-on-fhir", package = "shinyOAuth") (online guide) for a complete app.

Advanced SMART profile

The constructor applies SMART App Launch 2.2 permission rules, S256 PKCE and the exact FHIR base as the authorization request's aud. identity = "openid" requires signed identity-token and nonce checks; "fhirUser" also requires the user's FHIR reference.

Supported flows use direct authorization and query or form POST callbacks. Claims requests, remote authentication-freshness overrides, JAR, PAR, JARM, DPoP and mTLS are not supported by this constructor. The manager establishes a fresh registered transaction for each EHR launch; launch handles are single-use.

Tokens need a positive lifetime. Only an initial response may omit expires_in, and only with an explicit initial_expires_in_fallback. Refresh responses must supply a lifetime. Requests require TLS 1.2 or newer.

Examples

if (FALSE) { # \dontrun{
site <- smart_discover("https://ehr.example/fhir/R4")
client <- smart_client(site, "registered-app", "https://app.example/callback",
  scopes = c("launch/patient", "patient/Patient.r"),
  required_scopes = "patient/Patient.r", token_auth_style = "public")
client@smart[["launch"]]
} # }