R Shiny authentication (incl. demo app) | by Thomas Filaire | Towards Data Science

Shiny is extremely powerful to quickly build interactive web applications. It’s also the preferred technological solution for many data scientists, because it’s R-native.
As not all web applications are meant to be public, the ability to control access should be part of the know-how of any aspiring data scientist.
This tutorial will introduce you to a few options to secure your Shiny apps through authentication layer.
Note: in order to get the most from this tutorial, interested reader should already be familiar with basic Shiny principles (e.g. reactivity, how to use a module), web development (e.g. html tags and css), and RStudio projects.
This tutorial comes with a R Shiny demo application which you can access here.
The complete code is also available on my bitbucket account.
This tutorial will cover several approaches to secure access to R Shiny web application.
First, I’ll cover the basics of authentication, building my own login form, making the app appear (and the login form disappear) in case of correct credentials provided by the user.Then, I’ll pack the login form and the corresponding server logic into a module. This will increase your application readability and maintainability, and make your login form easy to reuse across several applications.In a third step, I’ll leverage the shinyauthr package, which is basically an implementation of step 2 with a few additional functionalities, including password hashing (based on the sodium package).Lastly, I’ll briefly mention two other approaches.
First, I’ll cover the basics of authentication, building my own login form, making the app appear (and the login form disappear) in case of correct credentials provided by the user.
Then, I’ll pack the login form and the corresponding server logic into a module. This will increase your application readability and maintainability, and make your login form easy to reuse across several applications.
In a third step, I’ll leverage the shinyauthr package, which is basically an implementation of step 2 with a few additional functionalities, including password hashing (based on the sodium package).
Lastly, I’ll briefly mention two other approaches.
Note: for the sake of the demonstration, I created a table to store username and password straight from the Shiny server section. Keep in mind that credentials should be encrypted and stored in a database in the case of apps deployed in production (database connection is not covered in this particular post).
In this section, you will learn how to build your own login form, as show in the short illustration below:
I’ll cover both the design of the login form and the corresponding server logic. Code explanation is split into 3 steps:
Load required packages and build application backboneBuild your own, fully customizable, login formDefine server side
Load required packages and build application backbone
Build your own, fully customizable, login form
Define server side
I used the 4 following packages: shiny, shinythemes, shinyjs, and tidyverse
# 1 - SET THE SCENE# load required packages library(shiny)          # web app framework library(shinyjs)        # improve user experience with JavaScriptlibrary(shinythemes)    # themes for shinylibrary(tidyverse)      # data manipulation
Then I designed a simple application backbone using navbarPage:
# 2 - UI PART# app backboneui <- navbarPage(    title = "R Shiny advanced tips series",    collapsible = TRUE,    windowTitle = "R Shiny tips - TFI",    theme = shinytheme("readable"),                tabPanel(        title = "Demo",        useShinyjs()    # Include shinyjs    ))# 3 - SERVER PARTserver <- function(input, output, session) {}# 4 - RUN APPshinyApp(ui = ui, server = server)
A login form requires the 3 following components:
a text input to capture user namea password input to capture password (similar to text input except that text is hidden)an action button to confirm/infirm access to the application
a text input to capture user name
a password input to capture password (similar to text input except that text is hidden)
an action button to confirm/infirm access to the application
Here is a simple code chunk you can adapt to your own preferences. In my case I mostly used basic bootstrap classes to make the text centered (“text-center”) and to add a light grey background to the login form (“well”).
Login form should be located in a dedicated div with class ‘container’.
Please also notice:
An id should be defined, so as to make the login form disappear in case of successful authentication. This part is defined just after in step 1.3
the use of the tagList() function to add an icon next to the text input and password input labels
div(    id = "login-basic",     style = "width: 500px; max-width: 100%; margin: 0 auto;",                        div(        class = "well",        h4(class = "text-center", "Please login"),        p(class = "text-center",           tags$small("First approach login form")        ),                                textInput(            inputId     = "ti_user_name_basic",             label       = tagList(icon("user"),                                   "User Name"),                            placeholder = "Enter user name"        ),                                passwordInput(            inputId     = "ti_password_basic",             label       = tagList(icon("unlock-alt"),                                   "Password"),             placeholder = "Enter password"        ),                                 div(            class = "text-center",            actionButton(                  inputId = "ab_login_button_basic",                   label = "Log in",                  class = "btn-primary"            )        )    ))
The server side is the place where developer can infuse intelligence into the application, define and control expected behaviors.
I first built a simple table to store users credential. In this example I granted access to only one user (“user_basic_1”), but you can of course define as many user x password combinations as you like.
# >> insert in server part# create userbaseuser_base_basic_tbl <- tibble(    user_name = "user_basic_1",    password  = "pass_basic_1")
In a second step, I used the eventReactive() function to check user’s login and password against valid credentials (stored in the above userbase). This toggles a TRUE/FALSE boolean parameter accordingly as soon as user clicks the login button.
# >> insert in server part# check credentials vs tibblevalidate_password_basic <- eventReactive(input$ab_login_button_basic, {           validate <- FALSE           if (input$ti_user_name_basic == user_base_basic_tbl$user_name &&            input$ti_password_basic == user_base_basic_tbl$password    {validate <- TRUE}})
Based on this boolean value:
Login form will hide, using the shinyjs::hide() function inside an observeEvent(). This requires an id to refer to the appropriate UI section to be hidden, thus my comment in step 1.2.Secured part of the app will appear, using a classic renderUI() + uiOutput() combination
Login form will hide, using the shinyjs::hide() function inside an observeEvent(). This requires an id to refer to the appropriate UI section to be hidden, thus my comment in step 1.2.
Secured part of the app will appear, using a classic renderUI() + uiOutput() combination
# >> insert in server part# hide form observeEvent(validate_password_basic(), {       shinyjs::hide(id = "login-basic")})# show app output$display_content_basic <- renderUI({           req(validate_password_basic())           div(       class = "bg-success",       id = "success_basic",       h4("Access confirmed!"),       p("Welcome to your basically-secured application!")   )        })
uiOutput should be inserted in the ui part
# app   uiOutput(outputId = "display_content_basic")
Congratulations if you’ve been able to follow and reproduce this example so far. You’ve just built your first login component to secure your R Shiny application.
As this process requires a significant amount of code which makes your application harder to maintain on the long run, let’s package it into a module!
Now that you understand the basic underlying principles of authentication and that you have built your first login form, let’s make it more robust and easier to reuse across multiple applications. This can be achieved defining a dedicated module.
A Shiny module is a piece of a Shiny app. It can’t be directly run, as a Shiny app can. Instead, it is included as part of a larger app (or as part of a larger Shiny module — they are composable). — Wiston Chang — Software engineer, RStudio
Note: this tutorial will not cover in details how to create a module, and I assume the reader is somehow familiar with its key concept.
In a nutshell, a module is composed of two functions that represent 1) a piece of UI, and 2) a fragment of server logic that uses that UI — similar to the way that Shiny apps are split into UI and server logic.
View the short illustration below to get an idea of what will be built with this second approach; as you can see the UI part looks exactly the same as what I built in the first approach. However it differs under the hood:
Code explanation is split into 4 steps:
Build the moduleSource the moduleAdd login form + secured part of the app in the UI partDefine server side
Build the module
Source the module
Add login form + secured part of the app in the UI part
Define server side
This steps essentially consists in bringing together the custom UI and associated server logic I’ve implemented in the previous approach, into a single module_login.R file.
Key aspects to notice in the code chunk below:
UI part:
Use of NS(): All UI function bodies should start with this line when creating a module. It takes the string id and creates a namespace function. All id names must be encapsulated within ns(). More on this here for interested reader
The function takes another variable, title, to make it easy to customize login page title, should you need to use the module in the context of other future R Shiny applications
# UI componentlogin_ui <- function(id, title) {        ns <- NS(id) # namespaced id        # define ui part    div(        id = ns("login"),        style = "width: 500px; max-width: 100%; margin: 0 auto;",                div(            class = "well",                        h4(class = "text-center", title),            p(class = "text-center",               tags$small("Second approach login form")),                        textInput(                inputId     = ns("ti_user_name_module"),                label       = tagList(icon("user"),                                       "User Name"),                placeholder = "Enter user name"            ),                        passwordInput(                inputId     = ns("ti_password_module"),                 label       = tagList(                                 icon("unlock-alt"),                                  "Password"                              ),                 placeholder = "Enter password"            ),                         div(                class = "text-center",                actionButton(                    inputId = ns("ab_login_button_module"),                     label   = "Log in",                    class   = "btn-primary"                )            )        )    )}
Server part:
use of {{ }} to use user-supplied expression within a function. More on this here for interested reader
call to shinyjs::hide() is embedded within the server part of the module, so there is no longer need to use an observeEvent as I did in the first approach
# SERVER componentvalidate_pwd <- function(input, output, session,                          data, user_col, pwd_col) {        # get user and pwd from data/ user_col/ pwd_col information    user <- data %>% pull({{ user_col }})     pwd  <- data %>% pull({{ pwd_col }})         # check correctness    eventReactive(input$ab_login_button_module, {                validate <- FALSE                if (input$ti_user_name_module == user &&            input$ti_password_module == pwd) {            validate <- TRUE        }                # hide login form when user is confirmed        if (validate) {            shinyjs::hide(id = "login")        }                validate    })}
Prior to using a particular module within your R Shiny application, be careful to source it appropriately (see my bitbucket account to understand the project structure):
# source module source("modules/module_login.R")
Both login form and secured part of your app can now be specified in the UI part with 2 lines of code, which ensure great readability and maintainability of your app on the long run.
# login form as defined in the modulelogin_ui(id = "module_login", title = "Please login"),                # app uiOutput(outputId = "display_content_module")
This is very similar to section 1.3.
I first built a simple table to store users credential. In this illustration I granted access to only one user, but you can of course define as many user x password combinations as you like.
# >> insert in server part# create userbaseuser_base_module_tbl <- tibble(        user_name = "user_module_1",        password  = "pass_module_1")
I then used the callModule() function to access the server side of my module, and passing information on where to search for login and passwords (via the data, user_col and pwd_col parameters to refer to the above userbase):
# check credentials vs tibble validate_password_module <- callModule(        module   = validate_pwd,         id       = "module_login",         data     = user_base_module_tbl,         user_col = user_name,         pwd_col  = password)
Finally, and as in the first approach, I use a classic renderUI() + uiOutput() combination to display the secured part of the application whenever the boolean validate_password_module() defined above toggles appropriately:
# app output$display_content_module <- renderUI({            req(validate_password_module())            div(        class = "bg-success",        id = "success_module",        h4("Access confirmed!"),        p("Welcome to your module-secured application!")    )        })
Congratulations if you’ve been able to follow and reproduce this example so far. You’ve been able to package your fully customizable login form into a module, which you can reuse with all your future web applications.
In the next section, we’ll leverage an existing module for authentication, available in the shinyauthr package. Main advantages compared to building your own module are:
No need for you to master the art of building modules, shinyauthr makes it ready for you to use
Ability to encrypt your password, via the sodium package. Sodium uses a hashing algorithm that is specifically designed to protect stored passwords from brute-force attacks
Shinyauthr is an R package providing module functions that can be used to add an authentication layer to your shiny apps. If you made it so far and successfully built your own module, this third approach should be easy to follow, and I recommend you use the package to speed up your future work.
View the short illustration below to get an idea of what will be built with this third approach; although it looks very similar to approaches 1 and 2, login form is slightly less customizable.
Code explanation below will specify new required packages, illustrate how to update the ui part and detail the server side. Remind that the entire code is available here.
In addition to the packages mentioned in previous approaches, you will need to load shinyauthr and sodium packages.
library(shinyauthr)     # shiny authentication moduleslibrary(sodium)         # crypto library
Both login form and secured part of your app can now be specified in the UI part with 2 lines of code, which ensure great readability and maintainability of your app on the long run.
loginUI() function from shinyauthr package is pretty similar to what you’ve built during second approach. Title (+ a few other parameters can be customized)
Secured part of the application is displayed through the uiOutput() function
# login formshinyauthr::loginUI(     id = "authr_login",      title = h4(class = "text-center", "Please login")),                # app uiOutput(outputId = "display_content_authr"),
I first built a simple table to store users credential. In this illustration I granted access to only one user, but you can of course define as many user x password combinations as you like.
Notice the use of password_store() function from the sodium package to encrypt the password.
# >> insert in server part# create userbaseuuser_base_authr_tbl <- tibble(     user_name = "user_authr_1",     password  = sodium::password_store("pass_authr_1"))
I then made a call to 2 modules that comes with the shinyauthr package (a login form + a logout form)
# >> insert in server partlogout_init <- callModule(      module = shinyauthr::logout,      id     = "authr_logout",      active = reactive(user_auth))    credentials <- callModule(      module   = shinyauthr::login,      id       = "authr_login",      data     = user_base_authr_tbl,       user_col = user_name,      pwd_col  = password,       sodium_hashed = TRUE,      log_out  = reactive(logout_init()))
When the login module is called, it returns a reactive list containing 2 elements:
user_auth (initial value is FALSE)
info (initial value is NULL)
I create reactive object on the server side to capture changes in these values (in particular, user_auth turns TRUE if the user supplies a matching user name and password.)
# >> insert in server partuser_auth <- reactive({        credentials()$user_auth})    user_data <- reactive({        credentials()$info})
Lastly, I need to define the secured part of the application within a renderUI() function, similarly to previous approaches.
# >> insert in server partoutput$display_content_authr <- renderUI({                req(user_auth())                div(            class = "bg-success",            id = "success_module",            h4("Access confirmed!"),            p("Welcome to your shinyauthr-secured application!               Notice that password is encrypted.")        )        })
Congratulations if you’ve made it this far, you are now equipped with a fast, powerful and easy way to secure your applications!
I usually implement one of the 3 above detailed approches.
However they are not the only ones and you might be interested in exploring other alternatives, such as:
shinymanager (very similar to shinyauthr)
polished a non-free solution providing you with several additional features on top of authentication, such as: custom login page, user management, single sign on, or user monitoring
I hope you found this tutorial useful, please feel free to leave comment to share your preferred (and possibly alternative) approach.
Want to learn more? This article is the second one of my advanced R Shiny tips series. First post is about building dynamic UI components.
shinymanager: simple and secure authentication mechanism for single Shiny applications.
shinyauthr: R package providing module functions that can be used to add an authentication layer to your shiny apps
polished: add authentication to your Shiny applications
Mastering Shiny: This book complements Shiny’s online documentation and is intended to help app authors develop a deeper understanding of Shiny
Business Science 202A — Build scalable applications using R: a great online courses