JohnCoene / firebase

Google FIrebase for shiny
https://firebase.john-coene.com
GNU Affero General Public License v3.0
170 stars 26 forks source link

Feature request: a way to not load the UI until after logging in #5

Open daattali opened 3 years ago

daattali commented 3 years ago

I'm not sure how, or if it's possible, but the current way of requiring all content to use either f$req_sign_in() or reqSignin() seems like it would get tedious and very verbose very fast. It would be good to have bypass that, like how RStudio's authentication and {auth0} really don't load the UI until it's safe to do so

bjungbogati commented 3 years ago

I agree with @daatalli, authentication in UI would be great.

Is there any workround for this problem @JohnCoene.

JohnCoene commented 3 years ago

Yes indeed, it does get tedious.

It could be done but it's quite a bit of work.

There are two ways I can think of doing this: by hijacking the app object as returned by shinyApp to, instead of returning the UI as response, return the login screen (when GET /).

Then either upon login load the shiny UI via websocket (can't return another response), or from the UI screen exectute a POST to another handler (e.g.: /login) which would return the UI.

The first is likely to cause issues with the rendering of some things as the UI is not meant to be loaded like that (e.g.: dependencies), the second is rather convoluted as it feels like we're logging people in twice.

There might be an easier way but I'm not seeing it.

Currently, using modules it's possible to reduce the use req_sign_in somewhat.

  observeEvent(f$req_sign_in(), {
    callModule(module_name_server, "module_name")
  })
chris31415926535 commented 2 years ago

Here's a potential workaround: build your ui() as usual, but wrap it in a call to shiny::renderUI() with a single f$req_sign_in().

The toy example below works for me: the text "Logged in!" doesn't show up in the html (as inspected using Chrome's dev panel) until you log in. I haven't tested it with a full app, but it worked okay with some buttons and other inputs and outputs.

Here's a screenshot showing the empty div "logged_in_ui" using this method:

image

Using firebase::reqSignIn() seems to just hide elements, so they can be found in the html. Here's the same ui wrapped in reqSignIn(): the text is there, just made invisible with the css style = display: none; .

image

This is a very minimal example, so maybe there are disadvantages in more complex cases--@JohnCoene, are there any obvious reasons this is a bad idea?

ui_secure <- fluidPage(
  useFirebase(), # import dependencies
  firebaseUIContainer(),

  shiny::uiOutput("logged_in_ui")
)

ui_secret <- shiny::fluidPage(
  h4("Logged in!")
)

server <- function(input, output){

  f <- FirebaseUI$
    new()$ # instantiate
    set_providers( # define providers
      email = TRUE,
      google = TRUE
    )$
    launch() # launch

  output$logged_in_ui <- shiny::renderUI({
    f$req_sign_in()
    ui_secret
  })
}

shiny::shinyApp(ui = ui_secure, server = server)

I'm posting here since there's been more discussion, but it's mostly a duplicate of this issue.

durraniu commented 1 year ago

@chris31415926535 Thank you for the solution above. I am trying to extend it with using a modal dialog. It works nicely for sign in but when signed out, It does not show the firebaseUI again. I'll appreciate any advice.

library(shiny)
library(firebase)
library(bslib)

# Firebase project configuration ------------------------------------------

firebase::firebase_config(api_key = "API_KEY",
                          project_id = "PROJECT_ID",
                          app_id = "APP_ID",
                          overwrite = TRUE)

# define signin
signin <- modalDialog(
  title = "Welcome. Please Sign in.",
  # useFirebase(),
  firebaseUIContainer(),
  footer = NULL
)

ui_dashboard <- page_navbar(

  title = "title",
  sidebar = sidebar("Sidebar"),
  nav_panel("P1", "Page 1 content"),
  nav_panel("P2", "Page 2 content"),
  nav_panel("P3", "Page 3 content"),
  nav_panel("P4", "Page 4 content"),

  ## Space between sections and sign out button
  nav_spacer(),
  ## Sign out button
  nav_item(
    uiOutput("sign_out_button")
  )
)

ui <- page_fillable(
  useFirebase(),
  shiny::uiOutput("logged_in_ui", fill = TRUE)
)

server <- function(input, output) {
  showModal(signin)

  f <- FirebaseUI$new()$set_providers(
    email = TRUE,
    google = TRUE)$launch()

  observe({
    f$req_sign_in()
    removeModal()
  })

  output$sign_out_button <- shiny::renderUI({
    f$req_sign_in()
    actionButton("signout", "Sign out")
  })

  observeEvent(input$signout, {
    f$sign_out()

    # Show the modal dialog
    showModal(signin)
  })

  output$logged_in_ui <- shiny::renderUI({
    f$req_sign_in()
    ui_dashboard
  })
}

shinyApp(ui, server)