Wrap a Shiny UI to enable OAuth 2.0/OIDC form_post callbacks
Source:R/oauth_form_post_ui.R
oauth_form_post_ui.Rdoauth_form_post_ui() enables the OpenID Foundation OAuth 2.0 Form Post
Response Mode for apps that use oauth_module_server(). It wraps your
existing Shiny UI so a provider can POST an authorization response to the
app's redirect URI. The POST body is stored server-side under a short-lived
one-time handle, and the browser is redirected back to the app with only
that opaque handle in the query string.
For most apps, this helper is not needed because the default transport for authorization responses is the query string, which works without this UI wrapper. You only need to use this helper if your provider requires or strongly recommends form_post response mode.
To request form_post response mode from the provider, wrap your UI with this
helper, configure your OAuthClient with response_mode = "form_post", and
ensure the redirect_uri is set to a URL that routes to this UI wrapper
(e.g., the app's root URL or a specific callback path).
This helper handles the plain form_post response mode, where the POST body
contains authorization response parameters such as code, state, error,
and iss. When response_mode = "form_post.jwt", the helper validates the
inbound JARM response, decrypts and validates the enclosed state, and then
stores the accepted callback payload under the same one-time handle so the
main callback flow can resume from a prevalidated POST boundary.
Arguments
- base_ui
Existing Shiny UI object, or a UI function accepting
req.- id
Shiny module id used by
oauth_module_server(). This must match theidargument passed to the server module.- client
OAuthClient object used by
oauth_module_server().- callback_path
Optional URL path to accept POST callbacks on. Defaults to the path component of
client@redirect_uri.
Value
A Shiny UI function. Pass it to shiny::shinyApp() and, for non-root
callback paths, use uiPattern = ".*" so Shiny routes the callback path to
this UI function.
Details
When this wrapper is used, it also injects use_shinyOAuth() automatically
for the wrapped GET UI, so you do not need a separate top-level
use_shinyOAuth() call.
The server-side callback handle is single-use and is rejected if it is older
than the smaller of client@state_payload_max_age and the configured
state_store TTL. The raw POST body and transient handle query parameters
are also bounded by the shinyOAuth.callback_max_form_post_* options
described in the usage vignette.
Examples
if (
# Example requires a local or remote Keycloak realm whose client allows
# http://127.0.0.1:8100/callback as a valid redirect URI.
nzchar(Sys.getenv("KEYCLOAK_BASE_URL")) &&
nzchar(Sys.getenv("KEYCLOAK_REALM")) &&
nzchar(Sys.getenv("KEYCLOAK_CLIENT_ID")) &&
interactive()
) {
library(shiny)
library(shinyOAuth)
provider <- oauth_provider_keycloak(
base_url = Sys.getenv("KEYCLOAK_BASE_URL"),
realm = Sys.getenv("KEYCLOAK_REALM")
)
client <- oauth_client(
provider = provider,
client_id = Sys.getenv("KEYCLOAK_CLIENT_ID"),
client_secret = Sys.getenv("KEYCLOAK_CLIENT_SECRET"),
redirect_uri = "http://127.0.0.1:8100/callback",
scopes = c("openid", "profile", "email"),
response_mode = "form_post"
)
base_ui <- fluidPage(
uiOutput("login")
)
ui <- oauth_form_post_ui(base_ui, id = "auth", client = client)
server <- function(input, output, session) {
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.")
}
})
}
runApp(
shinyApp(ui, server, uiPattern = ".*"),
port = 8100,
launch.browser = FALSE
)
}