Use a refresh token to obtain a new access token without sending the user
through login again. Call this when your application manages token lifetime
itself, for example before continuing API requests with an expiring token.
Assign the returned OAuthToken to keep the updated credentials.
oauth_module_server() can manage refresh during a Shiny session with
refresh_proactively = TRUE when a refresh token is available.
Usage
refresh_token(
client,
token,
async = FALSE,
introspect = NULL,
shiny_session = NULL,
oauth_client = NULL
)Arguments
- client
OAuthClient object
- token
OAuthToken object containing the refresh token
- async
If
TRUE, return a promise resolving to the result. Configure mirai daemons or a future plan first; mirai takes priority. Use a non-sequential future plan to move work outside the main R process. DefaultFALSEwaits and returns the result directly.- introspect
NULL(default) or a logical. After a successful refresh, introspect the new access token when either this argument isTRUEor the client was configured withintrospect = TRUE. A per-callFALSEcannot disable a configured client requirement. When enabled, refresh fails if introspection is unsupported, inactive, or missing requiredintrospection_checks. The raw introspection result is not stored separately, but a successful introspection response may backfilltoken@cnf.- shiny_session
Optional captured Shiny session details for audit events. Normally supplied by the module; leave
NULLwhen calling directly.- oauth_client
Compatibility alias for
client. Supply only one spelling.
Value
An updated OAuthToken object with refreshed credentials.
What changes:
access_token: Always updated to the fresh tokenexpires_at: Computed fromexpires_inwhen provided; otherwise a fallback lifetime set byshinyOAuth.default_expires_in(3600 seconds by default)refresh_token: Updated if the provider rotates it; otherwise preservedid_token: Updated only if the provider returns one (and it validates); otherwise the latest stored ID token is preservedoriginal_id_token: Retained from login for continuity checks, even if intermediate refresh ID tokens omitnonceorauth_timeuserinfo: Refreshed ifuserinfo_required = TRUE; otherwise preservedextra_fields: Replaced by the additional parameters in the refresh response, or an empty list if none are returned. Not merged with earlier responses; explicit JSONnullvalues remain namedNULLentries.initial_extra_fields: Preserved from the initial code exchange. This historical snapshot does not establish current access permissions.cnf: Updated from the token response when present, and may be backfilled from refresh-time introspection when enabled. When the refresh response omits new observablecnf, shinyOAuth does not carry forward a priorx5t#S256thumbprint onto the refreshed token; mTLS sender-constrained state is kept only when the new token or its introspection response supplies freshcnf
Validation failures cause errors: If the provider returns a new ID
token that fails validation (wrong issuer, audience, expired, or subject
mismatch with original), or if userinfo subject doesn't match the new ID
token, the refresh fails with an error. In oauth_module_server(), this
clears the session and sets authenticated = FALSE, unless
indefinite_session = TRUE keeps it with token_stale = TRUE.
Details
The provider may replace the refresh token too; otherwise the old refresh token is kept. Required userinfo is fetched again, and configured client introspection must succeed before the refreshed token is returned.
For ordinary OAuth clients, refresh explicitly requests the token's retained
granted_scopes when known. This preserves prior scope reductions and makes
an omitted response scope refer to that requested set. Tokens without known
scopes omit the request parameter. SMART uses its own original-grant rules.
OIDC refresh responses may omit the ID token, in which case the original is
kept. If a new ID token is returned, an original must be available and the
subject, issuer, and audience must remain consistent, as must auth_time
and nonce when applicable. Full signature and
claim validation runs when id_token_validation = TRUE. Userinfo is checked
against a validated ID token when both are available;
userinfo_id_token_match = TRUE requires that baseline.
Refresh does not establish a new interactive login. Use the module's
reauth_after_seconds argument when a fresh login is required.
A returned ID token must have an iat at or after the refresh request start,
allowing the provider's configured clock leeway and same-second issuance.
Within one R process, overlapping asynchronous calls for the same client and refresh token share one promise and result. Token snapshots, client settings, and validation options must match; conflicting calls fail before dispatch. A synchronous or reentrant call while that refresh is pending raises an error; await the existing promise instead. Separate R processes require coordination by the application. The input token is a value object: store the returned token for subsequent refreshes, and use an application generation check when assigning results after logout or a new login. Completed results are not cached.
Refresh errors carry a non-secret refresh_credential_outcome field:
"not_consumed", "consumed", "possibly_consumed", or "rejected".
Only "not_consumed" permits retrying the input refresh credential. After
any other outcome (including an unavailable worker result), discard that
credential and require a new login. This does not accept unvalidated access
or identity data. The module applies this rule even with indefinite sessions.
Examples
# get_userinfo(), introspect_token(), and refresh_token() are typically
# called by oauth_module_server() according to your provider/client and
# module settings, rather than directly by application code. The module
# also calls revoke_token() during logout when the provider supports it.
# These helpers are exported for custom login flows, on-demand profile or
# token checks, and applications that manage token lifetime themselves.
#
# The examples below require a real token from a completed login.
# Inside a reactive expression in server(), after creating auth with
# oauth_module_server() and confirming auth[["authenticated"]]:
if (interactive()) {
token <- auth[["token"]]
user_info <- get_userinfo(client, token)
# Requires an introspection endpoint. NA means activity is unknown.
result <- introspect_token(client, token)
isTRUE(result[["active"]])
# Requires a refresh token. Keep the returned replacement.
token <- refresh_token(client, token)
# Requires a revocation endpoint to invalidate the token at the provider.
result <- revoke_token(client, token, token_kind = "refresh")
}