I am pretty new to Shiny -- if there is a better way to do this, I'd appreciate being pointed in the right direction.
That said, what I would like to do is connect a shiny app to a django rest framework API. First off is authenticating the user and hopefully saving the authentication token on the client side with JavaScript.
I am trying to follow the instructions here:
Shiny: Communicating with JavaScript
My idea is that I would store the user's token in the javascript localStorage
.
In the app ui
, I have this:
ui <- shiny::navbarPage(
"Yeast Calling Cards DB",
shiny::tabPanel("DB Tables",
shiny::selectInput(
"db_tables",
"Choose a table",
choices = names(raw_db_tables)),
table_detail_ui('db_table')),
shiny::tabPanel("User Auth", login_ui("user_auth")),
header = tags$head(
tags$script(shiny::HTML('// In the client side JavaScript code
$(document).on("shiny:connected", function() {
console.log("hello world!!")
// Retrieve the authentication token from localStorage
var authToken = localStorage.getItem("authToken");
// If the authentication token is null, do nothing
if (authToken == null) {
return;
}
// Otherwise, set the authentication token in the Shiny input
Shiny.setInputValue("authToken", authToken);
});
// Custom message handler to save the authentication token in localStorage
Shiny.addCustomMessageHandler("saveAuthToken", function(token) {
console.log(token);
localStorage.setItem("authToken", token);
console.log(localStorage.getItem("authToken"));
});'))
)
)
And in the login
module, I have this:
#' Docstring omitted
#'
login_ui <- function(id) {
shiny::fluidPage(
shiny::textInput(shiny::NS(id, "username"), "Username"),
shiny::passwordInput(shiny::NS(id,"password"), "Password"),
shiny::actionButton(shiny::NS(id,"login"), "Log In"),
htmltools::p(
"The token is: ", shiny::verbatimTextOutput(shiny::NS(id, "token"))
)
)
}
#' Docstring omitted
#'
login_server <- function(id) {
shiny::moduleServer(id, function(input, output, session) {
# Make a request to the REST API to authenticate the user
shiny::observeEvent(input$login, {
shiny::req(input$username, input$password)
auth_response <- httr::POST(paste0(Sys.getenv('DB_HOST'),
database_info$endpoints$auth_token),
body = list(username = input$username,
password = input$password),
encode = "json",
httr::accept_json())
if(httr::status_code(auth_response) == 200){
# Send the token to the client side JavaScript
session$sendCustomMessage(
"saveAuthToken",
httr::content(auth_response)$token)
print(input$authToken)
} else{
session$sendCustomMessage("saveAuthToken", 'unknown username or password')
}
})
# Render the token
output$token <- shiny::renderText({
if (!is.null(input$authToken)) {
input$authToken
} else {
"No token found"
}
})
})
}
The result of the console.log lines is good -- the token is getting passed, and it is getting added to localStorage
.
But, input$authToken
never updates.
My question is: is this even possible? can I save the token for the duration of the session and access it from different parts of my shiny app, after the user is logged in? If so, how to I access it, once it is in localStorage
?
The "full" app that I'm working on is here, for what it is worth: