MarkEdmondson1234 / googleAuthR

Google API Client Library for R. Easy authentication and help to build Google API R libraries with OAuth2. Shiny compatible.
https://code.markedmondson.me/googleAuthR
Other
175 stars 51 forks source link

approval_prompt: 'auto' is not valid #177

Closed rasnes closed 4 years ago

rasnes commented 4 years ago

What goes wrong

I am using googleAuthR to access my Shiny dashboards hosted on Google Cloud Run. It has been working well, but today I got a new error I have not seen before, and I suspect the error is not in my setup.

Error 400: invalid_request Invalid parameter value for approval_prompt: 'auto' is not valid

image

Steps to reproduce the problem

library(googleAuthR) # version 1.1.1
options(googleAuthR.webapp.client_id = <client_id>)
options(googleAuthR.scopes.selected = "email")
options(googleAuthR.webapp.client_secret = <secret>)
options(googleAuthR.redirect = <url>)

ui <- fluidPage(...)

server <- function(input, output, session){
  gar_shiny_auth(session)
  ...
}

shinyApp(gar_shiny_ui(ui), server)

Could it be that some changes to Google's API causes this?

hugovk commented 4 years ago

This is affecting Vimeo and also one of our sites (we use https://github.com/thephpleague/oauth2-client/).

Looks like a Google change and a fix is replacing approval_prompt=auto with prompt=, or leaving out both approval_prompt and prompt, or `approval_prompt=force.

https://developers.google.com/identity/protocols/oauth2/openid-connect#authenticationuriparameters

MarkEdmondson1234 commented 4 years ago

Interesting, thanks. Wasn’t sure Shiny could work on Cloud Run so good to know, would be interested in your setup.

To confirm Hugo’s diagnosis try changing the URL parameters that you have at the Google OAuth2 login screen to exclude approval_prompt parameter. If so, then I can roll out a fix. It’s already possible if using the JavaScript client based authentication Shiny module, gar_Authjs


From: Hugo van Kemenade notifications@github.com Sent: Tuesday, April 21, 2020 9:37:21 AM To: MarkEdmondson1234/googleAuthR googleAuthR@noreply.github.com Cc: Subscribed subscribed@noreply.github.com Subject: Re: [MarkEdmondson1234/googleAuthR] approval_prompt: 'auto' is not valid (#177)

This is affecting Vimeo and also one of our sites (we use https://github.com/thephpleague/oauth2-client/).

Looks like a Google change and a fix is replacing approval_prompt=auto with prompt=, or leaving out both approval_prompt and prompt, or `approval_prompt=force.

https://developers.google.com/identity/protocols/oauth2/openid-connect#authenticationuriparameters

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHubhttps://github.com/MarkEdmondson1234/googleAuthR/issues/177#issuecomment-617008970, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AAYCPLGYMSTO4UOJD2FZ3JTRNVELDANCNFSM4MNB3A7A.

rasnes commented 4 years ago

Update: it seems to be working as usual again today. Some temporary change or something on Google's end?

rasnes commented 4 years ago

Regarding Cloud Run setup. The main issue for me in setting it up (besides authentication :D) was that WebSockets are not supported. But disabling it in the protocols in Shiny server config, like instructed here works well in general, at least for "simpler" dashboards: https://github.com/rstudio/shiny/issues/2455. The exception is that certain features and functions in Shiny seem to only work with WebSockets, like some parts of Dynamic UI I can't get to work properly on Cloud Run.

hugovk commented 4 years ago

Update: it seems to be working as usual again today. Some temporary change or something on Google's end?

Yes, looks like Google have sorted it. We've kept the fix in our library, because there's no mention of approval_prompt in the docs.

MarkEdmondson1234 commented 4 years ago

It is possible with current code to alter the approval_prompt, which is derived from gar_shiny_auth_url

https://github.com/MarkEdmondson1234/googleAuthR/blob/11271d499bdbbb4f18fe8d32623bdcc4e44bd0e1/R/shiny-modifyurl.R#L18-L38

The default is approval_prompt = "auto" but change it to "force" by passing through your own login_ui function into gar_shiny_ui() which defaults to silent_auth:

https://github.com/MarkEdmondson1234/googleAuthR/blob/11271d499bdbbb4f18fe8d32623bdcc4e44bd0e1/R/shiny-modifyurl.R#L124-L153

So it would look like:

my_silent_auth <- function(req){
    shiny::tags$script(shiny::HTML(
    sprintf("location.replace(\"%s\");", gar_shiny_auth_url(req, approval_prompt = "force")
            )))
}

ui <- fluidPage(...)

server <- function(input, output, session){
  gar_shiny_auth(session)
  ...
}

shinyApp(gar_shiny_ui(ui, login_ui = my_silent_auth), server)

The idea is you can alter the login page or behaviour as its a function that generated the login screen/URL.

Will keep this issue open to see if the default needs to change if it happens again, otherwise I should document the above a little better.

rasnes commented 4 years ago

Okay, thank you very much!

OuNao commented 4 years ago

Hi, approval_prompt is not part of GoogleAPI anymore. https://developers.google.com/identity/protocols/oauth2/web-server#httprest

The correct parameter is "prompt"

I use googleAuth-jsUI in my project. I needed to change to be able to unforce show consent page:

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")
  assert_that(is.string(login_class), is.string(logout_class), 
                          is.string(login_text), is.string(logout_text), 
              !is.null(scopes))
  prompt <- match.arg(prompt)
  if (prompt == "both") prompt<-"consent select_account"
  approval_prompt_line <- paste0(",\n 'prompt':'",prompt,"'")
  check_package_loaded("shiny")
  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")))
}

Now I can select "select_account" prompt:

googleAuth_jsUI("loginButton", prompt = "select")
<script src="https://apis.google.com/js/auth.js"></script>
<button id="loginButton-login" onclick="auth();" class="btn btn-primary">Log In</button>
<button id="loginButton-logout" onclick="out();" class="btn btn-danger">Log Out</button>
<script type="text/javascript">var authorizeButton = document.getElementById('loginButton-login');var signoutButton = document.getElementById('loginButton-logout');signoutButton.style.display = 'none';function auth() {var config = {'client_id': 'myid.apps.googleusercontent.com','scope': 'https://www.googleapis.com/auth/drive.readonly' , 'prompt':'select_account'};gapi.auth.authorize(config, function() {token = gapi.auth.getToken();console.log('login complete');Shiny.onInputChange('loginButton-js_auth_access_token', token.access_token);Shiny.onInputChange('loginButton-js_auth_token_type', token.token_type);Shiny.onInputChange('loginButton-js_auth_expires_in', token.expires_in);authorizeButton.style.display = 'none';signoutButton.style.display = 'block';});}function out(){gapi.auth.signOut();location.reload();}</script>

Regards,

MarkEdmondson1234 commented 4 years ago

Thanks very much @OuNao - I've updated the function on github (version 1.2.0) - can anyone test it to see if it works ok? A CRAN release is pending soon so this can be updated quickly.