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

Unable to persist googleAuthR login session #149

Closed frdnrdb closed 5 years ago

frdnrdb commented 5 years ago

I created a generic login flow for a Shiny app using the googleAuthR module. The login process works as expected, but i'm having problems persisting the session.

Here is the working login logic:

// -----> server

library(shiny)

server <- function(input, output, session) {

  loggedIn <- callModule(authorize, 'login')

  observe({
    req(loggedIn())
    # POST AUTH LOGIC... callModule(moduleForLoggedInUsersOnly, 'dashboard')
    cat('Logged in successfully...')
  }) 

}

ui <- bootstrapPage(
  authorizeUI('login'),
  # POST AUTH LOGIC... uiOutput('dashboard')
) 

shinyApp(ui, server)

// -----> authorize

library(googleAuthR) # googleAuthR_0.7.0

options(googleAuthR.scopes.selected = 'email')
options(googleAuthR.webapp.client_id = Sys.getenv('GOOGLE_AUTH_CLIENT_ID'))
options(googleAuthR.webapp.client_secret = Sys.getenv('GOOGLE_AUTH_CLIENT_SECRET'))

authorizeUI <- function(id) {
  ns <- NS(id)

  fluidPage(id = 'login',
    googleAuthUI(ns('loginButton'))
  )
}

authorize <- function(input, output, session) {
  if (GLOBAL_SKIP_LOGIN) return(function()TRUE)

  access_token <- callModule(googleAuth, "loginButton", login_text = "Login with Google", approval_prompt = "auto", access_type = "offline", revoke = TRUE)

  token <- reactiveVal(value = NULL)

  loggedIn <- reactive({
    req(access_token())
    token(isolate(access_token()$credentials$access_token))
    with_shiny(f = authReturnCode, shiny_access_token = access_token())
  })

  authReturnCode <- function() {
    securityCode <- getOption("googleAuthR.securitycode")
    pars <- shiny::parseQueryString(session$clientData$url_search)
    return(ifelse(pars$state == securityCode & !is.null(token()), TRUE, FALSE))
  }

  return(loggedIn)

}

Expected output

Automatic authentication (login process bypass) once login flow is completed and before the session has expired

Actual output

Warning: Error in : Authentication error: invalid_grantBad Request
  127: stop
  126: gar_shiny_getToken
  125: <reactive>
  109: accessToken
   96: shiny::renderUI
   95: func
   82: origRenderFunc
MarkEdmondson1234 commented 5 years ago

There is some wrapping of the base modules which may be affecting things here, perhaps in changing the expected scopes or state parameter during the OAuth2 flow.
e.g. why do you need to create your own loggedIn and authReturnCode() functions as they are part of the googleAuth module internally?

Can you inspect the URL that it gives a user to authenticate (the Google one?) The error suggests that is not valid. Also know that whats actually happening is a new Shiny session is being created so some assumptions about what variables are still set may be broken.

Also if its only a signIn you need (email scope), consider the client side based googleSignIn module that needs less moving parts.

frdnrdb commented 5 years ago

Hello, and thanks for the swift response!

Generated Google login link: https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=XXX&redirect_uri=XXX&scope=email&state=XXX&access_type=offline&approval_prompt=auto

Wrapping: I needed to remove code and just keep logic in the initial server function. For sanity. Also the app is data sensitive, so conditional panels etc. won't suffice as the JS-familiar user could easily "hack" the UI.

The loggedIn part is just used as a signal to the server function, and the authReturnCode is supposed to be a simple not-a-fraudulent-auth check/ step.

I will look at the googleSignIn module and see if it's easier to use.

EDIT: i also need to use organization-based access from Google and i'm not sure if the googleSignIn-module accepts that? calling https://accounts.google.com/o/oauth2/auth?redirect_uri=storagerelay://http/127.0.0.1:1410?id=XXX&response_type=permission id_token&scope=email profile openid&openid.realm=&client_id=XXX&ss_domain=http://127.0.0.1:1410&fetch_basic_profile=true&gsiwebsdk=2 (localhost:1410 is whitelisted @ google) yields

400. That’s an error.
Error: invalid_request
frdnrdb commented 5 years ago

ok, so basically exchanging custom module authorize with googleAuthR-module googleSignIn solved the issue 👍

MarkEdmondson1234 commented 5 years ago

Great, thanks for updating.