My solution was to use the Google Sign-In API, write a very small amount of javascript, and use the js function Shiny.onInputChange
to create reactive variables from the user data.
The Sign-In API provides a button, does not require a secret, and allows the client ID and scope to be specified in meta tags in the HTML HEAD, so it's very easy to use.
In app.R
I simply add the Google API code, scope, client ID, and a login button like this.
ui <- tagList(
tags$head(
tags$meta(name="google-signin-scope",content="profile email"),
tags$meta(name="google-signin-client_id", content="YOURCLIENTID.apps.googleusercontent.com"),
HTML('<script src="https://apis.google.com/js/platform.js?onload=init"></script>'),
includeScript("signin.js"),
),
fluidPage(
titlePanel("Sample Google Sign-In"),
sidebarLayout(
sidebarPanel(
div(id="signin", class="g-signin2", "data-onsuccess"="onSignIn"),
actionButton("signout", "Sign Out", onclick="signOut();", class="btn-danger")
Note that Google's API will turn the signin
div into a button and the data-onsuccess parameter names the function onSignIn
to call upon successful authentication. Conveniently this is called whether the user is automatically logged in or actually going through the Google approval process.
There is also a signOut function which invalidates local cookies and also nullifies the profile data.
In a separate file signin.js
I defined the callback function onSignIn
that sends the user profile info to the shiny server from the client.
function onSignIn(googleUser) {
var profile = googleUser.getBasicProfile();
Shiny.onInputChange("g.id", profile.getId());
Shiny.onInputChange("g.name", profile.getName());
Shiny.onInputChange("g.image", profile.getImageUrl());
Shiny.onInputChange("g.email", profile.getEmail());
}
function signOut() {
var auth2 = gapi.auth2.getAuthInstance();
auth2.signOut();
Shiny.onInputChange("g.id", null);
Shiny.onInputChange("g.name", null);
Shiny.onInputChange("g.image", null);
Shiny.onInputChange("g.email", null);
}
That's about it. Your UI and server just need to add code to access the user profile reactives. Here's an example in the UI:
mainPanel(
with(tags, dl(dt("Name"), dd(textOutput("g.name")),
dt("Email"), dd(textOutput("g.email")),
dt("Image"), dd(uiOutput("g.image")) ))
)
And in the server:
server <- function(input, output) {
output$g.name = renderText({ input$g.name })
output$g.email = renderText({ input$g.email })
output$g.image = renderUI({ img(src=input$g.image) })
I put together a working example that you can run (with a very small bit of contortion - you must specify port 7445 and use localhost). Please read the README for more details.