Creating a Shiny app with Google login

I will no longer be monitoring this site.

Please visit https://lmyint.github.io/post/shiny-app-with-google-login/.

Creating a Shiny application that enables user login can be useful for tailoring individual user experience and for analyzing user actions with profile-type data. With basic file I/O functions, it is possible to create a simple but insecure app that stores login names and passwords in text files. A much more secure alternative is to use an existing authentication system to handle login. I’m sure many of you have seen websites that allow you to login via Google or Facebook. I will outline here the steps needed to setup a “Login with Google” functionality on your Shiny app.

Step 1: Install packages

You will need the googleAuthR and googleID packages to allow for Google authentication and login. If you plan to publish your app on shinyapps.io, you’ll also need the shinyjs package to avoid a clunky “Disconnected from the server” message on logout. You can install these packages with

install.packages(c("googleAuthR", "shinyjs"))
devtools::install_github("MarkEdmondson1234/googleID")

It is important to install the googleID package with the command above to avoid an “Unable to retrieve package records” error when publishing your app (see here).

Step 2: Setup Google APIs

Setup a Google API project

  1. Make sure that you are logged into Google and visit the Google APIs project page.
  2. Click the “Create Project” link at the top and enter a name for the project (e.g. “myShinyApp”). After a few seconds, you will be redirected to the Google API manager.
  3. Click on the Google+ API link under “Social APIs” and click the “Enable” link at the top to activate the Google+ API.

Setup authentication credentials

  1. Click the “Credentials” link in the menu on the left.
  2. Navigate to the “OAuth consent screen” tab near the top.
  3. Fill in the “Product name shown to users” form with the name of your Shiny application. The information you provide in this tab populate the authentication screen that pops up when users click the “Login with Google” link in your app (example).
  4. Navigate to the “Credentials” tab at the top.
  5. On the “Create Credentials” dropdown menu, select “OAuth client ID” and select “Web application” for the application type.
  6. Fill in any descriptive name for this authentication client.
  7. In the redirect URLs field, fill in
  8. After saving this information, a client ID and secret will pop up. Copy and paste these for use in your code later.

Step 3: Code

Include the following code at the top of your app.R file to setup scopes for the relevant API functions you’ll be using and to specify the client ID and secret you received in step 8 above:

options(googleAuthR.scopes.selected = c("https://www.googleapis.com/auth/userinfo.email",
                                        "https://www.googleapis.com/auth/userinfo.profile"))
options("googleAuthR.webapp.client_id" = "YOUR_CLIENT_ID")
options("googleAuthR.webapp.client_secret" = "YOUR_CLIENT_SECRET")

Below is the shell of an app.R file that will create a login/logout button using Google authentication. I’ll explain the individual components afterward.

ui <- navbarPage(
    title = "App Name",
    windowTitle = "Browser window title",
    tabPanel("Tab 1",
        useShinyjs(),
        sidebarLayout(
            sidebarPanel(
                p("Welcome!"),
                googleAuthUI("gauth_login")
            ),
            mainPanel(
                textOutput("display_username")
            )
        )
    ),
    tabPanel("Tab 2",
        p("Layout for tab 2")
    )
)

server <- function(input, output, session) {
    ## Global variables needed throughout the app
    rv <- reactiveValues(
        login = FALSE
    )

    ## Authentication
    accessToken <- callModule(googleAuth, "gauth_login",
        login_class = "btn btn-primary",
        logout_class = "btn btn-primary")
    userDetails <- reactive({
        validate(
            need(accessToken(), "not logged in")
        )
        rv$login <- TRUE
        with_shiny(get_user_info, shiny_access_token = accessToken())
    })

    ## Display user's Google display name after successful login
    output$display_username <- renderText({
        validate(
            need(userDetails(), "getting user details")
        )
        userDetails()$displayName
    })

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

shinyApp(ui = ui, server = server)

The login/logout button is created as part of the UI by calling the googleAuthUI function and supplying an ID:

googleAuthUI("gauth_login")

Use the same ID to call the Google authentication module with callModule. It is also possible to set the classes of the login and logout buttons. For styling purposes, I’ve set the classes of the login and logout buttons to be the same which renders the buttons as flat blue buttons with white text. By default, the logout button just has the btn class and is a standard silver button.

accessToken <- callModule(googleAuth, "gauth_login",
    login_class = "btn btn-primary",
    logout_class = "btn btn-primary")

The userDetails object is a reactive expression that is a list of several pieces of information from the user’s Google profile (see the googleID example). Until the access token is generated, any output that depends on userDetails will instead display “not logged in.”

userDetails <- reactive({
    validate(
        need(accessToken(), "not logged in")
    )
    rv$login <- TRUE
    with_shiny(get_user_info, shiny_access_token = accessToken())
})

If parts of the UI are to be rendered based on this information after user login, include a validate() command:

output$display_username <- renderText({
    validate(
        need(userDetails(), "getting user details")
    )
    userDetails()$displayName
})

Without the last piece of code using shinyjs, clicking the logout button would cause the app to be disconnected from the server. This results in a clunky, undesirable logout experience. This last piece of code redirects to the specified URL when the logout button is clicked.

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

Other considerations

The steps above should help you quickly get started developing a Shiny application with Google login. The meat of the app will depend on your needs, but if you want to keep track of user information, consider using some online file system or database to map users’ Google IDs to your app’s own set of profile information.

17 thoughts on “Creating a Shiny app with Google login

  1. Hi Leslie, very nice post!
    I wanted to know how to take advantage of this login to “block” the access to the app by switching UI when the user login…. Wich is the “indicator” of the status of login?
    I tried to use this but I failed….
    output$display_username <- renderText({
    validate(
    need(userDetails(), "getting user details")
    )
    userDetails()$displayName
    })

    • The indicator status of a user being logged in is with the reactive value rv$login. This is set to FALSE when the user first visits the app and set to TRUE after the “Login with Google” button is clicked. I’m a little confused about what you mean by “block access to the app” though – do you want the “Login with Google” button to disappear after users login? If so, they won’t be able to logout, which might be unsettling to them.

  2. When I click on Logout it will redirect to the URL specifies but then if I try again and click on login it didn’t ask for password and logged in with previous account. how to logout from account and allow user to login with different ID.

    • The shutdown wasn’t on my radar, so thanks for letting me know! I’d have to guess that this won’t work anymore after the shutdown, but hopefully there are other APIs that can help achieve identical functionality. I’d have to explore, but don’t have a lot of time to do that right now.

  3. Hi Leslie, I am setting up a gmail login for my website and the only issue I am running with now is that when I refresh the page the user does not stay signed in, it gives me an invalid grant bad request. I checked and it seems to me that the problem is that there is a unique code that is generated each time you log in, so it is not user specific and apparently is only valid when you log in, after that it becomes invalid. I took the url’s from the logins I did and they were always different even if i used the same gmail account, and if I tried to go to any of them they said the grant request was invalid. Is there any way to make the url valid for a specific amount of time? I can send you my code if it’s helpful. Thank you

    • Unfortunately I don’t know what to do about the new tokens that get generated on refresh. I’d recommend advising your users to not refresh the page while in the app.

  4. Pingback: How I implemented googleSignIn in R (shiny) and lived – Data Science Austria

  5. Pingback: How I implemented googleSignIn in R (shiny) and lived | R-bloggers

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.