Skip to contents

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. Default FALSE waits and returns the result directly.

introspect

NULL (default) or a logical. After a successful refresh, introspect the new access token when either this argument is TRUE or the client was configured with introspect = TRUE. A per-call FALSE cannot disable a configured client requirement. When enabled, refresh fails if introspection is unsupported, inactive, or missing required introspection_checks. The raw introspection result is not stored separately, but a successful introspection response may backfill token@cnf.

shiny_session

Optional captured Shiny session details for audit events. Normally supplied by the module; leave NULL when 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 token

  • expires_at: Computed from expires_in when provided; otherwise a fallback lifetime set by shinyOAuth.default_expires_in (3600 seconds by default)

  • refresh_token: Updated if the provider rotates it; otherwise preserved

  • id_token: Updated only if the provider returns one (and it validates); otherwise the latest stored ID token is preserved

  • original_id_token: Retained from login for continuity checks, even if intermediate refresh ID tokens omit nonce or auth_time

  • userinfo: Refreshed if userinfo_required = TRUE; otherwise preserved

  • extra_fields: Replaced by the additional parameters in the refresh response, or an empty list if none are returned. Not merged with earlier responses; explicit JSON null values remain named NULL entries.

  • 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 observable cnf, shinyOAuth does not carry forward a prior x5t#S256 thumbprint onto the refreshed token; mTLS sender-constrained state is kept only when the new token or its introspection response supplies fresh cnf

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")
}