Skip to content

Commit

Permalink
When require_auth: Don't listen to non-auth login endpoints if user i…
Browse files Browse the repository at this point in the history
…s not authenticated + minor doc fixes
  • Loading branch information
thohan88 committed Aug 29, 2024
1 parent 52c536e commit 56e097d
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 23 deletions.
13 changes: 7 additions & 6 deletions R/oauth-shiny-app.R
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ oauth_shiny_app <- function(
# This function takes the app object and transforms/decorates it to create a
# new app object. The new app object will wrap the original ui/server with
# authentication logic, so that the original ui/server is not invoked unless
# and until the user has a valid Google token.
# and until the user has an app token from an auth provider if `require_auth`
# is `TRUE`.

check_installed("jose")
check_installed("sodium")
Expand All @@ -119,12 +120,12 @@ oauth_shiny_app <- function(
# request, and either HTML tag objects or a shiny::httpResponse if it
# decided to handle it.
resp <-
# The logout_path revokes all app and client tokens and deletes cookies
# The logout_path revokes all app and access tokens and deletes cookies
handle_oauth_app_logout(req, client_config, logout_path, cookie_name, logout_ui) %||%
# The client logout_path revokes a single client token and deletes cookies
handle_oauth_client_logout(req, client_config) %||%
# The client logout_path revokes a single access token and deletes cookies
handle_oauth_client_logout(req, client_config, require_auth, cookie_name, key) %||%
# The client login_path handles redirection to the specific client
handle_oauth_client_login(req, client_config) %||%
handle_oauth_client_login(req, client_config, require_auth, cookie_name, key) %||%
# Handles callback from oauth client (after login)
handle_oauth_client_callback(req, client_config, require_auth, cookie_name, key, token_validity) %||%
# Handles requests that have good cookies or does not require auth
Expand Down Expand Up @@ -178,7 +179,7 @@ oauth_shiny_app <- function(
#'
#' @description Inferring the correct app url on the server requires some work.
#' This function attempts to guess the correct server url, but may fail outside
#' of tested hosts (´127.0.0.1` and `shinyapps.io`). To be sure, set the
#' of tested hosts (`127.0.0.1` and `shinyapps.io`). To be sure, set the
#' environment variable `HTTR2_OAUTH_APP_URL` explicitly. Logic inspired by
#' [https://github.com/r4ds/shinyslack](r4ds/shinyslack).
#' @param req A request object.
Expand Down
42 changes: 33 additions & 9 deletions R/oauth-shiny-http-handlers.R
Original file line number Diff line number Diff line change
Expand Up @@ -117,12 +117,15 @@ handle_oauth_app_logout <- function(req, client_config, logout_path, cookie, log
#'
#' @param req A `shiny` request object.
#' @param client_config A list of client configurations used for OAuth.
#' @param require_auth Logical, whether authentication is required.
#' @param cookie The name of the cookie where the app's token is stored.
#' @param key A secret key used to encrypt and decrypt tokens.
#'
#' @return An HTTP response object or `NULL` if no client is matched.
#' @keywords internal
handle_oauth_client_login <- function(req, client_config) {
handle_oauth_client_login <- function(req, client_config, require_auth, cookie, key) {
for (client in client_config) {
resp <- handle_oauth_client_login_redirect(req, client)
resp <- handle_oauth_client_login_redirect(req, client, require_auth, cookie, key)
if (!is.null(resp)) {
return(resp)
}
Expand All @@ -131,21 +134,31 @@ handle_oauth_client_login <- function(req, client_config) {

#' Handle OAuth Client Login Redirect
#'
#' This function handles redirection to the OAuth authorization URL for a
#' specific client. It manages PKCE (Proof Key for Code Exchange) if enabled,
#' and sets up necessary cookies before redirecting the user.
#' This function is invoked when the user is redirected to a client login
#' endpoint set by `login_path`. It handles redirection to the OAuth
#' authorization URL for a specific client. It manages state and PKCE (if
#' enabled) by setting cookies when redirecting the user to the OAuth endpoint.
#'
#' @param req A `shiny` request object.
#' @param client A single client configuration object.
#' @param require_auth Logical, whether authentication is required.
#' @param cookie The name of the cookie where the app's token is stored.
#' @param key A secret key used to encrypt and decrypt tokens.
#'
#' @return An HTTP response object or `NULL` if the request path does not match
#' the client's login path.
#' @keywords internal
handle_oauth_client_login_redirect <- function(req, client) {
handle_oauth_client_login_redirect <- function(req, client, require_auth, cookie, key) {
if (sub("^/", "", req$PATH_INFO) != client$login_path) {
return(NULL)
}

# Don't accept request for non-auth login endpoints if user is not authenticated
has_auth <- !is.null(oauth_shiny_get_app_token_from_request(req, cookie, key))
if(require_auth && !has_auth && !client$auth_provider) {
return(NULL)
}

state <- paste0(client$id, base64_url_rand(32))
auth_params <- client$auth_params

Expand Down Expand Up @@ -313,12 +326,15 @@ handle_oauth_client_callback <- function(req, client_config, require_auth, cooki
#'
#' @param req A `shiny` request object.
#' @param client_config A list of client configurations used for OAuth.
#' @param require_auth Logical, whether authentication is required.
#' @param cookie The name of the cookie where the app's token is stored.
#' @param key A secret key used to encrypt and decrypt tokens.
#'
#' @return An HTTP response object or `NULL` if no client is matched.
#' @keywords internal
handle_oauth_client_logout <- function(req, client_config) {
handle_oauth_client_logout <- function(req, client_config, require_auth, cookie, key) {
for (client in client_config) {
resp <- handle_oauth_client_logout_delete_cookies(req, client)
resp <- handle_oauth_client_logout_delete_cookies(req, client, require_auth, cookie, key)
if (!is.null(resp)) {
return(resp)
}
Expand All @@ -332,15 +348,23 @@ handle_oauth_client_logout <- function(req, client_config) {
#'
#' @param req A `shiny` request object.
#' @param client A single client configuration object.
#' @param require_auth Logical, whether authentication is required.
#' @param cookie The name of the cookie where the app's token is stored.
#' @param key A secret key used to encrypt and decrypt tokens.
#'
#' @return An HTTP response object or `NULL` if the request path does not match
#' the client's logout path.
#' @keywords internal
handle_oauth_client_logout_delete_cookies <- function(req, client) {
handle_oauth_client_logout_delete_cookies <- function(req, client, require_auth, cookie, key) {
if (sub("^/", "", req$PATH_INFO) != client$logout_path) {
return(NULL)
}

has_auth <- !is.null(oauth_shiny_get_app_token_from_request(req, cookie, key))
if(require_auth && !has_auth) {
return(NULL)
}

cookies_app <- names(parse_cookies(req))
cookies_match <- cookies_app[grepl(client$client_cookie_name, cookies_app)]
cookies_del <- unlist(lapply(cookies_match, delete_cookie_header, cookie_options()))
Expand Down
8 changes: 7 additions & 1 deletion man/handle_oauth_client_login.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 11 additions & 4 deletions man/handle_oauth_client_login_redirect.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 7 additions & 1 deletion man/handle_oauth_client_logout.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 13 additions & 1 deletion man/handle_oauth_client_logout_delete_cookies.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion man/oauth_shiny_infer_app_url.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 56e097d

Please sign in to comment.