diff --git a/DESCRIPTION b/DESCRIPTION index c6d4ec2..319bdbf 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: googleAuthR Type: Package -Version: 1.4.2 +Version: 2.0.0 Title: Authenticate and Create Google APIs Description: Create R functions that interact with OAuth2 Google APIs easily, diff --git a/NAMESPACE b/NAMESPACE index 5bfa06a..8555c93 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -51,15 +51,10 @@ export(gar_shiny_login_ui) export(gar_shiny_ui) export(gar_token) export(gar_token_info) -export(googleAuth) -export(googleAuthUI) -export(googleAuth_js) -export(googleAuth_jsUI) export(googleSignIn) export(googleSignInUI) export(silent_auth) export(skip_if_no_env_auth) -export(with_shiny) import(assertthat) import(cli) import(memoise) diff --git a/NEWS.md b/NEWS.md index e2714a1..7739433 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,6 @@ -# googleAuthR 1.4.2 +# googleAuthR 2.0.0 -* Removed `with_shiny()` +* Removed `with_shiny()` and `googleAuth` modules that have been deprecated for a while -use `gar_shiny_*` instead. * Improved error handling with custom http errors # googleAuthR 1.4.1 diff --git a/R/batch.R b/R/batch.R index 6231f0b..0fd3ce8 100644 --- a/R/batch.R +++ b/R/batch.R @@ -65,8 +65,7 @@ gar_batch <- function(function_list, parsed <- paste(c(parse_list, "--gar_batch--"), collapse="") - l <- list(parsed = parsed, - shiny_access_token = function_list[[1]]$shiny_access_token) + l <- list(parsed = parsed) ## call doHttrRequest with batched together functions cached_call <- !is.null(gar_cache_get_loc()) diff --git a/R/checks.R b/R/checks.R index 0fbc3c5..7ca44df 100644 --- a/R/checks.R +++ b/R/checks.R @@ -128,8 +128,6 @@ token_exists <- function() { #' Check API data token #' -#' @param shiny_access_token auth token -#' #' @return boolean if it works. #' #' @keywords internal diff --git a/R/gadget.R b/R/gadget.R deleted file mode 100644 index c995d88..0000000 --- a/R/gadget.R +++ /dev/null @@ -1,159 +0,0 @@ -#' Gadget for easy authentication -#' @noRd -gar_gadget <- function(){ - ## No @import to avoid making shiny and miniUI an import - check_package_loaded("shiny") - check_package_loaded("miniUI") - ui <- miniUI::miniPage( - miniUI::gadgetTitleBar("googleAuthR Authentication", right = NULL), - miniUI::miniContentPanel( - shiny::uiOutput("button"), - shiny::selectInput("api", label = "Select API to prefill scopes", choices = NULL), - shiny::uiOutput("scope_selector"), - shiny::div() - ) - ) - - server <- function(input, output, session) { - - returning <- shiny::reactive(authReturnCode(session)) - - output$button <- shiny::renderUI({ - - if(is.null(returning())){ - googleAuthUI("gadget") - } else { - shiny::helpText("Your R session is now authenticated, and you can close this window.") - } - - }) - - output$scope_selector <- shiny::renderUI({ - - if(is.null(returning())){ - out <- shiny::tagList( - shiny::selectInput("scopes_selected", - label = "Current API scopes", - choices = getOption("googleAuthR.scopes.selected"), - selected = getOption("googleAuthR.scopes.selected"), - multiple = TRUE, - width = "100%"), - shiny::selectInput("add_scopes",label = "Add new scopes", width = "100%", choices = NULL, multiple = TRUE), - shiny::actionButton("do_add_scopes","Add scopes", icon = shiny::icon("plus")), - shiny::helpText("Google API scopes are listed ", - shiny::a(href="https://developers.google.com/identity/protocols/googlescopes", - "here", - target="_blank")), - shiny::strong("googleAuthR.client_id"), - shiny::helpText(getOption("googleAuthR.client_id")), - shiny::strong("googleAuthR.webapp.client_id"), - shiny::helpText(getOption("googleAuthR.webapp.client_id")), - shiny::hr(), - shiny::helpText("Ensure above settings match your", - shiny::a(target = "_blank", - href="https://console.developers.google.com/apis/credentials", - "Google console API credentials"), - "for successful authentication.") - ) - } else { - NULL - } - - }) - - ##update token() - shiny::observe({ - - if(is.null(returning())){ - options("googleAuthR.scopes.selected" = input$scopes_selected) - } - - token <- shiny::callModule(googleAuth, - "gadget", - login_text = "Login", - prompt="consent") - - token <- token() - - if(!is.null(token)){ - gar_auth(token) - message("Authentication complete for: ", - paste(getOption("googleAuthR.scopes.selected"), - sep=",", - collapse =" ") - ) - message("You can safely close the browser window.") - shiny::stopApp() - } - - - }) - - #update input$scopes_selected - shiny::observeEvent(input$do_add_scopes, { - shiny::validate( - shiny::need(input$add_scopes,"add_scopes") - ) - - if(is.null(returning())){ - options("googleAuthR.scopes.selected" = input$scopes_selected) - - now_scopes <- getOption("googleAuthR.scopes.selected") - new_scopes <- unlist(strsplit(input$add_scopes, " ")) - - scopes <- c(new_scopes, now_scopes) - - } else { - scopes <- NULL - } - - shiny::updateSelectInput(session, - "scopes_selected", - choices = scopes, - selected = scopes) - - shiny::updateSelectInput(session, - "add_scopes", - choices = c("")) - }) - - apis <- shiny::reactive({ - gar_discovery_apis_list() - }) - - #update API list - shiny::observe({ - shiny::req(apis()) - - apis <- apis() - - choices <- apis$id - names(choices) <- paste(apis$name, apis$version) - - shiny::updateSelectInput(session, - "api", - choices = choices) - - }) - - shiny::observe({ - shiny::req(apis()) - shiny::req(input$api) - - id_obj <- strsplit(input$api, ":")[[1]] - - api_detail <- gar_discovery_api(id_obj[1], version = id_obj[2]) - - scopes <- names(api_detail$auth$oauth2$scopes) - - shiny::updateSelectInput(session, "add_scopes", choices = scopes, selected = scopes) - - }) - - - - } - - viewer <- shiny::dialogViewer("googleAuthR") - shiny::runGadget(ui, server, viewer = viewer) -} \ No newline at end of file diff --git a/R/generator.R b/R/generator.R index f04afd9..9ddbebd 100644 --- a/R/generator.R +++ b/R/generator.R @@ -140,7 +140,6 @@ gar_api_generator <- function(baseURI, if(any(with_gar_batch)){ ## batching req <- list(req_url = req_url, - shiny_access_token = shiny_access_token, http_header = http_header, the_body = the_body, name = digest(c(req_url, the_body))) diff --git a/R/shiny-auth.R b/R/shiny-auth.R deleted file mode 100644 index 69dfb8f..0000000 --- a/R/shiny-auth.R +++ /dev/null @@ -1,171 +0,0 @@ -#' Shiny Google Authorisation [UI Module] -#' -#' UI part of shiny module, use with \link{googleAuth} -#' -#' @param id shiny id -#' -#' @return A shiny UI for logging in -#' -#' @family shiny module functions -#' @export -googleAuthUI <- function(id){ - check_package_loaded("shiny") - ns <- shiny::NS(id) - - shiny::uiOutput(ns("googleAuthUi")) -} - - -#' Shiny Google Authorisation [Server Module] -#' -#' Server part of shiny module, use with \link{googleAuthUI} -#' -#' Call via \code{shiny::callModule(googleAuth, "your_ui_name", login_text = "Login")} -#' -#' In some platforms the URL you are authenticating from will not match the Docker container the script is running in (e.g. shinyapps.io or a kubernetes cluster) - in that case you can manually set it via `options(googleAuthR.redirect = http://your-shiny-url`). In other circumstances the Shiny app should be able to detect this itself. -#' -#' -#' @param input shiny input -#' @param output shiny output -#' @param session shiny session -#' @param login_text What the login text will read on the button -#' @param logout_text What the logout text will read on the button -#' @param login_class The CSS class for the login link -#' @param logout_class The CSS class for the logout link -#' @param access_type Online or offline access for the authentication URL -#' @param prompt What type of consent screen on authentication -#' @param revoke If TRUE a user on logout will need to re-authenticate -#' -#' @return A reactive authentication token -#' -#' @examples -#' -#' \dontrun{ -#' options("googleAuthR.scopes.selected" = -#' c("https://www.googleapis.com/auth/urlshortener")) -#' -#' shorten_url <- function(url){ -#' body = list( -#' longUrl = url -#' ) -#' -#' f <- -#' gar_api_generator("https://www.googleapis.com/urlshortener/v1/url", -#' "POST", -#' data_parse_function = function(x) x$id) -#' -#' f(the_body = body) -#' -#' } -#' -#' server <- function(input, output, session){ -#' -#' ## Create access token and render login button -#' access_token <- callModule(googleAuth, -#' "loginButton", -#' login_text = "Login1") -#' -#' short_url_output <- eventReactive(input$submit, { -#' ## wrap existing function with_shiny -#' ## pass the reactive token in shiny_access_token -#' ## pass other named arguments -#' with_shiny(f = shorten_url, -#' shiny_access_token = access_token(), -#' url=input$url) -#' }) -#' -#' output$short_url <- renderText({ -#' -#' short_url_output() -#' -#' }) -#' -#' } -#' -#' ## ui -#' ui <- fluidPage( -#' googleAuthUI("loginButton"), -#' textInput("url", "Enter URL"), -#' actionButton("submit", "Shorten URL"), -#' textOutput("short_url") -#' ) -#' -#' shinyApp(ui = ui, server = server) -#' } -#' -#' @family shiny module functions -#' @export -googleAuth <- function(input, output, session, - login_text="Login via Google", - logout_text="Logout", - login_class="btn btn-primary", - logout_class="btn btn-default", - access_type = c("online","offline"), - prompt = c("consent", "select_account", "both", "none"), - revoke = FALSE){ - check_package_loaded("shiny") - - access_type <- match.arg(access_type) - prompt <- match.arg(prompt) - ns <- session$ns - - accessToken <- shiny::reactive({ - - ## gets all the parameters in the URL. The auth code should be one of them. - if(!is.null(authReturnCode(session))){ - ## extract the authorization token - app_url <- gar_shiny_getUrl(session) - access_token <- gar_shiny_getToken(authReturnCode(session), app_url) - - access_token - - } else { - NULL - } - }) - - output$googleAuthUi <- shiny::renderUI({ - - - - if(is.null(shiny::isolate(accessToken()))) { - shiny::actionLink(ns("signed_in"), - shiny::a(login_text, - href = gar_shiny_getAuthUrl(gar_shiny_getUrl(session), - access_type = access_type, - prompt = prompt), - class=login_class, - role="button")) - } else { - if(revoke){ - - logout_button <- shiny::actionButton(ns("revoke"), "Revoke Access", - href = gar_shiny_getUrl(session), - class=logout_class, - role="button") - - } else { - logout_button <- shiny::a(logout_text, - href = gar_shiny_getUrl(session), - class=logout_class, - role="button") - } - - logout_button - - } - }) - - shiny::observeEvent(input[[ns("revoke")]], { - - ## GETS the revoke URL for this user's access_token - httr::GET(httr::modify_url("https://accounts.google.com/o/oauth2/revoke", - query = - list(token = - shiny::isolate(access_token)$credentials$access_token))) - myMessage("Revoked access", level=2) - }) - - return(accessToken) - -} \ No newline at end of file diff --git a/R/shiny-js-auth.R b/R/shiny-js-auth.R index ab8e13c..7360358 100644 --- a/R/shiny-js-auth.R +++ b/R/shiny-js-auth.R @@ -1,106 +1,3 @@ -#' Shiny JavaScript Google Authorisation [UI Module] -#' -#' A Javascript Google authorisation flow for Shiny apps. -#' -#' Shiny Module for use with \link{googleAuth_js} -#' -#' @param id Shiny id -#' @param login_class CSS class of login button -#' @param logout_class CSS class of logout button -#' @param login_text Text to show on login button -#' @param logout_text Text to show on logout button -#' @param prompt The type of login screen -#' @param scopes Set the scopes, minimum needs is "email" -#' -#' @return Shiny UI -#' @import assertthat -#' @export -googleAuth_jsUI <- function(id, - login_class = "btn btn-primary", - logout_class = "btn btn-danger", - login_text = "Log In", - logout_text = "Log Out", - prompt = c("consent", "select_account", "both", "none"), - scopes = getOption("googleAuthR.scopes.selected", "email")){ - check_package_loaded("shiny") - prompt <- match.arg(prompt) - - assert_that( - is.string(login_class), - is.string(logout_class), - is.string(login_text), - is.string(logout_text), - !is.null(scopes) - ) - - if(prompt == "both"){ - prompt <- "consent select_account" - } - - approval_prompt_line <- paste0(",\n 'prompt':'",prompt,"'") - - ## No @import to avoid making shiny and miniUI an import - - ns <- shiny::NS(id) - shiny::tagList( - - shiny::tags$script(src='https://apis.google.com/js/auth.js'), - shiny::tags$button(id = ns("login"), onclick="auth();", login_text, class = login_class), - shiny::tags$button(id = ns("logout"), onclick="out();", logout_text, class = logout_class), - load_js_template("js/js-auth.js", - ns("login"), - ns("logout"), - getOption("googleAuthR.webapp.client_id"), - paste(scopes, collapse = " "), - approval_prompt_line, - ns("js_auth_access_token"), - ns("js_auth_token_type"), - ns("js_auth_expires_in")) - ) - -} - - -#' Shiny JavaScript Google Authorisation [Server Module] -#' -#' Shiny Module for use with \link{googleAuth_jsUI} -#' -#' Call via \code{shiny::callModule(googleAuth_js, "your_id")} -#' -#' @param input shiny input -#' @param output shiny output -#' @param session shiny session -#' @param message The message to show when not authenticated -#' -#' @return A httr reactive OAuth2.0 token -#' @export -googleAuth_js <- function(input, output, session, message = "Authenticate with your Google account"){ - check_package_loaded("shiny") - js_token <- shiny::reactive({ - shiny::validate( - shiny::need(input$js_auth_access_token, message) - ) - - list(access_token = input$js_auth_access_token, - token_type = input$js_auth_token_type, - expires_in = input$js_auth_expires_in - ) - - }) - - ## Create access token - access_token <- shiny::reactive({ - - shiny::req(js_token()) - - gar_js_getToken(js_token()) - - }) - - return(access_token) - -} - #' Create a httr token from a js token #' @keywords internal #' @noRd diff --git a/R/shiny.R b/R/shiny.R index aab30c1..079b812 100644 --- a/R/shiny.R +++ b/R/shiny.R @@ -165,90 +165,3 @@ gar_shiny_getToken <- function(code, cache_path = FALSE) } - - - - -#' Turn a googleAuthR data fetch function into a Shiny compatible one -#' -#' @param f A function generated by \code{googleAuth_fetch_generator}. -#' @param shiny_access_token A reactive object that resolves to a token. -#' @param ... Other arguments passed to f. -#' @return the function f with an extra parameter, shiny_access_token=NULL. -#' @family shiny auth functions -#' @export -#' -#' @examples -#' \dontrun{ -#' ## in global.R -#' -#' ## create the API call function, example with goo.gl URL shortner -#' library(googleAuthR) -#' options("googleAuthR.scopes.selected" = c("https://www.googleapis.com/auth/urlshortener")) -#' -#' shorten_url <- function(url){ -#' -#' body = list( -#' longUrl = url -#' ) -#' -#' f <- gar_api_generator("https://www.googleapis.com/urlshortener/v1/url", -#' "POST", -#' data_parse_function = function(x) x$id) -#' -#' f(the_body = body) -#' -#' } -#' -#' -#' ## in server.R -#' library(shiny) -#' library(googleAuthR) -#' source('global.R') -#' -#' shinyServer(function(input, output, session)){ -#' -#' ## Get auth code from return URL -#' access_token <- reactiveAccessToken(session) -#' -#' ## Make a loginButton to display using loginOutput -#' output$loginButton <- renderLogin(session, access_token()) -#' -#' short_url_output <- eventReactive(input$submit, { -#' ## wrap existing function with_shiny -#' ## pass the reactive token in shiny_access_token -#' ## pass other named arguments -#' short_url <- with_shiny(f = shorten_url, -#' shiny_access_token = access_token(), -#' url=input$url) -#' -#' }) -#' -#' output$short_url <- renderText({ -#' -#' short_url_output() -#' -#' }) -#' } -#' -#' ## in ui.R -#' library(shiny) -#' library(googleAuthR) -#' -#' shinyUI( -#' fluidPage( -#' loginOutput("loginButton"), -#' textInput("url", "Enter URL"), -#' actionButton("submit", "Shorten URL"), -#' textOutput("short_url") -#' )) -#' } -with_shiny <- function(f, shiny_access_token=NULL, ...){ - if(is.null(shiny_access_token)) - stop("Need to provide the reactive access token in shiny_access_token argument. - e.g. shiny_access_token=access_token()") - - formals(f) <- c(formals(f), list(shiny_access_token=shiny_access_token)) - - f(...) -} diff --git a/cloud_build/build.R b/cloud_build/build.R index 3a7f31a..24103a7 100644 --- a/cloud_build/build.R +++ b/cloud_build/build.R @@ -18,7 +18,7 @@ make_dep_build <- function( bs <- cr_buildstep_r(r_cmd, name = "gcr.io/gcer-public/packagetools") yml <- cr_build_yaml(bs) - build <- cr_build_make(yml) + build <- cr_build_make(yml, timeout = 3600) cr_buildtrigger(build, name = "revdepcheck-googleAuthR", diff --git a/inst/js_auth_demo/app.R b/inst/js_auth_demo/app.R deleted file mode 100644 index 1d261ef..0000000 --- a/inst/js_auth_demo/app.R +++ /dev/null @@ -1,50 +0,0 @@ -library(shiny) -library(googleAuthR) - -gar_set_client() - -## ui.R -ui <- fluidPage( - googleAuth_jsUI("js_token"), - textInput("url", "Enter URL"), - actionButton("submit", "Shorten URL"), - textOutput("short_url") -) - -shorten_url <- function(url){ - - body = list( - longUrl = url - ) - - f <- gar_api_generator("https://www.googleapis.com/urlshortener/v1/url", - "POST", - data_parse_function = function(x) x$id) - - f(the_body = body) - -} - -## server.R -server <- function(input, output, session){ - - access_token <- callModule(googleAuth_js, "js_token") - - short_url_output <- eventReactive(input$submit, { - ## wrap existing function with_shiny - ## pass the reactive token in shiny_access_token - ## pass other named arguments - with_shiny(f = shorten_url, - shiny_access_token = access_token(), - url=input$url) - - }) - - output$short_url <- renderText({ - - short_url_output() - - }) -} - -shinyApp(ui, server) diff --git a/man/checkTokenAPI.Rd b/man/checkTokenAPI.Rd index a679276..312393d 100644 --- a/man/checkTokenAPI.Rd +++ b/man/checkTokenAPI.Rd @@ -6,9 +6,6 @@ \usage{ checkTokenAPI() } -\arguments{ -\item{shiny_access_token}{auth token} -} \value{ boolean if it works. } diff --git a/man/googleAuth.Rd b/man/googleAuth.Rd deleted file mode 100644 index bce08f2..0000000 --- a/man/googleAuth.Rd +++ /dev/null @@ -1,112 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/shiny-auth.R -\name{googleAuth} -\alias{googleAuth} -\title{Shiny Google Authorisation [Server Module]} -\usage{ -googleAuth( - input, - output, - session, - login_text = "Login via Google", - logout_text = "Logout", - login_class = "btn btn-primary", - logout_class = "btn btn-default", - access_type = c("online", "offline"), - prompt = c("consent", "select_account", "both", "none"), - revoke = FALSE -) -} -\arguments{ -\item{input}{shiny input} - -\item{output}{shiny output} - -\item{session}{shiny session} - -\item{login_text}{What the login text will read on the button} - -\item{logout_text}{What the logout text will read on the button} - -\item{login_class}{The CSS class for the login link} - -\item{logout_class}{The CSS class for the logout link} - -\item{access_type}{Online or offline access for the authentication URL} - -\item{prompt}{What type of consent screen on authentication} - -\item{revoke}{If TRUE a user on logout will need to re-authenticate} -} -\value{ -A reactive authentication token -} -\description{ -Server part of shiny module, use with \link{googleAuthUI} -} -\details{ -Call via \code{shiny::callModule(googleAuth, "your_ui_name", login_text = "Login")} - -In some platforms the URL you are authenticating from will not match the Docker container the script is running in (e.g. shinyapps.io or a kubernetes cluster) - in that case you can manually set it via `options(googleAuthR.redirect = http://your-shiny-url`). In other circumstances the Shiny app should be able to detect this itself. -} -\examples{ - -\dontrun{ -options("googleAuthR.scopes.selected" = - c("https://www.googleapis.com/auth/urlshortener")) - -shorten_url <- function(url){ - body = list( - longUrl = url - ) - - f <- - gar_api_generator("https://www.googleapis.com/urlshortener/v1/url", - "POST", - data_parse_function = function(x) x$id) - - f(the_body = body) - - } - -server <- function(input, output, session){ - - ## Create access token and render login button - access_token <- callModule(googleAuth, - "loginButton", - login_text = "Login1") - - short_url_output <- eventReactive(input$submit, { - ## wrap existing function with_shiny - ## pass the reactive token in shiny_access_token - ## pass other named arguments - with_shiny(f = shorten_url, - shiny_access_token = access_token(), - url=input$url) - }) - - output$short_url <- renderText({ - - short_url_output() - - }) - -} - -## ui -ui <- fluidPage( - googleAuthUI("loginButton"), - textInput("url", "Enter URL"), - actionButton("submit", "Shorten URL"), - textOutput("short_url") -) - -shinyApp(ui = ui, server = server) -} - -} -\seealso{ -Other shiny module functions: -\code{\link{googleAuthUI}()} -} -\concept{shiny module functions} diff --git a/man/googleAuthUI.Rd b/man/googleAuthUI.Rd deleted file mode 100644 index 746f8b2..0000000 --- a/man/googleAuthUI.Rd +++ /dev/null @@ -1,22 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/shiny-auth.R -\name{googleAuthUI} -\alias{googleAuthUI} -\title{Shiny Google Authorisation [UI Module]} -\usage{ -googleAuthUI(id) -} -\arguments{ -\item{id}{shiny id} -} -\value{ -A shiny UI for logging in -} -\description{ -UI part of shiny module, use with \link{googleAuth} -} -\seealso{ -Other shiny module functions: -\code{\link{googleAuth}()} -} -\concept{shiny module functions} diff --git a/man/googleAuth_js.Rd b/man/googleAuth_js.Rd deleted file mode 100644 index e91f9eb..0000000 --- a/man/googleAuth_js.Rd +++ /dev/null @@ -1,31 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/shiny-js-auth.R -\name{googleAuth_js} -\alias{googleAuth_js} -\title{Shiny JavaScript Google Authorisation [Server Module]} -\usage{ -googleAuth_js( - input, - output, - session, - message = "Authenticate with your Google account" -) -} -\arguments{ -\item{input}{shiny input} - -\item{output}{shiny output} - -\item{session}{shiny session} - -\item{message}{The message to show when not authenticated} -} -\value{ -A httr reactive OAuth2.0 token -} -\description{ -Shiny Module for use with \link{googleAuth_jsUI} -} -\details{ -Call via \code{shiny::callModule(googleAuth_js, "your_id")} -} diff --git a/man/googleAuth_jsUI.Rd b/man/googleAuth_jsUI.Rd deleted file mode 100644 index c1020b4..0000000 --- a/man/googleAuth_jsUI.Rd +++ /dev/null @@ -1,40 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/shiny-js-auth.R -\name{googleAuth_jsUI} -\alias{googleAuth_jsUI} -\title{Shiny JavaScript Google Authorisation [UI Module]} -\usage{ -googleAuth_jsUI( - id, - login_class = "btn btn-primary", - logout_class = "btn btn-danger", - login_text = "Log In", - logout_text = "Log Out", - prompt = c("consent", "select_account", "both", "none"), - scopes = getOption("googleAuthR.scopes.selected", "email") -) -} -\arguments{ -\item{id}{Shiny id} - -\item{login_class}{CSS class of login button} - -\item{logout_class}{CSS class of logout button} - -\item{login_text}{Text to show on login button} - -\item{logout_text}{Text to show on logout button} - -\item{prompt}{The type of login screen} - -\item{scopes}{Set the scopes, minimum needs is "email"} -} -\value{ -Shiny UI -} -\description{ -A Javascript Google authorisation flow for Shiny apps. -} -\details{ -Shiny Module for use with \link{googleAuth_js} -} diff --git a/man/with_shiny.Rd b/man/with_shiny.Rd deleted file mode 100644 index 61a5658..0000000 --- a/man/with_shiny.Rd +++ /dev/null @@ -1,88 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/shiny.R -\name{with_shiny} -\alias{with_shiny} -\title{Turn a googleAuthR data fetch function into a Shiny compatible one} -\usage{ -with_shiny(f, shiny_access_token = NULL, ...) -} -\arguments{ -\item{f}{A function generated by \code{googleAuth_fetch_generator}.} - -\item{shiny_access_token}{A reactive object that resolves to a token.} - -\item{...}{Other arguments passed to f.} -} -\value{ -the function f with an extra parameter, shiny_access_token=NULL. -} -\description{ -Turn a googleAuthR data fetch function into a Shiny compatible one -} -\examples{ -\dontrun{ -## in global.R - -## create the API call function, example with goo.gl URL shortner -library(googleAuthR) -options("googleAuthR.scopes.selected" = c("https://www.googleapis.com/auth/urlshortener")) - -shorten_url <- function(url){ - - body = list( - longUrl = url - ) - - f <- gar_api_generator("https://www.googleapis.com/urlshortener/v1/url", - "POST", - data_parse_function = function(x) x$id) - - f(the_body = body) - - } - - -## in server.R -library(shiny) -library(googleAuthR) -source('global.R') - -shinyServer(function(input, output, session)){ - - ## Get auth code from return URL - access_token <- reactiveAccessToken(session) - - ## Make a loginButton to display using loginOutput - output$loginButton <- renderLogin(session, access_token()) - - short_url_output <- eventReactive(input$submit, { - ## wrap existing function with_shiny - ## pass the reactive token in shiny_access_token - ## pass other named arguments - short_url <- with_shiny(f = shorten_url, - shiny_access_token = access_token(), - url=input$url) - - }) - - output$short_url <- renderText({ - - short_url_output() - - }) - } - -## in ui.R -library(shiny) -library(googleAuthR) - -shinyUI( - fluidPage( - loginOutput("loginButton"), - textInput("url", "Enter URL"), - actionButton("submit", "Shorten URL"), - textOutput("short_url") - )) -} -} -\concept{shiny auth functions} diff --git a/vignettes/google-authentication-types.Rmd b/vignettes/google-authentication-types.Rmd index a130836..981654a 100644 --- a/vignettes/google-authentication-types.Rmd +++ b/vignettes/google-authentication-types.Rmd @@ -9,7 +9,15 @@ vignette: > %\VignetteEncoding{UTF-8} --- -## Quick user based authentication +# Other more modern libraries + +`googleAuthR` was one of my first R packages and has enjoyed 7+ years of being my key workhorse for Google authentication, but in the meantime more modern packages have been released that may be more suited to your needs - consider [`gargle`](https://gargle.r-lib.org/) (that `googleAuthR` heavily depends upon now for authentication rather than its own home spun functions) and [`firebase`](https://firebase.john-coene.com/) for alternatives. The roles/functionality of `googleAuthR` aside authentication such as batching, package creation etc. will eventually be also superseded by smaller packages. However, `googleAuthR` is still in active use and will be supported in a maintenance mode. + +## Version 2.0 + +Version 2.0 removed `googleAuthR` shiny modules in favour of `gar_shiny_*`. If you need those older legacy functions depend on `googleAuthR == 1.4.1` or before. + +# Quick user based authentication Once setup, then you should go through the Google login flow in your browser when you run this command: @@ -35,7 +43,7 @@ gar_auth(email = "your@email.com") These functions are usually wrapped in package specific functions when used in other packages, such as `googleAnalyticsR::ga_auth()` -## Client options +# Client options Most libraries will set the appropriate options for you, otherwise you will need to supply them from the Google Cloud console, in its `APIs & services > Credentials` section ( `https://console.cloud.google.com/apis/credentials` ). @@ -47,9 +55,9 @@ You will need as a minimum: If creating your own library you can choose to supply some or all of the above to the end-user, as an end-user you may need to set some of the above (most usually your own user authentication). -## Multiple authentication tokens +# Multiple authentication tokens -### googleAuthR > 1.0.0 +## googleAuthR > 1.0.0 Authentication cache tokens are kept at a global level on your computer. When you authenticate the first time with a new client.id, scope or email then you will go through the authentication process in the browser, however the next time it wil be cached and be a lot quicker. @@ -68,62 +76,7 @@ gar_auth(email = "your@email.com", ``` -### Legacy flows - -> Applicable before `googleAuthR < 1.0.0` - -If you supply a filename to `googleAuthR::gar_auth(token = "filename")` then it will save the token there. If it doesn't exist, it will make a new one, if it does exist it will attempt to read the token from that file. Relative and absolute filenames work. - -You can use different token names to save different authentication settings such as with different scopes and client Ids. - -An example switching between `googleAnalyticsR` and `searchConsoleR` authentication, assuming you have previously authenticated with two tokens, one name `ga.httr-oauth` and one named `sc.httr-oauth` - -```r -library(googleAuthR) -library(googleAnalyticsR) -library(searchConsoleR) - -# start with google analytics auth -gar_auth("ga.httr-oauth") - -# can run Google Analytics API calls: -ga_account_list() - -# switch to Seach Console auth -gar_auth("sc.httr-oauth") - -# can now run Search Console API calls: -list_websites() -``` - -Alternatively, you can authenticate with both API services in the same token by specifying the scopes for the request - this determines what permission screen you get the first time you go through the OAuth2 flow. - -You can access the scopes you required via the `googleAuthR` RStudio plugin. - -```r -library(googleAuthR) -library(googleAnalyticsR) -library(searchConsoleR) - -# set the scopes required -options(googleAuthR.scopes.selected = c("https://www.googleapis.com/auth/analytics", - "https://www.googleapis.com/auth/webmasters")) - -# you may also set the client id and secret here as well -options(googleAuthR.client_id = "XXXXXXX", - googleAuthR.client_secret = "XXXXXX") - -# authenticate and go through the OAuth2 flow first time - specify a filename to save to by passing it in -gar_auth(token = "sc_ga.httr-oauth") - -# can run Google Analytics API calls: -ga_account_list() - -# and run Search Console API calls: -list_websites() -``` - -## Setting the client via Google Cloud client JSON +# Setting the client via Google Cloud client JSON To avoid keeping track of which client_id/secret to use, Google offers a client ID JSON file you can download from the Google Cloud console here - `https://console.cloud.google.com/apis/credentials`. Make sure the client ID type is `Desktop` for desktop applications. @@ -164,11 +117,11 @@ Then you just need to supply the scopes: gar_set_client(scopes = "https://www.googleapis.com/auth/webmasters") ``` -## Authentication with no browser +# Authentication with no browser Refer to [this gargle article](https://gargle.r-lib.org/articles/non-interactive-auth.html) on how to authenticate in a non-interactive manner -## Authentication with a JSON file via Service Accounts +# Authentication with a JSON file via Service Accounts You can also authenticate single users via a server side JSON file rather than going through the online OAuth2 flow. The end user could supply this JSON file, or you can upload your own JSON file to your applications. This is generally more secure if you know its only one user on the service, such as for Cloud services. @@ -176,10 +129,14 @@ This involves downloading a secret JSON key with the authentication details. Mo To use, go to your Project in the Google Developement Console and select JSON Key type. Save the JSON file to your computer and supply the file location to the function `gar_auth_service()` + +## Roles + +Roles all start with `roles/*` e.g. `roles/editor` - a list of [predefined roles are here](https://cloud.google.com/iam/docs/understanding-roles#predefined_roles) or you can see [roles within your GCP console here](https://console.cloud.google.com/iam-admin/roles/details/roles). -### Creating service account and a key +## Creating service account and a key -#### From R +### From R The `gar_service_create()` and related functions let you create service accounts from a user OAuth2 login. The user requires permission `iam.serviceAccounts.create` for the project. Most often the user is an Owner/Editor. @@ -194,12 +151,7 @@ See this related [Google help article or creating service accounts](https://clou The above workflow is encapsulated within `gar_service_provision()` which will run through them for you if you supply it with your GCP projects Client Id (another JSON file that identifies your project.) -#### Roles - -Roles all start with `roles/*` e.g. `roles/editor` - a list of [predefined roles are here](https://cloud.google.com/iam/docs/understanding-roles#predefined_roles) or you can see [roles within your GCP console here](https://console.cloud.google.com/iam-admin/roles/details/roles). - - -#### WebUI +### WebUI Navigate to the JSON file from the Google Developer Console via: `Credentials > New credentials > Service account Key > Select service account > Key type = JSON` @@ -248,36 +200,34 @@ list_websites() ``` -## Authentication within Shiny +# Authentication within Shiny If you want to create a Shiny app just using your data, refer to the [non-interactive authentication article on gargle](https://gargle.r-lib.org/articles/non-interactive-auth.html) If you want to make a multi-user Shiny app, where users login to their own Google account and the app works with their data, `googleAuthR` provides the below functions to help make the Google login process as easy as possible. -### Types of Shiny Authentication +## Types of Shiny Authentication There are now these types of logins available, which suit different needs: * `gar_shiny_*` functions. These create a login UI before the main Shiny UI loads. Authentication occurs, and then the main UI loads but with the created unique user's authentication. You can then use `httr` based Google authentication functions normally as you would offline. -* `googleAuth` module - this creates a reactive server side token object for your API calls. You need to wrap your `googleAuthR` functions with `with_shiny()` to pass the reactive token. -* `googleAuth_js` module - this creates a client side token object via JavaScript, that you can then pass to your API calls. You need to wrap your `googleAuthR` functions with `with_shiny()` to pass the reactive token. * `googleSignIn` module - this is for when you just want to have a login, but do not need to make API calls. It is a lightweight JavaScript based sign in solution. -### Shiny Modules +## Shiny Modules `googleAuthR` uses [Shiny Modules](https://shiny.rstudio.com/articles/modules.html). This means less code and the ability to have multiple login buttons on the same app. To use modules, you need to use the functions ending with `_UI` in your ui.R, then call the id you set there server side with the `callModule(moduleName, "id")` syntax. See the examples below. -### Shiny Authentication Examples +## Shiny Authentication Examples Remember that client IDs and secrets will need to be created for the examples below. You need to pick a clientID for *web applications*, not *"Other"* as is used for offline `googleAuthR` functions. -#### URL redirects +### URL redirects In some platforms the URL you are authenticating from will not match the Docker container the script is running in (e.g. shinyapps.io or a kubernetes cluster) - in that case you can manually set it via `options(googleAuthR.redirect = http://your-shiny-url.com`). In other circumstances the Shiny app should be able to detect this itself. -#### `gar_shiny_*` functions example +### `gar_shiny_*` functions example This uses the most modern `gar_shiny_*` family of functions to create authentication. The app lists the files you have stored in Google Drive. @@ -319,144 +269,7 @@ server <- function(input, output, session){ shinyApp(gar_shiny_ui(ui, login_ui = gar_shiny_login_ui), server) ``` -#### `googleAuth` module example - -The Google Cloud project needs to be setup to accept the URL and port of your app (see setup pages). You can fix the port via `options(shiny.port=1221)`, and then make sure if you launch your app locally to change the ip address from 127.0.0.1 to localhost in your browser (Google doesn't accept ip addresses). - -```r -## in global.R -library(googleAuthR) -library(shiny) -options(googleAuthR.scopes.selected = "https://www.googleapis.com/auth/urlshortener") -options(googleAuthR.webapp.client_id = "YOUR_PROJECT_KEY") -options(googleAuthR.webapp.client_secret = "YOUR_CLIENT_SECRET") -shorten_url <- function(url){ - - body = list( - longUrl = url - ) - - f <- gar_api_generator("https://www.googleapis.com/urlshortener/v1/url", - "POST", - data_parse_function = function(x) x$id) - - f(the_body = body) - -} -## server.R -source("global.R") -server <- function(input, output, session){ - - ## Create access token and render login button - access_token <- callModule(googleAuth, "loginButton") - - short_url_output <- eventReactive(input$submit, { - ## wrap existing function with_shiny - ## pass the reactive token in shiny_access_token - ## pass other named arguments - with_shiny(f = shorten_url, - shiny_access_token = access_token(), - url=input$url) - - }) - - output$short_url <- renderText({ - - short_url_output() - - }) -} -## ui.R -ui <- fluidPage( - googleAuthUI("loginButton"), - textInput("url", "Enter URL"), - actionButton("submit", "Shorten URL"), - textOutput("short_url") -) -### If the above global.R, server.R and ui.R files are in folder "test" like so: -## /home -## |->/test/ -## /global.R -## /ui.R -## /server.R -## -## Port 1221 has been set in your Google Project options as the port to listen to -## as explained in authentication setup section -## run below in /home directory -shiny::runApp("./test/", launch.browser=T, port=1221) -``` - -By default the logout button causes a disconnect form the server, but you can use `shinyjs` to improve the user experience via this bit of code: - -```r -observe({ - if (rv$login) { - shinyjs::onclick("gauth_login-googleAuthUi", - shinyjs::runjs("window.location.href = 'https://yourdomain.shinyapps.io/appName';")) - } -}) -``` - -See this post on [creating a Shiny App with a Google login](https://lesliemyint.wordpress.com/2017/01/01/creating-a-shiny-app-with-google-login/) for details. - -#### `googleAuth_js` module example - -The Google Cloud project needs to be setup to accept JavaScript origin of the URL and port of your app ((see setup pages))note this is different from server-side configurations above). Make sure if you launch your app locally to change the ip address from 127.0.0.1 to localhost in your browser (Google doesn't accept ip addresses). - -```r -library(shiny) -library(googleAuthR) - -gar_set_client() - -## ui.R -ui <- fluidPage( - googleAuth_jsUI("js_token"), - textInput("url", "Enter URL"), - actionButton("submit", "Shorten URL"), - textOutput("short_url") -) - -shorten_url <- function(url){ - - body = list( - longUrl = url - ) - - f <- gar_api_generator("https://www.googleapis.com/urlshortener/v1/url", - "POST", - data_parse_function = function(x) x$id) - - f(the_body = body) - -} - -## server.R -server <- function(input, output, session){ - - access_token <- callModule(googleAuth_js, "js_token") - - short_url_output <- eventReactive(input$submit, { - ## wrap existing function with_shiny - ## pass the reactive token in shiny_access_token - ## pass other named arguments - with_shiny(f = shorten_url, - shiny_access_token = access_token(), - url=input$url) - - }) - - output$short_url <- renderText({ - - short_url_output() - - }) -} - -shinyApp(ui, server) -``` - -#### `googleSignIn` module example +### `googleSignIn` module example This module is suitable if you don't need to authenticate APIs in your app, you just would like a login. You can then reach the user email, id, name or avatar to decide which content you want to show with durther logic within your Shiny app. @@ -499,85 +312,7 @@ server <- function(input, output, session) { shinyApp(ui = ui, server = server) ``` - -## Authentication via RStudio Addin - -An RStudio Addin is available via the RStudio Addin menu once you load the package. - -It lets you set the scopes and then saves you some typing by calling the Google authentication flow for you. - -## Authentication in RMarkdown via JavaScript - -There are two functions that can be called from within RMarkdown for authentication. They use JavaScript, rather than R/Shiny to authenticate, as an RMarkdown document can not read the URL tokens. - -A demo and example are available here: `https://mark.shinyapps.io/googleAuthRMarkdown/` - -### RMarkdown authentication - Setup - -The RMarkdown document YAML needs runtime shiny and to be a HTML document: - -``` -output: html_document -runtime: shiny -``` - -Locally, you have to run the RMarkdown document on the specified port configured in Google console (`1221` for the default shared project of `googleAuthR`), configured via `options(shiny.port = 1221)` - -This means you shouldn’t launch the RMarkdown via the Run button in RStudio as that starts a new R session without your set options. - -Instead set the options and run via `rmarkdown::run("myfile.Rmd")` - -```r -options(shiny.port = 1221) -options(googleAuthR.scopes.selected = "https://www.googleapis.com/auth/plus.me") -rmarkdown::run("googleAuthRMarkdown.Rmd") -``` - -When publishing, you also need to add the domain to the Javascript origins in the Google API console. Use `127.0.0.1:XXX` where XXX is your chosen Shiny port for local testing. - -### Example of RMarkdown authentication - -Below creates a button that when clicked makes a popup for Google authentication: - -```r -library(googleAuthR) - -googleAuth_jsUI("auth_demo", login_text = "Click Me") - -``` -The authentication token is available via the server side module command: - -``` -auth <- callModule(googleAuth_js, "auth_demo") -``` -Pass the auth token to API functions. Below example using googleID to return G+ user info. -``` -# devtools::install_github("MarkEdmondson1234/googleID") -library(googleID) - -user_info <- reactive({ - - req(auth()) - - with_shiny(get_user_info, - shiny_access_token = auth()) - -}) -``` -You can now output the user data taken from the G+ API: - -``` -## creates an output -renderUI({ - - req(user_info()) - - h1("Hello ", user_info()$displayName) - -}) -``` - -## Auto-authentication +# Auto-authentication Auto-authentication can be performed upon a package load. @@ -604,9 +339,9 @@ GCS_AUTH_FILE="/Users/mark/auth/my_auth_file.json" ``` -## Revoking Authentication +# Revoking Authentication -For local use, call `gar_deauth()` to unauthentiucate a session. To avoid cache tokens being reused delete them from the gargle cache folder, usually `~/.R/gargle/gargle-oauth/` +For local use, call `gar_deauth()` to de-authenticate a session. To avoid cache tokens being reused delete them from the gargle cache folder, usually `~/.R/gargle/gargle-oauth/` For service level accounts delete the JSON file. diff --git a/vignettes/google-authentication-types.html b/vignettes/google-authentication-types.html index f565625..5666df0 100644 --- a/vignettes/google-authentication-types.html +++ b/vignettes/google-authentication-types.html @@ -12,7 +12,7 @@ - + Google authentication types for R @@ -141,12 +141,20 @@

Google authentication types for R

Mark Edmondson

-

2021-12-17

+

2022-01-28

-
-

Quick user based authentication

+
+

Other more modern libraries

+

googleAuthR was one of my first R packages and has enjoyed 7+ years of being my key workhorse for Google authentication, but in the meantime more modern packages have been released that may be more suited to your needs - consider gargle (that googleAuthR heavily depends upon now for authentication rather than its own home spun functions) and firebase for alternatives. The roles/functionality of googleAuthR aside authentication such as batching, package creation etc. will eventually be also superseded by smaller packages. However, googleAuthR is still in active use and will be supported in a maintenance mode.

+
+

Version 2.0

+

Version 2.0 removed googleAuthR shiny modules in favour of gar_shiny_*. If you need those older legacy functions depend on googleAuthR == 1.4.1 or before.

+
+
+
+

Quick user based authentication

Once setup, then you should go through the Google login flow in your browser when you run this command:

library(googleAuthR)
 # starts auth process with defaults
@@ -161,8 +169,8 @@ 

Quick user based authentication

gar_auth(email = "your@email.com")

These functions are usually wrapped in package specific functions when used in other packages, such as googleAnalyticsR::ga_auth()

-
-

Client options

+
+

Client options

Most libraries will set the appropriate options for you, otherwise you will need to supply them from the Google Cloud console, in its APIs & services > Credentials section ( https://console.cloud.google.com/apis/credentials ).

You will need as a minimum:

    @@ -172,10 +180,10 @@

    Client options

If creating your own library you can choose to supply some or all of the above to the end-user, as an end-user you may need to set some of the above (most usually your own user authentication).

-
-

Multiple authentication tokens

-
-

googleAuthR > 1.0.0

+
+

Multiple authentication tokens

+
+

googleAuthR > 1.0.0

Authentication cache tokens are kept at a global level on your computer. When you authenticate the first time with a new client.id, scope or email then you will go through the authentication process in the browser, however the next time it wil be cached and be a lot quicker.

# switching between auth scopes
 # first time new scope manual auth, then auto if supplied email   
@@ -189,95 +197,53 @@ 

googleAuthR > 1.0.0

# ..query BigQuery functions ...
-
-

Legacy flows

-
-

Applicable before googleAuthR < 1.0.0

-
-

If you supply a filename to googleAuthR::gar_auth(token = "filename") then it will save the token there. If it doesn’t exist, it will make a new one, if it does exist it will attempt to read the token from that file. Relative and absolute filenames work.

-

You can use different token names to save different authentication settings such as with different scopes and client Ids.

-

An example switching between googleAnalyticsR and searchConsoleR authentication, assuming you have previously authenticated with two tokens, one name ga.httr-oauth and one named sc.httr-oauth

+
+
+

Setting the client via Google Cloud client JSON

+

To avoid keeping track of which client_id/secret to use, Google offers a client ID JSON file you can download from the Google Cloud console here - https://console.cloud.google.com/apis/credentials. Make sure the client ID type is Desktop for desktop applications.

+

You can use this to set the client details before your first authentication. The above example would then be:

library(googleAuthR)
 library(googleAnalyticsR)
 library(searchConsoleR)
 
-# start with google analytics auth
-gar_auth("ga.httr-oauth")
-
-# can run Google Analytics API calls:
-ga_account_list()
-
-# switch to Seach Console auth
-gar_auth("sc.httr-oauth")
-
-# can now run Search Console API calls:
-list_websites()
-

Alternatively, you can authenticate with both API services in the same token by specifying the scopes for the request - this determines what permission screen you get the first time you go through the OAuth2 flow.

-

You can access the scopes you required via the googleAuthR RStudio plugin.

-
library(googleAuthR)
-library(googleAnalyticsR)
-library(searchConsoleR)
-
-# set the scopes required
-options(googleAuthR.scopes.selected = c("https://www.googleapis.com/auth/analytics", 
-                                        "https://www.googleapis.com/auth/webmasters"))
-                                        
-# you may also set the client id and secret here as well
-options(googleAuthR.client_id = "XXXXXXX",
-        googleAuthR.client_secret = "XXXXXX")
-
-# authenticate and go through the OAuth2 flow first time - specify a filename to save to by passing it in
-gar_auth(token = "sc_ga.httr-oauth")
-                                        
-# can run Google Analytics API calls:
-ga_account_list()
-
-# and run Search Console API calls:
-list_websites()
-
-
-
-

Setting the client via Google Cloud client JSON

-

To avoid keeping track of which client_id/secret to use, Google offers a client ID JSON file you can download from the Google Cloud console here - https://console.cloud.google.com/apis/credentials. Make sure the client ID type is Desktop for desktop applications.

-

You can use this to set the client details before your first authentication. The above example would then be:

-
library(googleAuthR)
-library(googleAnalyticsR)
-library(searchConsoleR)
-
-# set the scopes required
-scopes = c("https://www.googleapis.com/auth/analytics", 
-          "https://www.googleapis.com/auth/webmasters")
-                                        
-# set the client
-gar_set_client("client-id.json", scopes = scopes)
-
-# authenticate and go through the OAuth2 flow first time
-gar_auth()
-                                        
-# can run Google Analytics API calls:
-ga_account_list()
-
-# and run Search Console API calls:
-list_websites()
+# set the scopes required +scopes = c("https://www.googleapis.com/auth/analytics", + "https://www.googleapis.com/auth/webmasters") + +# set the client +gar_set_client("client-id.json", scopes = scopes) + +# authenticate and go through the OAuth2 flow first time +gar_auth() + +# can run Google Analytics API calls: +ga_account_list() + +# and run Search Console API calls: +list_websites()

You can also place the file location of your client ID JSON in the GAR_CLIENT_JSON environment argument, where it will look for it by default:

-
# .Renviron
-GAR_CLIENT_JSON="~/path/to/clientjson.json"
+
# .Renviron
+GAR_CLIENT_JSON="~/path/to/clientjson.json"

Then you just need to supply the scopes:

-
gar_set_client(scopes = "https://www.googleapis.com/auth/webmasters")
+
gar_set_client(scopes = "https://www.googleapis.com/auth/webmasters")
-
-

Authentication with no browser

+
+

Authentication with no browser

Refer to this gargle article on how to authenticate in a non-interactive manner

-
-

Authentication with a JSON file via Service Accounts

+
+

Authentication with a JSON file via Service Accounts

You can also authenticate single users via a server side JSON file rather than going through the online OAuth2 flow. The end user could supply this JSON file, or you can upload your own JSON file to your applications. This is generally more secure if you know its only one user on the service, such as for Cloud services.

This involves downloading a secret JSON key with the authentication details. More details are available from Google here: Using OAuth2.0 for Server to Server Applications[https://developers.google.com/identity/protocols/oauth2/service-account]

To use, go to your Project in the Google Developement Console and select JSON Key type. Save the JSON file to your computer and supply the file location to the function gar_auth_service()

-
-

Creating service account and a key

-
-

From R

+
+

Roles

+

Roles all start with roles/* e.g. roles/editor - a list of predefined roles are here or you can see roles within your GCP console here.

+
+
+

Creating service account and a key

+
+

From R

The gar_service_create() and related functions let you create service accounts from a user OAuth2 login. The user requires permission iam.serviceAccounts.create for the project. Most often the user is an Owner/Editor.

The workflow for authenticating with a new key from R is:

    @@ -289,12 +255,8 @@

    From R

    See this related Google help article or creating service accounts.

    The above workflow is encapsulated within gar_service_provision() which will run through them for you if you supply it with your GCP projects Client Id (another JSON file that identifies your project.)

-
-

Roles

-

Roles all start with roles/* e.g. roles/editor - a list of predefined roles are here or you can see roles within your GCP console here.

-
-
-

WebUI

+
+

WebUI

Navigate to the JSON file from the Google Developer Console via: Credentials > New credentials > Service account Key > Select service account > Key type = JSON

If you are using the JSON file, you must ensure:

    @@ -303,339 +265,158 @@

    WebUI

  • The Google Project has the API turned on

An example using a service account JSON file for authentication is shown below:

-
library(googleAuthR)
-options(googleAuthR.scopes.selected = "https://www.googleapis.com/auth/urlshortner")
-service_token <- gar_auth_service(json_file="~/location/of/the/json/secret.json")
-analytics_url <- function(shortUrl, 
-                          timespan = c("allTime", "month", "week","day","twoHours")){
-  
-  timespan <- match.arg(timespan)
-  
-  f <- gar_api_generator("https://www.googleapis.com/urlshortener/v1/url",
-                         "GET",
-                         pars_args = list(shortUrl = "shortUrl",
-                                          projection = "FULL"),
-                         data_parse_function = function(x) { 
-                           a <- x$analytics 
-                           return(a[timespan][[1]])
-                         })
-  
-  f(pars_arguments = list(shortUrl = shortUrl))
-}
-analytics_url("https://goo.gl/2FcFVQbk")
+
library(googleAuthR)
+options(googleAuthR.scopes.selected = "https://www.googleapis.com/auth/urlshortner")
+service_token <- gar_auth_service(json_file="~/location/of/the/json/secret.json")
+analytics_url <- function(shortUrl, 
+                          timespan = c("allTime", "month", "week","day","twoHours")){
+  
+  timespan <- match.arg(timespan)
+  
+  f <- gar_api_generator("https://www.googleapis.com/urlshortener/v1/url",
+                         "GET",
+                         pars_args = list(shortUrl = "shortUrl",
+                                          projection = "FULL"),
+                         data_parse_function = function(x) { 
+                           a <- x$analytics 
+                           return(a[timespan][[1]])
+                         })
+  
+  f(pars_arguments = list(shortUrl = shortUrl))
+}
+analytics_url("https://goo.gl/2FcFVQbk")

Another example is from the searchConsoleR library - in this case we avoid using scr_auth() to authenticate via the JSON, which has had the service email added to the Search Console web property as a user.

-
library(googleAuthR)
-library(searchConsoleR)
-options(googleAuthR.scopes.selected = "https://www.googleapis.com/auth/webmasters") 
-
-gar_auth_service("auth.json")
-
-list_websites()
+
library(googleAuthR)
+library(searchConsoleR)
+options(googleAuthR.scopes.selected = "https://www.googleapis.com/auth/webmasters") 
+
+gar_auth_service("auth.json")
+
+list_websites()
-
-

Authentication within Shiny

+
+

Authentication within Shiny

If you want to create a Shiny app just using your data, refer to the non-interactive authentication article on gargle

If you want to make a multi-user Shiny app, where users login to their own Google account and the app works with their data, googleAuthR provides the below functions to help make the Google login process as easy as possible.

-
-

Types of Shiny Authentication

+
+

Types of Shiny Authentication

There are now these types of logins available, which suit different needs:

  • gar_shiny_* functions. These create a login UI before the main Shiny UI loads. Authentication occurs, and then the main UI loads but with the created unique user’s authentication. You can then use httr based Google authentication functions normally as you would offline.
  • -
  • googleAuth module - this creates a reactive server side token object for your API calls. You need to wrap your googleAuthR functions with with_shiny() to pass the reactive token.
  • -
  • googleAuth_js module - this creates a client side token object via JavaScript, that you can then pass to your API calls. You need to wrap your googleAuthR functions with with_shiny() to pass the reactive token.
  • googleSignIn module - this is for when you just want to have a login, but do not need to make API calls. It is a lightweight JavaScript based sign in solution.
-
-

Shiny Modules

+
+

Shiny Modules

googleAuthR uses Shiny Modules. This means less code and the ability to have multiple login buttons on the same app.

To use modules, you need to use the functions ending with _UI in your ui.R, then call the id you set there server side with the callModule(moduleName, "id") syntax. See the examples below.

-
-

Shiny Authentication Examples

+
+

Shiny Authentication Examples

Remember that client IDs and secrets will need to be created for the examples below. You need to pick a clientID for web applications, not “Other” as is used for offline googleAuthR functions.

-
-

URL redirects

+
+

URL redirects

In some platforms the URL you are authenticating from will not match the Docker container the script is running in (e.g. shinyapps.io or a kubernetes cluster) - in that case you can manually set it via options(googleAuthR.redirect = http://your-shiny-url.com). In other circumstances the Shiny app should be able to detect this itself.

-
-

gar_shiny_* functions example

+
+

gar_shiny_* functions example

This uses the most modern gar_shiny_* family of functions to create authentication. The app lists the files you have stored in Google Drive.

-
library(shiny)
-library(googleAuthR)
-gar_set_client(scopes = "https://www.googleapis.com/auth/drive")
-
-fileSearch <- function(query) {
-  gar_api_generator("https://www.googleapis.com/drive/v3/files/",
-                    "GET",
-                    pars_args=list(q=query),
-                    data_parse_function = function(x) x$files)()
-}
-
-## ui.R
-ui <- fluidPage(title = "googleAuthR Shiny Demo",
-                textInput("query", 
-                          label = "Google Drive query", 
-                          value = "mimeType != 'application/vnd.google-apps.folder'"),
-                tableOutput("gdrive")
-)
-
-## server.R
-server <- function(input, output, session){
-  
-  # create a non-reactive access_token as we should never get past this if not authenticated
-  gar_shiny_auth(session)
-  
-  output$gdrive <- renderTable({
-    req(input$query)
-    
-    # no need for with_shiny()
-    fileSearch(input$query)
-    
-  })
-}
-
-shinyApp(gar_shiny_ui(ui, login_ui = gar_shiny_login_ui), server)
-
-
-

googleAuth module example

-

The Google Cloud project needs to be setup to accept the URL and port of your app (see setup pages). You can fix the port via options(shiny.port=1221), and then make sure if you launch your app locally to change the ip address from 127.0.0.1 to localhost in your browser (Google doesn’t accept ip addresses).

-
## in global.R
-library(googleAuthR)
-library(shiny)
-options(googleAuthR.scopes.selected = "https://www.googleapis.com/auth/urlshortener")
-options(googleAuthR.webapp.client_id = "YOUR_PROJECT_KEY")
-options(googleAuthR.webapp.client_secret = "YOUR_CLIENT_SECRET")
-shorten_url <- function(url){
-  
-  body = list(
-    longUrl = url
-  )
-  
-  f <- gar_api_generator("https://www.googleapis.com/urlshortener/v1/url",
-                         "POST",
-                         data_parse_function = function(x) x$id)
-  
-  f(the_body = body)
-  
-}
-## server.R
-source("global.R")
-server <- function(input, output, session){
-  
-  ## Create access token and render login button
-  access_token <- callModule(googleAuth, "loginButton")
-  
-  short_url_output <- eventReactive(input$submit, {
-    ## wrap existing function with_shiny
-    ## pass the reactive token in shiny_access_token
-    ## pass other named arguments
-    with_shiny(f = shorten_url, 
-               shiny_access_token = access_token(),
-               url=input$url)
-    
-  })
-  
-  output$short_url <- renderText({
-    
-    short_url_output()
-    
-  })
-}
-## ui.R
-ui <- fluidPage(
-  googleAuthUI("loginButton"),
-  textInput("url", "Enter URL"),
-  actionButton("submit", "Shorten URL"),
-  textOutput("short_url")
-)
-### If the above global.R, server.R and ui.R files are in folder "test" like so:
-## /home
-##    |->/test/
-##            /global.R
-##            /ui.R
-##            /server.R
-##
-## Port 1221 has been set in your Google Project options as the port to listen to
-## as explained in authentication setup section
-## run below in /home directory
-shiny::runApp("./test/", launch.browser=T, port=1221)
-

By default the logout button causes a disconnect form the server, but you can use shinyjs to improve the user experience via this bit of code:

-
observe({
-    if (rv$login) {
-        shinyjs::onclick("gauth_login-googleAuthUi",
-            shinyjs::runjs("window.location.href = 'https://yourdomain.shinyapps.io/appName';"))
-    }
-})
-

See this post on creating a Shiny App with a Google login for details.

-
-
-

googleAuth_js module example

-

The Google Cloud project needs to be setup to accept JavaScript origin of the URL and port of your app ((see setup pages))note this is different from server-side configurations above). Make sure if you launch your app locally to change the ip address from 127.0.0.1 to localhost in your browser (Google doesn’t accept ip addresses).

-
library(shiny)
-library(googleAuthR)
-
-gar_set_client()
-
-## ui.R
-ui <- fluidPage(
-  googleAuth_jsUI("js_token"),
-  textInput("url", "Enter URL"),
-  actionButton("submit", "Shorten URL"),
-  textOutput("short_url")
-)
-
-shorten_url <- function(url){
-  
-  body = list(
-    longUrl = url
-  )
-  
-  f <- gar_api_generator("https://www.googleapis.com/urlshortener/v1/url",
-                         "POST",
-                         data_parse_function = function(x) x$id)
-  
-  f(the_body = body)
-  
-}
-
-## server.R
-server <- function(input, output, session){
-  
-  access_token <- callModule(googleAuth_js, "js_token")
-  
-  short_url_output <- eventReactive(input$submit, {
-    ## wrap existing function with_shiny
-    ## pass the reactive token in shiny_access_token
-    ## pass other named arguments
-    with_shiny(f = shorten_url, 
-               shiny_access_token = access_token(),
-               url=input$url)
-    
-  })
-  
-  output$short_url <- renderText({
-    
-    short_url_output()
-    
-  })
-}
-
-shinyApp(ui, server)
+
library(shiny)
+library(googleAuthR)
+gar_set_client(scopes = "https://www.googleapis.com/auth/drive")
+
+fileSearch <- function(query) {
+  gar_api_generator("https://www.googleapis.com/drive/v3/files/",
+                    "GET",
+                    pars_args=list(q=query),
+                    data_parse_function = function(x) x$files)()
+}
+
+## ui.R
+ui <- fluidPage(title = "googleAuthR Shiny Demo",
+                textInput("query", 
+                          label = "Google Drive query", 
+                          value = "mimeType != 'application/vnd.google-apps.folder'"),
+                tableOutput("gdrive")
+)
+
+## server.R
+server <- function(input, output, session){
+  
+  # create a non-reactive access_token as we should never get past this if not authenticated
+  gar_shiny_auth(session)
+  
+  output$gdrive <- renderTable({
+    req(input$query)
+    
+    # no need for with_shiny()
+    fileSearch(input$query)
+    
+  })
+}
+
+shinyApp(gar_shiny_ui(ui, login_ui = gar_shiny_login_ui), server)
-
-

googleSignIn module example

+
+

googleSignIn module example

This module is suitable if you don’t need to authenticate APIs in your app, you just would like a login. You can then reach the user email, id, name or avatar to decide which content you want to show with durther logic within your Shiny app.

You only need to set the client.id for this login, as no secrets are being created.

-
library(shiny)
-library(googleAuthR)
-
-options(googleAuthR.webapp.client_id = "1080525199262-qecndq7frddi66vr35brgckc1md5rgcl.apps.googleusercontent.com")
-
-ui <- fluidPage(
-    
-    titlePanel("Sample Google Sign-In"),
-    
-    sidebarLayout(
-      sidebarPanel(
-        googleSignInUI("demo")
-      ),
-      
-      mainPanel(
-        with(tags, dl(dt("Name"), dd(textOutput("g_name")),
-                      dt("Email"), dd(textOutput("g_email")),
-                      dt("Image"), dd(uiOutput("g_image")) ))
-      )
-    )
-  )
-
-server <- function(input, output, session) {
-  
-  sign_ins <- shiny::callModule(googleSignIn, "demo")
-  
-  output$g_name = renderText({ sign_ins()$name })
-  output$g_email = renderText({ sign_ins()$email })
-  output$g_image = renderUI({ img(src=sign_ins()$image) })
-  
-}
-
-# Run the application 
-shinyApp(ui = ui, server = server)
-
-
-
-
-

Authentication via RStudio Addin

-

An RStudio Addin is available via the RStudio Addin menu once you load the package.

-

It lets you set the scopes and then saves you some typing by calling the Google authentication flow for you.

-
-
-

Authentication in RMarkdown via JavaScript

-

There are two functions that can be called from within RMarkdown for authentication. They use JavaScript, rather than R/Shiny to authenticate, as an RMarkdown document can not read the URL tokens.

-

A demo and example are available here: https://mark.shinyapps.io/googleAuthRMarkdown/

-
-

RMarkdown authentication - Setup

-

The RMarkdown document YAML needs runtime shiny and to be a HTML document:

-
output: html_document
-runtime: shiny
-

Locally, you have to run the RMarkdown document on the specified port configured in Google console (1221 for the default shared project of googleAuthR), configured via options(shiny.port = 1221)

-

This means you shouldn’t launch the RMarkdown via the Run button in RStudio as that starts a new R session without your set options.

-

Instead set the options and run via rmarkdown::run("myfile.Rmd")

-
options(shiny.port = 1221)
-options(googleAuthR.scopes.selected = "https://www.googleapis.com/auth/plus.me")
-rmarkdown::run("googleAuthRMarkdown.Rmd")
-

When publishing, you also need to add the domain to the Javascript origins in the Google API console. Use 127.0.0.1:XXX where XXX is your chosen Shiny port for local testing.

+
library(shiny)
+library(googleAuthR)
+
+options(googleAuthR.webapp.client_id = "1080525199262-qecndq7frddi66vr35brgckc1md5rgcl.apps.googleusercontent.com")
+
+ui <- fluidPage(
+    
+    titlePanel("Sample Google Sign-In"),
+    
+    sidebarLayout(
+      sidebarPanel(
+        googleSignInUI("demo")
+      ),
+      
+      mainPanel(
+        with(tags, dl(dt("Name"), dd(textOutput("g_name")),
+                      dt("Email"), dd(textOutput("g_email")),
+                      dt("Image"), dd(uiOutput("g_image")) ))
+      )
+    )
+  )
+
+server <- function(input, output, session) {
+  
+  sign_ins <- shiny::callModule(googleSignIn, "demo")
+  
+  output$g_name = renderText({ sign_ins()$name })
+  output$g_email = renderText({ sign_ins()$email })
+  output$g_image = renderUI({ img(src=sign_ins()$image) })
+  
+}
+
+# Run the application 
+shinyApp(ui = ui, server = server)
-
-

Example of RMarkdown authentication

-

Below creates a button that when clicked makes a popup for Google authentication:

-
library(googleAuthR)
-
-googleAuth_jsUI("auth_demo", login_text = "Click Me")
-

The authentication token is available via the server side module command:

-
auth <- callModule(googleAuth_js, "auth_demo")
-

Pass the auth token to API functions. Below example using googleID to return G+ user info.

-
# devtools::install_github("MarkEdmondson1234/googleID")
-library(googleID)
-
-user_info <- reactive({
-  
-  req(auth())
-  
-  with_shiny(get_user_info,
-             shiny_access_token = auth())
-  
-})
-

You can now output the user data taken from the G+ API:

-
## creates an output
-renderUI({
-  
-  req(user_info())
-  
-  h1("Hello ", user_info()$displayName)
-  
-})
-
-

Auto-authentication

+
+

Auto-authentication

Auto-authentication can be performed upon a package load.

This requires the setup of environment variables either in your .Renviron file or via Sys.setenv() to point to a previously created authentication file. This file can be either a .httr-oauth file created via gar_auth() or a Google service account JSON downloaded from the Google API console.

This file will then be used for authentication via gar_auth_auto. You can call this function yourself in scripts or R sessions, but its main intention is to be called in the .onAttach function via gar_attach_auth_auto, so that you will authenticate right after you load the library via library(yourlibrary)

An example from googleCloudStorageR is shown below:

-
.onAttach <- function(libname, pkgname){
-
-  googleAuthR::gar_attach_auto_auth("https://www.googleapis.com/auth/devstorage.full_control",
-                                    environment_var = "GCS_AUTH_FILE")
-}
+
.onAttach <- function(libname, pkgname){
+
+  googleAuthR::gar_attach_auto_auth("https://www.googleapis.com/auth/devstorage.full_control",
+                                    environment_var = "GCS_AUTH_FILE")
+}

..which calls an environment variable set in ~/.Renvion:

GCS_AUTH_FILE="/Users/mark/auth/my_auth_file.json"
-
-

Revoking Authentication

-

For local use, call gar_deauth() to unauthentiucate a session. To avoid cache tokens being reused delete them from the gargle cache folder, usually ~/.R/gargle/gargle-oauth/

+
+

Revoking Authentication

+

For local use, call gar_deauth() to de-authenticate a session. To avoid cache tokens being reused delete them from the gargle cache folder, usually ~/.R/gargle/gargle-oauth/

For service level accounts delete the JSON file.

For a Shiny app, a cookie is left by Google that will mean a faster login next time a user uses the app with no Authorization screen that they get the first time through. To force this every time, activate the parameter revoke=TRUE within the googleAuth function.