Managing Users

There are several ways that users can be managed in Shiny apps but they all require access to the config file. User access can be managed within the app itself using a database to hold user information. Two examples are given below: The first utilizes a simple database in which the passwords are not encrypted (not recommended for multiple reasons!) The second is very similar but it uses the sodium library to encode the passwords in the database. These are very simplistic examples and contain no functions for password management (such as users being able to change their passwords or password reset) but these would be straight forward to add.

User login - No encrpytion

https://gist.github.com/harveyl888/a85e17c3048e0da03cf4e6b52d1da3db

 1## Authentication
 2## This is a small app to demonstrate user-managed authentication without encoded passwords.
 3## Users are stored in a SQL database with passwords along with roles.  
 4## Once a user is logged in the shiny app responds to the user's role.
 5## In order to use in a real setting, additional code for password management,
 6## changing and resetting would need to be implemented.
 7
 8library(shiny)
 9library(RSQLite)
10
11## create the initial password database
12## This code should be run once to create the initial database of users, passwords and roles
13##
14# db.pw <- data.frame(user = c('Augustin', 'Matt', 'Harvey'), role = c('Manager', 'User', 'User'), password = c('ABC', 'DEF', 'GHI'))
15# db = dbConnect(SQLite(), dbname = 'auth_nohash.sqlite')
16# dbSendQuery(db, 'CREATE TABLE pw (user TEXT, password TEXT, role TEXT)')
17# apply(db.pw, 1, function(x) dbSendQuery(db, paste0('INSERT INTO pw VALUES("', x['user'], '", "', x['password'], '", "', x['role'], '")')))
18# dbDisconnect(db)
19
20## Connect to the database (may be a remote connection)
21db = dbConnect(SQLite(), dbname = 'auth_nohash.sqlite')
22
23server <- function(input, output, session) {
24  
25  ## Initialize - user is not logged in
26  user <- reactiveValues(login = FALSE, name = NULL, role = NULL, header = NULL)
27  
28  ## Display login modal
29  observe({
30    showModal(modalDialog(
31      title = "Enter Login Details",
32      textInput('userInp', 'Login'),
33      passwordInput('pwInp', 'Password'),
34      actionButton('butLogin', 'Login', class = 'btn action-button btn-success', icon = icon('sign-in')),
35      size = 's',
36      easyClose = FALSE,
37      footer = NULL
38    ))
39  })
40  
41  ## Check for user in database
42  observeEvent(input$butLogin, {  ## login button pressed
43    req(input$userInp, input$pwInp)  ## ensure we have inputs
44    removeModal()  ## remove the modal
45    pw_out <- dbGetQuery(db, paste0('SELECT password FROM pw WHERE user = \"', input$userInp, '\"'))  ## query database
46    if (nrow(pw_out) == 0) {  ## user does not exist
47      user$login <- FALSE
48      user$header <- 'ERROR - UNKNOWN USER'
49    } else {
50      pw <- as.character(pw_out$password)[[1]]  ## grab password from database
51      passwordVerified <- pw == input$pwInp  ## check that it matches user input
52      if (passwordVerified) {  ## match
53        user$login <- TRUE
54        user$name <- input$userInp
55        user$role <- db.pw[db.pw$user == input$userInp, 'role']
56        user$header <- paste0(user$name, ' (', user$role, ')')
57      } else {  ## no match
58        user$login <- FALSE
59        user$header <- 'ERROR - INCORRECT PASSWORD'
60      }
61    }
62  })
63  
64  ## close database on exit
65  session$onSessionEnded(function(){
66    dbDisconnect(db)
67  })
68  
69  output$data <- renderUI({
70    h4(user$header)
71  })
72  
73  output$myPlot <- renderPlot({
74    req(user$login)
75    if (user$role == 'Manager') {  ## If manager role, display iris plot
76      plot(iris$Sepal.Length, iris$Sepal.Width)
77    } else {  ## If user role, display mtcars plot
78      plot(mtcars$mpg, mtcars$cyl)
79    }
80  })
81  
82}
83
84ui <- fluidPage(
85  uiOutput('data'),
86  plotOutput('myPlot')
87)
88
89shinyApp(ui = ui, server = server)

User login - With encrpytion

https://gist.github.com/harveyl888/3e5123a6469fbdc3830123e3efb31a2a

 1## Authentication
 2## This is a small app to demonstrate user-managed authentication using a hash to encode passwords.
 3## Users are stored in a SQL database with passwords along with roles.
 4## Once a user is logged in the shiny app responds to the user's role.
 5## In order to use in a real setting, additional code for password management,
 6## changing and resetting would need to be implemented.
 7
 8library(shiny)
 9library(RSQLite)
10library(sodium)
11
12## create the initial password database
13## This code should be run once to create the initial database of users, passwords and roles
14##
15# db.pw <- data.frame(user = c('Augustin', 'Matt', 'Harvey'), role = c('Manager', 'User', 'User'), password = c('ABC', 'DEF', 'GHI'))
16# db.pw$encrypt <- apply(db.pw, 1, function(x) password_store(x['password']))
17# db = dbConnect(SQLite(), dbname = 'auth_hash.sqlite')
18# dbSendQuery(db, 'CREATE TABLE pw (user TEXT, password TEXT, role TEXT)')
19# apply(db.pw, 1, function(x) dbSendQuery(db, paste0('INSERT INTO pw VALUES("', x['user'], '", "', x['encrypt'], '", "', x['role'], '")')))
20# dbDisconnect(db)
21
22## Connect to the database (may be a remote connection)
23db = dbConnect(SQLite(), dbname = 'auth_hash.sqlite')
24
25server <- function(input, output, session) {
26  
27  ## Initialize - user is not logged in
28  user <- reactiveValues(login = FALSE, name = NULL, role = NULL, header = NULL)
29  
30  ## Display login modal
31  observe({
32    showModal(modalDialog(
33      title = "Enter Login Details",
34      textInput('userInp', 'Login'),
35      passwordInput('pwInp', 'Password'),
36      actionButton('butLogin', 'Login', class = 'btn action-button btn-success', icon = icon('sign-in')),
37      size = 's',
38      easyClose = FALSE,
39      footer = NULL
40    ))
41  })
42  
43  ## Check for user in database
44  observeEvent(input$butLogin, {  ## login button pressed
45    req(input$userInp, input$pwInp)  ## ensure we have inputs
46    removeModal()  ## remove the modal
47    pw_out <- dbGetQuery(db, paste0('SELECT password FROM pw WHERE user = \"', input$userInp, '\"'))  ## query database
48    if (nrow(pw_out) == 0) {  ## user does not exist
49      user$login <- FALSE
50      user$header <- 'ERROR - UNKNOWN USER'
51    } else {
52      pw <- as.character(pw_out$password)[[1]]  ## grab password from database
53      passwordVerified <- password_verify(pw, input$pwInp)  ## check that it matches user input
54      if (passwordVerified) {  ## match
55        user$login <- TRUE
56        user$name <- input$userInp
57        user$role <- db.pw[db.pw$user == input$userInp, 'role']
58        user$header <- paste0(user$name, ' (', user$role, ')')
59      } else {  ## no match
60        user$login <- FALSE
61        user$header <- 'ERROR - INCORRECT PASSWORD'
62      }
63    }
64  })
65  
66  ## close database on exit
67  session$onSessionEnded(function(){
68    dbDisconnect(db)
69  })
70  
71  output$data <- renderUI({
72    h4(user$header)
73  })
74  
75  output$myPlot <- renderPlot({
76    req(user$login)
77    if (user$role == 'Manager') {  ## If manager role, display iris plot
78      plot(iris$Sepal.Length, iris$Sepal.Width)
79    } else {  ## If user role, display mtcars plot
80      plot(mtcars$mpg, mtcars$cyl)
81    }
82  })
83  
84}
85
86ui <- fluidPage(
87  uiOutput('data'),
88  plotOutput('myPlot')
89)
90
91shinyApp(ui = ui, server = server)