A connection lets your Shiny server use the current login to call an API or supply a token to another R package. It checks that the login and permissions are still available, so your app does not need to keep its own token cache.
Details
Inside reactive server code, obtain a connection with
connection <- shiny::req(auth$connection()), where auth comes from
oauth_module_server(). With oauth_connections_server(), select a saved
login using auth$connection(connection_id).
Use $access_token() for another R package or database driver, $request() for
an API in the client's resource_bases, and $has_scopes() to check permissions
for optional app features. $request(refresh = TRUE) can refresh before
sending. See vignette("external-integrations", package = "shinyOAuth")
(online guide)
for a complete example.
Connection lifetime
A connection belongs to one login and one Shiny session. Token refresh keeps the same connection and ID. Logout, replacement or session closure invalidates the old connection; obtain a new one after the next login. A connection manager can retain the underlying login across navigation, but a new session gets its own connection reference.
Methods check the owning Shiny session. $is_usable() returns FALSE when
access is unavailable; request and token methods raise errors.
Advanced lifecycle behavior
Reactive expressions that read connection(), $access_token() or
$has_scopes() do not rerun on token refresh alone if the login and permissions
stay the same. Status reads such as $summary() may update more often. Reads
do not extend a managed owner's inactivity limit. The manager's touch()
method records explicit user activity.
Use the module's connection() for coordinated token acquisition, refresh
and token targets. The oauth_connection() wrapper follows a supplied token
reactive across logins and does not support those features.
Active bindings
idRead-only opaque character string identifying this reference. A single module preserves it across refresh and replaces it on login. A manager uses the stored grant's ID across sessions and refreshes;
oauth_connection()generates an ID lasting only for that reference.
Methods
OAuthConnection$new()
Initialize a reference. This constructor is for internal use;
applications should use a module's connection() method or the legacy
oauth_connection() wrapper to establish session ownership.
Usage
OAuthConnection$new(
id,
client,
resolve,
refresh = NULL,
acquire = NULL,
is_current = NULL
)Arguments
idOpaque character string identifying the reference.
clientThe OAuthClient to bind to this reference.
resolveInternal function with no arguments that enforces session ownership and returns a list with
clientidentical to this reference's client andtokencontaining the current OAuthToken orNULL. It must raise an error when the owning session is unavailable.refreshOptional internal function implementing the owner's coordinated refresh. Legacy wrappers leave this
NULL.acquireInternal coordinated credential refresh for external use, accepting
asyncand returning TRUE or a promise resolving to TRUE.is_currentOptional internal reactive predicate. FALSE permanently ends permission notifications for this authorization reference.
OAuthConnection$access_token()
Get a current bearer token for an SDK or database driver, refreshing when
needed. Obtain this connection from a module's connection() method.
The method refreshes credentials when needed, without starting login,
requesting new permissions or retrying the SDK operation.
Usage
OAuthConnection$access_token(
required_scopes = character(),
target = NULL,
min_valid_for_seconds = 60,
force_refresh = FALSE,
async = FALSE
)Arguments
required_scopesPermissions needed for this operation, in addition to client requirements.
targetOptional API name from
token_targets.NULLselects the client's default. Its token is acquired when needed.min_valid_for_secondsMinimum remaining lifetime in seconds (default 60). Allow time for the operation and clock differences. Unknown or insufficient expiry triggers refresh; an unusable replacement raises an error.
force_refreshRequire a new token even if a cached one is usable.
asyncIf
TRUE, return a promise, including for a cached token. IfFALSE, return a string or raise an error; a synchronous call cannot wait for an already running async refresh.
Details
Errors can indicate an unavailable login, insufficient permission, pending or
failed refresh, or an unsuitable token lifetime. Stable reason names include
authorization_unavailable, insufficient_scope, refresh_pending,
refresh_unavailable, interaction_required, lifetime_unavailable,
unsupported_token_binding, unknown_target and unsupported_target.
See vignette("external-integrations", package = "shinyOAuth")
(online guide)
for error recovery and async examples.
DPoP and mTLS-bound tokens require their signing key or certificate; use
$request() instead of exporting a bearer string. Exported tokens retain all
provider-issued permissions, which may exceed the app's local operation limits.
Recheck the current login and permissions before using an async token snapshot.
OAuthConnection$has_scopes()
Check recorded operation permissions for optional-feature UI, without refreshing or requiring an unexpired access token.
OAuthConnection$is_usable()
Check whether the current token is locally usable. This checks token
presence, known unexpired lifetime and the client's required scopes.
Request-specific scopes are checked by [["request"]]().
OAuthConnection$refresh()
Refresh a connection obtained from either module's connection() method.
Its owner coordinates refresh and verifies ownership before updating it.
References created with oauth_connection() use their existing module's
refresh lifecycle and cannot invoke this method.
Arguments
scopesOptional non-empty character vector requesting fewer permissions for this connection, or
NULL(default). Scopes must be covered by the current grant and client configuration, and retain the client's required scopes. SMART clients use semantic coverage.targetOptional declared token-target name. With targets, narrowing applies only to the selected entry and persists across target switching and reauthorization. Other targets keep their own retained scope limits.
Details
Successful narrowing persists across later automatic refreshes and retained sessions. A new authorization is needed to request broader permissions. It changes the app's accepted permissions, not the provider's underlying consent. Providers may reject narrowing; the request is not retried without the requested scopes.
Microsoft API targets do not support explicit narrowing because their tokens
can include earlier consent. The call fails before sending with reason
unsupported_scope_narrowing, leaving the current login usable. Use a
separate registration or change provider consent for a token with fewer permissions.
OIDC clients that require a profile must retain openid. Include any further
profile permissions in client required_scopes. See
vignette("token-targets", package = "shinyOAuth")
(online guide)
for OIDC consent and provider-specific refresh rules.
OAuthConnection$summary()
Resolve the current connection and return status information without credentials, identity claims or token extension fields. Raises an error when called outside the owning session or after that session closes.
Arguments
targetOptional API name from
token_targets.NULLselects the client's default. Status and expiry describe that API's current token; this read never acquires or refreshes it.
Details
Managed lifecycle states take precedence: refreshing means a refresh
claim is in progress, uncertain requires a new authorization after an
ambiguous refresh outcome, disconnected means local access was removed,
and unavailable means the stored credentials could not be restored.
Otherwise token status is evaluated in this order:
not_acquired: the selected target has no token yet.disconnected: there is no current token.expiry_unknown: the token's expiry is unknown.expired: the token has reached its expiry time.insufficient_scope: the grant lacks a client-required scope.limited: required scopes are covered, but some other requested scopes are absent from the grant.active: all requested scopes are covered.
Scope checks enforce retained target limits and use the token's current
granted_scopes, which may be
assumed or carried forward when an ordinary OAuth provider omits scope
information. SMART clients require explicit evidence and use semantic
coverage for both connection and operation permissions.
See OAuthToken for the distinction from verified scope evidence.
Returns
A named list with the following entries:
connection_id: the reference's character ID.client_label: the client's application-defined character label.status: one of the character values listed in this method's details.expires_at: numeric seconds since the Unix epoch,NA_real_when there is no token or its expiry is unknown, orInffor a non-expiring token.resource_ids: character vector of the client's approved resource IDs.replaces_connection_id: present on managed replacement authorizations; identifies the locally ended connection passed toreauthorize().
OAuthConnection$identity()
Read explicitly selected fields from the authorization's cryptographically validated OIDC identity.
Arguments
claimsCharacter vector of ID-token claim names, defaulting to
c("iss", "sub"). Usecharacter()to select none.userinfoCharacter vector of previously fetched UserInfo field names, defaulting to none. UserInfo must have a
subexactly matching the validated ID token before any requested profile fields are returned.
Details
Available inside the owning session's reactive context for connections with validated OIDC identity.
Ordinary connections require a usable access token with openid currently
granted. Target connections retain their validated identity while the
authorization remains available in the owning session, even when the
primary access token expires or its current scopes no longer include
openid.
These are the last validated identity/profile snapshots; an OAuth refresh can retain earlier ID-token claims.
OAuthConnection$request()
Resolve the current token and perform an authenticated request within a named resource base. The connection must be usable, and its current grant must cover any scopes required for this operation.
Usage
OAuthConnection$request(
resource_id,
path = "",
query = NULL,
method = "GET",
required_scopes = character(),
configure = NULL,
target = NULL,
refresh = FALSE,
min_valid_for_seconds = 60
)Arguments
resource_idSingle character string naming an entry in the client's
resource_bases.pathSingle character string resolved relative to the selected base directory;
""selects the base itself. Absolute and root-relative URLs, including pagination links, must remain within the same approved origin and base path. Dot segments and ambiguous encodings are rejected.queryOptional named list of query parameters, or
NULL.methodSingle HTTP method string, defaulting to
"GET".TRACEandTRACKare rejected by the resource transport.required_scopesCharacter vector of scopes required for this operation, in addition to the client's required scopes. They must have been requested by the client and be covered by the current grant.
character()adds no operation-specific scope check.configureOptional function taking an unauthenticated
httr2::request()and returning it with only body and application headers changed. Usehttr2::req_body_json(),httr2::req_body_form(),httr2::req_body_raw()andhttr2::req_headers(). Set the HTTP method withmethodabove. URL, transport policies, authentication and Host headers cannot be changed. Configuration runs once. Both refresh modes recheck the current authorization afterward, before sending the request.targetOptional declared token-target name. Target clients require an explicit association through that declaration's
resource_ids.refreshIf
TRUE, obtain a usable token before sending the request. DefaultFALSEuses the current token. This is synchronous and cannot wait for an async refresh. Failed API calls are not refreshed or replayed; ordinary HTTP retries are disabled for this call. DPoP nonce handling remains available. The destination andconfigureresult are checked before refresh, and permission is checked again before sending.min_valid_for_secondsMinimum remaining lifetime when
refresh = TRUE, in seconds. Has the same meaning as on$access_token().
Details
Uses perform_resource_req() with the configured client for Bearer, DPoP
and mTLS authentication. Redirects are never followed. Transport error
messages are redacted to exclude resource paths, queries and response
bodies. Scope requirements are supplied by the application; they cannot
be inferred from an arbitrary API's HTTP method and path.
Returns
An httr2 response object. Invalid resources, unusable connections, insufficient scopes and transport failures raise errors.
OAuthConnection$smart_context()
Read interpreted context for a usable SMART connection in this session.
Returns
The sensitive context list documented in smart_context().
OAuthConnection$smart_resource()
Fetch the contextual Patient or validated fhirUser through the approved
FHIR base, using current read permissions. smart_patient() and
smart_fhir_user() are the corresponding public helpers.
Returns
An httr2 response. Missing context, scope or resource binding raises an error before an authenticated request is sent.
OAuthConnection$print()
Print the class name and session-binding description, with credentials redacted.
Arguments
...Unused; accepted for compatibility with
base::print().
Examples
if (FALSE) { # \dontrun{
# Configure outside server(), using an existing provider:
client <- oauth_client(
provider, client_id = "registered-app",
redirect_uri = "https://app.example/callback", scopes = c("read", "write"),
resource_bases = c(api = "https://api.example/v1"),
required_scopes = "read"
)
server <- function(input, output, session) {
auth <- oauth_module_server("auth", client, refresh_proactively = TRUE)
output[["status"]] <- shiny::renderText({
connection <- shiny::req(auth[["connection"]]())
connection[["summary"]]()[["status"]]
})
records <- shiny::reactive({
connection <- shiny::req(auth[["connection"]]())
response <- httr2::request("https://api.example/v1/records") |>
httr2::req_auth_bearer_token(connection[["access_token"]]("read")) |>
httr2::req_perform()
httr2::resp_body_json(response)
})
}
} # }