Simple and secure authentication mechanism for single ‘Shiny’ applications. Credentials are stored in an encrypted ‘SQLite’ database. Password are hashed using ‘scrypt’ R package. Source code of main application is protected until authentication is successful.
Live demo:
You can authenticate with: * user: shiny
/ password:
shiny
* user: shinymanager
/ password:
shinymanager
(Admin)
Online documentation : https://datastorm-open.github.io/shinymanager/
choose_language
quanteda
bad interactionInstall from CRAN with :
install.packages("shinymanager")
Or install development version from GitHub :
::install_github("datastorm-open/shinymanager") remotes
Secure your Shiny app to control who can access it :
secure_app()
& auth_ui()
(customization)secure_server()
&
check_credentials()
# define some credentials
<- data.frame(
credentials user = c("shiny", "shinymanager"), # mandatory
password = c("azerty", "12345"), # mandatory
start = c("2019-04-15"), # optinal (all others)
expire = c(NA, "2019-12-31"),
admin = c(FALSE, TRUE),
comment = "Simple and secure authentification mechanism
for single ‘Shiny’ applications.",
stringsAsFactors = FALSE
)
library(shiny)
library(shinymanager)
<- fluidPage(
ui $h2("My secure application"),
tagsverbatimTextOutput("auth_output")
)
# Wrap your UI with secure_app
<- secure_app(ui)
ui
<- function(input, output, session) {
server
# call the server part
# check_credentials returns a function to authenticate users
<- secure_server(
res_auth check_credentials = check_credentials(credentials)
)
$auth_output <- renderPrint({
outputreactiveValuesToList(res_auth)
})
# your classic server logic
}
shinyApp(ui, server)
Starting page of the application will be :
Once logged, the application will be launched and a button added to navigate between the app and the admin panel (SQL credentials only and if user is authorized to access it), and to logout from the application :
Store your credentials data in SQL database protected with a
symmetric AES encryption from openssl
, and password hashing
using scrypt
:
create_db()
# Init DB using credentials data
<- data.frame(
credentials user = c("shiny", "shinymanager"),
password = c("azerty", "12345"),
# password will automatically be hashed
admin = c(FALSE, TRUE),
stringsAsFactors = FALSE
)
# you can use keyring package to set database key
library(keyring)
key_set("R-shinymanager-key", "obiwankenobi")
# Init the database
create_db(
credentials_data = credentials,
sqlite_path = "path/to/database.sqlite", # will be created
passphrase = key_get("R-shinymanager-key", "obiwankenobi")
# passphrase = "passphrase_wihtout_keyring"
)
# Wrap your UI with secure_app, enabled admin mode or not
<- secure_app(ui, enable_admin = TRUE)
ui
<- function(input, output, session) {
server
# check_credentials directly on sqlite db
<- secure_server(
res_auth check_credentials = check_credentials(
"path/to/database.sqlite",
passphrase = key_get("R-shinymanager-key", "obiwankenobi")
# passphrase = "passphrase_wihtout_keyring"
)
)
$auth_output <- renderPrint({
outputreactiveValuesToList(res_auth)
})
# your classic server logic
... }
Using SQL database protected, an admin mode is available to manage access to the application, features included are
You can also use your own authentification function with
check_credentials
, for example doiing a control to your
intern database. check_credentials
must be a function with
two arguments user
& password
, returning a
least with at least result
(TRUE
to authorize
acces, or FALSE
) and user_info
(all you want
to retrieve from the user in the app) :
require(RPostgreSQL)
library(shiny)
library(shinymanager)
library(DBI)
library(glue)
= "*****"
dbname = "localhost"
host = *****
port = "*****"
user = "******"
password
<- dbConnect(dbDriver("PostgreSQL"), dbname = dbname , host = host, port = port ,
con user = user, password = password )
::dbWriteTable(con, "my_table", data.frame(
DBIuser = c("test"),
password = c("123"),
stringsAsFactors = FALSE
))
# or a config .yml file or others arguments
<- function(dbname, host, port, db_user, db_password) {
my_custom_check_creds
# finally one function of user and password
function(user, password) {
<- dbConnect(dbDriver("PostgreSQL"), dbname = dbname,
con host = host, port = port,
user = db_user, password = db_password)
on.exit(dbDisconnect(con))
<- glue_sql("SELECT * FROM my_table WHERE \"user\" = ({user}) AND \"password\" = ({password})",
req user = user, password = password, .con = con
)
<- dbSendQuery(con, req)
req <- dbFetch(req)
res if (nrow(res) > 0) {
list(result = TRUE, user_info = list(user = user, something = 123))
else {
} list(result = FALSE)
}
}
}
<- fluidPage(
ui $h2("My secure application"),
tagsverbatimTextOutput("auth_output")
)<- secure_app(ui)
ui
<- function(input, output, session) {
server <- secure_server(
res_auth check_credentials = my_custom_check_creds(
dbname = "******",
host = "*****",
port = ****,
db_user = "*****",
db_password = "*******"
)
) <- reactive({
auth_output reactiveValuesToList(res_auth)
})
# access info
observe({
print(auth_output())
})
}
shinyApp(ui, server)
Two inputs are created :
observe({
print(input$shinymanager_where)
print(input$shinymanager_language)
})
You can customize the module (css, image, language, …).
?secure_app
?auth_ui
?set_labels
It’s possible to use shinymanager
authentification on
flexdashboard
(but not admin console at moment). You can
find information on this
discussion. But it’s not a really secure way because user can
overpass the authentification using developper console… Prefer use
shiny
application with secure_app
function.
There’s no persistent data storage on shinyapps.io
, you
can read more here : https://docs.rstudio.com/shinyapps.io/Storage.html.
So your sqlite database is lost when the instance is
closed, and the one you’ve pushed when deploying the application will be
used. You have to use external database.
The application works fine without shinymanager
but not
you have trouble using shinymanager
.
There is a lag between your ui
and the
server
, since shinymanger
hides the
ui
part until authentication is successful. It is therefore
possible that some of `ui element`` (input) are not defined and are
NULL. In this case, you’ll see some warning / error message in your R
console.
So we recommend to use in all your reactive/observer functions the
req
instruction to validate the
inputs.
One more global and brutal solution can be :
server <- function(input, output, session) {
auth_out <- secure_server(....)
observe({
if(is.null(input$shinymanager_where) || (!is.null(input$shinymanager_where) && input$shinymanager_where %in% "application")){
# your server app code
}
})
}
But it’s better to use req
solution. More discussion on
https://github.com/datastorm-open/shinymanager/issues/36
shinymanager
use http request and sha256 tokens to grant
access to the application, like this the source code is protected
without having the need to change your UI or server code.
The credentials database is secured with a pass phrase and the openssl
package. Hashed password using scrypt
. If
you have concern about method we use, please fill an issue.
Package shinyauthr
provides a nice shiny module to add an authentication layer to your
shiny apps.