Provides a simple, configurable framework for 'OpenID Connect' (OIDC) authentication and 'OAuth 2.0' authorization in 'shiny' applications using 'S7' classes. Defines providers, clients, and tokens, as well as various supporting functions and a 'shiny' module. Features include cross-site request forgery (CSRF) protection, state encryption, 'Proof Key for Code Exchange' (PKCE) handling, validation of OIDC identity tokens (nonces, signatures, claims), automatic user info retrieval for OIDC and supported 'OAuth' providers, asynchronous flows, and hooks for audit logging.
'shinyOAuth' is an R package implementing provider‑agnostic OpenID Connect (OIDC) authentication and OAuth 2.0 authorization for Shiny apps. It is built with modern S7 classes and security in mind.
OAuth 2.0 and OIDC let users log in to your app with accounts they already have (for example, Google or Microsoft), with a self-hosted identity provider such as Keycloak, or with an identity service such as Auth0 or Okta. To achieve this, your app redirects your users to the identity provider, they authenticate there, and are redirected back to your app with an authorization code. Your app then exchanges this code for tokens. In OAuth flows, an access token is obtained to authorize API calls, and you may get the user's profile information from the provider's userinfo endpoint. In OIDC flows, a validated ID token authenticates the user.
This package streamlines this flow for Shiny applications, enabling developers to add OAuth 2.0 and OIDC authorization/authentication to their apps with minimal code. The provided Shiny module handles redirecting unauthenticated users, managing state/PKCE/nonce for secure code-token exchange, verifying OIDC tokens, automatically fetching user info and performing token refresh, using asynchronous execution, and more. The package is highly configurable and works with various providers and protocol features.
Shiny module: oauth_module_server() gives you a ready‑to‑use OIDC authentication and OAuth authorization flow
with secure defaults. Easily read authentication status, token details, & user info as reactive values
in your Shiny server logic
S7 classes: OAuthProvider, OAuthClient, OAuthToken, for a structured representation
of key elements of the OAuth 2.0/OIDC flow
Functions: prepare_call(), handle_callback(), introspect_token(), refresh_token(), and more,
should you wish to manually implement parts of the OAuth 2.0/OIDC flow
Provider helpers: you can configure your own OAuth 2.0/OIDC providers,
but the package also includes an oauth_provider_oidc_discover() function for quick OIDC setup, and
contains built-in configurations for popular providers (e.g., GitHub, Google, Microsoft, Keycloak, Auth0).
Security best practices: AES-GCM–sealed state payloads (AEAD), server-side state validation coupled with
origin-scoped browser binding, HTTPS enforcement, PKCE (S256), ID token signature/claims validation (including nonce),
userinfo subject match, support for DPoP, mTLS, JAR, PAR, and more
(see vignette("authentication-flow", package = "shinyOAuth") (link))
Provides hooks for auditing & logging key events,
like login successes or failures; also supports emitting OpenTelemetry signals
(see vignette("audit-logging", package = "shinyOAuth") (link)
and vignette("opentelemetry", package = "shinyOAuth") (link))
Install from CRAN:
install.packages("shinyOAuth")
Install the development version from GitHub:
if (!requireNamespace("remotes", quietly = TRUE)) {
install.packages("remotes")
}
remotes::install_github("lukakoning/shinyOAuth")
For complete usage documentation (i.e., making a manual login button, making authenticated
API calls, setting various options, and a security checklist) see: vignette("usage", package = "shinyOAuth") (link).
Below is a minimal example using a GitHub OAuth 2.0 app. If you want to try this example yourself, you can register an app at your GitHub Developer Settings.
library(shiny)
library(shinyOAuth)
# GitHub OAuth 2.0 provider has been preconfigured in the package
# - You can quickly configure OIDC providers with `oauth_provider_oidc_discover()`
# - You can manually configure every other provider with `oauth_provider()`
provider <- oauth_provider_github()
# Build client using your app's ID, secret, & redirect URI:
client <- oauth_client(
provider = provider,
client_id = Sys.getenv("GITHUB_OAUTH_CLIENT_ID"),
client_secret = Sys.getenv("GITHUB_OAUTH_CLIENT_SECRET"),
redirect_uri = "http://127.0.0.1:8100",
scopes = c("read:user", "user:email")
)
# Simple UI
ui <- oauth_ui(fluidPage(
# Show login information:
uiOutput("login_information")
), id = "auth", client = client)
# Server which obtains authentication
server <- function(input, output, session) {
# Start authentication module; will automatically redirect unauthenticated users
# to the provider's login page and handle the callback
# Returns reactive values with authentication status, token details, user info,
# etc.
auth <- oauth_module_server("auth", client)
# Render login information:
output[["login_information"]] <- renderUI({
if (auth[["authenticated"]]) {
user_info <- auth[["token"]]@userinfo
tagList(
tags[["p"]]("You are logged in! Your details:"),
tags[["pre"]](paste(capture.output(str(user_info)), collapse = "\n"))
)
} else {
tags[["p"]]("You are not logged in.")
}
})
}
runApp(
shinyApp(ui, server), port = 8100,
launch.browser = FALSE
)
# Open the app in your regular browser at http://127.0.0.1:8100
# (viewers in RStudio/Positron/etc. cannot perform necessary redirects)
The package provides hooks for logging/auditing crucial events (e.g., callbacks issued & received, login success/failures). It can also emit signals via OpenTelemetry.
See vignette("audit-logging", package = "shinyOAuth") (link) for audit event details,
and vignette("opentelemetry", package = "shinyOAuth") (link) for OpenTelemetry details.
For an in-depth step-by-step explanation of what happens during the authentication flow, see:
vignette("authentication-flow", package = "shinyOAuth") (link).
For a checklist of security considerations and best practices for production use, see:
vignette("usage", package = "shinyOAuth") (link).
The package has a standard 'testthat' test suite under tests/testthat/.
An additional set of integration tests against a local Keycloak instance (in Docker/Podman) is provided under integration/keycloak/.
These integration tests also include browser-driven end-to-end tests using 'shinytest2' and 'chromote'.
Finally, minimal demo app deployments are provided under integration/gcp/ for Google Cloud Run and integration/posit/ for Posit Connect Cloud.