I am looking for tips and best practises on how to add authentification in a shiny app.
I have developed a shiny app, which consists of a public part accessible for everyone and a private part only accessible after login. The public app shows aggregated data of all users, while the private part shows the logged in user's data with more functionality.
There is lots of information on authentification functionality for shiny in blog posts, but nearly all handling authentification by adding an authentification layer before starting the shiny app. This is not applicable in my use case because the home page of the app should include shiny contents like plots etc.
So my current approach is to have two separate modules (module_public_app and module_private_app), a user database with encrypted passwords and a reactive value about the user's authentification status, then generating the user interface with renderUI depending on the status. Is this a good approach?
One problem I have found was that inputs still exist after logout and can be therefore a problem when logging in again, so I increased the id of the modules with every login. I need to make sure that nothing from the previous user session is still accessible. What happens to reactive elements that have been created, but are not used anymore? Could this affect performance?
This is my code:
library(shiny)
ui <- fluidPage(
uiOutput("ui")
)
mod_public_app_ui <- function(id) {
ns <- NS(id)
tagList(
textInput(ns("user_name"), "Username", value = ""),
passwordInput(ns("user_pw"), "Password", value = ""),
actionButton(ns("user_login"), "Login")
)
}
mod_public_app <- function(input, output, session) {
rv_user <- reactiveValues(user_name = NULL)
observeEvent(input$user_login, {
rv_user$user_name <- input$user_name
})
reactive({rv_user$user_name})
}
mod_private_app_ui <- function(id) {
ns <- NS(id)
tagList(
verbatimTextOutput(ns("welcome")),
actionButton(ns("user_logout"), "Logout")
)
}
mod_private_app <- function(input, output, session, .user) {
output$welcome <- renderPrint({paste0("Hello ", .user)})
reactive({input$user_logout})
}
server <- function(input, output, session) {
values <- reactiveValues(user = NULL, id = 0)
observe({
req(is.null(values$user))
output$ui <- renderUI({
mod_public_app_ui(paste0("public", values$id))
})
user <- callModule(mod_public_app, paste0("public", values$id))
observeEvent(user(), {
values$user <- user()
})
})
observe({
req(values$user)
output$ui <- renderUI({
mod_private_app_ui(paste0("private", values$id))
})
logout <- callModule(mod_private_app, paste0("private", values$id), .user = values$user)
observeEvent(logout(), {
values$user <- NULL
values$id <- values$id + 1L
})
})
}
shinyApp(ui, server)