S7 class representing an OAuth 2.0 client configuration, including a provider, client credentials, redirect URI, requested scopes, and state management.
This is a low-level constructor intended for advanced use. Most users should
prefer the helper constructor oauth_client().
Usage
OAuthClient(
provider = NULL,
client_id = character(0),
client_secret = character(0),
client_private_key = NULL,
client_private_key_kid = NA_character_,
client_assertion_alg = NA_character_,
redirect_uri = character(0),
scopes = character(0),
state_store = cachem::cache_mem(max_age = 300),
state_entropy = 64,
state_key = random_urlsafe(n = 128)
)Arguments
- provider
OAuthProvider object
- client_id
OAuth client ID
- client_secret
OAuth client secret.
Validation rules:
Required (non-empty) when the provider authenticates the client with HTTP Basic auth at the token endpoint (
token_auth_style = "header", also known asclient_secret_basic).Optional for public PKCE-only clients when the provider is configured with
use_pkce = TRUEand uses form-body client authentication at the token endpoint (token_auth_style = "body", also known asclient_secret_post). In this case, the secret is omitted from token requests.
Note: If your provider issues HS256 ID tokens and
id_token_validationis enabled, a non-emptyclient_secretis required for signature validation.- client_private_key
Optional private key for
private_key_jwtclient authentication at the token endpoint. Can be anopenssl::keyor a PEM string containing a private key. Required when the provider'stoken_auth_style = 'private_key_jwt'. Ignored for other auth styles.- client_private_key_kid
Optional key identifier (kid) to include in the JWT header for
private_key_jwtassertions. Useful when the authorization server uses kid to select the correct verification key.- client_assertion_alg
Optional JWT signing algorithm to use for client assertions. When omitted, defaults to
HS256forclient_secret_jwt. Forprivate_key_jwt, a compatible default is selected based on the private key type/curve (e.g.,RS256for RSA,ES256/ES384/ES512for EC P-256/384/521, orEdDSAfor Ed25519/Ed448). If an explicit value is provided but incompatible with the key, validation fails early with a configuration error. Supported values areHS256,HS384,HS512for client_secret_jwt and asymmetric algorithms supported byjose::jwt_encode_sig(e.g.,RS256,PS256,ES256,EdDSA) for private keys.- redirect_uri
Redirect URI registered with provider
- scopes
Vector of scopes to request
- state_store
State storage backend. Defaults to
cachem::cache_mem(max_age = 300). Alternative backends could includecachem::cache_disk()or a custom implementation (which you can create withcustom_cache(). The backend must implement cachem-like methods$get(key, missing),$set(key, value), and$remove(key);$info()is optional.Trade-offs:
cache_memis in-memory and thus scoped to a single R process (good default for a single Shiny process).cache_diskpersists to disk and can be shared across multiple R processes (useful for multi-process deployments or when Shiny workers aren't sticky). Acustom_cache()backend could use a database or external store (e.g., Redis, Memcached). See alsovignette("usage", package = "shinyOAuth").The client automatically generates, persists (in
state_store), and validates the OAuthstateparameter (and OIDCnoncewhen applicable) during the authorization code flow- state_entropy
Integer. The length (in characters) of the randomly generated state parameter. Higher values provide more entropy and better security against CSRF attacks. Must be between 22 and 128 (to align with
validate_state()'s default minimum which targets ~128 bits for base64url‑like strings). Default is 64, which provides approximately 384 bits of entropy- state_key
Optional per-client secret used as the state sealing key for AES-GCM AEAD (authenticated encryption) of the state payload that travels via the
statequery parameter. This provides confidentiality and integrity (via authentication tag) for the embedded data used during callback verification. If you omit this argument, a random value is generated viarandom_urlsafe(128). This key is distinct from the OAuthclient_secretand may be used with public clients.Type: character string (>= 32 bytes when encoded) or raw vector (>= 32 bytes). Raw keys enable direct use of high-entropy secrets from external stores. Both forms are normalized internally by cryptographic helpers.
Multi-process deployments: if your app runs with multiple R workers or behind a non-sticky load balancer, you must configure a shared
state_storeand the samestate_keyacross all workers. Otherwise callbacks that land on a different worker will be unable to decrypt/validate the state envelope and authentication will fail. In such environments, do not rely on the random per-process default: provide an explicit, high-entropy key (for example via a secret store or environment variable). Prefer values with substantial entropy (e.g., 64–128 base64url characters or a raw 32+ byte key). Avoid human‑memorable passphrases. See alsovignette("usage", package = "shinyOAuth").
Examples
if (
# Example requires configured GitHub OAuth 2.0 app
# (go to https://github.com/settings/developers to create one):
nzchar(Sys.getenv("GITHUB_OAUTH_CLIENT_ID"))
&& nzchar(Sys.getenv("GITHUB_OAUTH_CLIENT_SECRET"))
&& interactive()
) {
library(shiny)
library(shinyOAuth)
# Define client
client <- oauth_client(
provider = oauth_provider_github(),
client_id = Sys.getenv("GITHUB_OAUTH_CLIENT_ID"),
client_secret = Sys.getenv("GITHUB_OAUTH_CLIENT_SECRET"),
redirect_uri = "http://127.0.0.1:8100"
)
# Choose which app you want to run
app_to_run <- NULL
while (!isTRUE(app_to_run %in% c(1:4))) {
app_to_run <- readline(
prompt = paste0(
"Which example app do you want to run?\n",
" 1: Auto-redirect login\n",
" 2: Manual login button\n",
" 3: Fetch additional resource with access token\n",
" 4: No app (all will be defined but none run)\n",
"Enter 1, 2, 3, or 4... "
)
)
}
# Example app with auto-redirect (1) -----------------------------------------
ui_1 <- fluidPage(
use_shinyOAuth(),
uiOutput("login")
)
server_1 <- function(input, output, session) {
# Auto-redirect (default):
auth <- oauth_module_server(
"auth",
client,
auto_redirect = TRUE
)
output$login <- renderUI({
if (auth$authenticated) {
user_info <- auth$token@userinfo
tagList(
tags$p("You are logged in!"),
tags$pre(paste(capture.output(str(user_info)), collapse = "\n"))
)
} else {
tags$p("You are not logged in.")
}
})
}
app_1 <- shinyApp(ui_1, server_1)
if (app_to_run == "1") {
runApp(app_1, port = 8100)
}
# Example app with manual login button (2) -----------------------------------
ui_2 <- fluidPage(
use_shinyOAuth(),
actionButton("login_btn", "Login"),
uiOutput("login")
)
server_2 <- function(input, output, session) {
auth <- oauth_module_server(
"auth",
client,
auto_redirect = FALSE
)
observeEvent(input$login_btn, {
auth$request_login()
})
output$login <- renderUI({
if (auth$authenticated) {
user_info <- auth$token@userinfo
tagList(
tags$p("You are logged in!"),
tags$pre(paste(capture.output(str(user_info)), collapse = "\n"))
)
} else {
tags$p("You are not logged in.")
}
})
}
app_2 <- shinyApp(ui_2, server_2)
if (app_to_run == "2") {
runApp(app_2, port = 8100)
}
# Example app requesting additional resource with access token (3) -----------
# Below app shows the authenticated username + their GitHub repositories,
# fetched via GitHub API using the access token obtained during login
ui_3 <- fluidPage(
use_shinyOAuth(),
uiOutput("ui")
)
server_3 <- function(input, output, session) {
auth <- oauth_module_server(
"auth",
client,
auto_redirect = TRUE
)
repositories <- reactiveVal(NULL)
observe({
req(auth$authenticated)
# Example additional API request using the access token
# (e.g., fetch user repositories from GitHub)
req <- client_bearer_req(auth$token, "https://api.github.com/user/repos")
resp <- httr2::req_perform(req)
if (httr2::resp_is_error(resp)) {
repositories(NULL)
} else {
repos_data <- httr2::resp_body_json(resp, simplifyVector = TRUE)
repositories(repos_data)
}
})
# Render username + their repositories
output$ui <- renderUI({
if (isTRUE(auth$authenticated)) {
user_info <- auth$token@userinfo
repos <- repositories()
return(tagList(
tags$p(paste("You are logged in as:", user_info$login)),
tags$h4("Your repositories:"),
if (!is.null(repos)) {
tags$ul(
Map(function(url, name) {
tags$li(tags$a(href = url, target = "_blank", name))
}, repos$html_url, repos$full_name)
)
} else {
tags$p("Loading repositories...")
}
))
}
return(tags$p("You are not logged in."))
})
}
app_3 <- shinyApp(ui_3, server_3)
if (app_to_run == "3") {
runApp(app_3, port = 8100)
}
}