5

I am using Azure Webapps for Containers to host an R Shiny-Server. I want to use Azure Active Directory to authenticate and authorize the user logging into the app.

I am using the rocker/shiny image from dockerhub and the image builds and run easily. However, when I am turning on Active Directory the app does not work anymore. Any hints and clues on what might be wrong would be of great help.

Working

Not working

Kasper Christensen
  • 895
  • 3
  • 10
  • 30

2 Answers2

6

I got the same problem of an "empty" page, because loading of static files by the browser returned HTTP 400 when activating AD authentication. I have a Shiny app in a docker container on Azure App Services on the latest version of Shiny server (v1.5.12.933) and Shiny (1.4.0).

This means the problem described here https://community.rstudio.com/t/shiny-v1-3-known-regressions-and-serious-issues/28180/4 which I suspected first is not the reason.

w/o AD authentication the page is displayed correctly. The Azure proxy responsible for AD authentication injects some HTTP headers and cookies. I inspected the full HTTP request on server side via tcpflow -p -c -i eth0 port 3838 and had a look at the underlying R library httpuv which is responsible for the HTTP connection to the Shiny server.

When searching where in this library HTTP 400 codes are returned I found https://github.com/rstudio/httpuv/blob/master/src/webapplication.cpp and the following code snippet

// Make sure that there's no message body.
  if (pRequest->hasHeader("Content-Length") || pRequest->hasHeader("Transfer-Encoding")) {
    return error_response(pRequest, 400);
  }

while the request arriving at the server had the header Content-Length: 0 which is not present if AD authentication is turned off.

I created a fix and PR for httpuv, see issue https://github.com/rstudio/httpuv/issues/247.

You can use it as long as it is not merged into their repo.

Just run

R -e 'library(devtools); install_github("LHaferkamp/httpuv")'

in your Dockerfile

lhaferkamp
  • 676
  • 8
  • 10
  • Thanks a lot for your answer @lhaferkamp. While I used the most recent httpuv from CRAN, still got the same issue, though Content-Length in the header response is 16 and not 0. Hence following your code snippet, also logically returns a 400 Bad Request. So there's unfortunately still no proper solution to it I guess? – Marco Apr 29 '20 at 11:22
  • @Marco : Until now the httpuv version with my fix is not released to R CRAN repos. I asked on Github when the fix will be released and they told me end of april. So I'm waiting as well to finally remove my workaround. It's been now half a year, so release cycles are really slow there :( – lhaferkamp Apr 30 '20 at 12:43
  • 1
    The new version of httpuv is out and I can verify with that version, this problem is now fixed – Tyler Rinker May 29 '20 at 16:11
1

First you need to register your application in the Azure app registration service and get a tenant and app id. read this for the details. You use the Azure active directory by get_azure_token function from AzureAuth library. read more about the arguments here.

    ##################################
######### Installing libraires #################
load.lib <- c("AzureAuth","shiny","shinyjs","httr")

install.lib <- load.lib[!load.lib %in% installed.packages()]
for(lib in install.lib) install.packages(lib,dependencies=TRUE)
sapply(load.lib,library,character=TRUE)
##############################################
######### Setting the local port ###############
redirect <- "http://localhost:8100"

port <- httr::parse_url(redirect)$port
options(shiny.port=if(is.null(port)) 80 else as.numeric(port))
##################################################
######### Authentication #######################
tenant <- "your-tenant-here from Azure app service"
app <- "your-app-id-here from azure app service"
resource <- "your-scopes-here"
#example 
resource <- c("https://graph.microsoft.com/User.Read.All",
                  "https://graph.microsoft.com/User.ReadWrite.All",
                  "https://graph.microsoft.com/Directory.ReadWrite.All",
                  "offline_access")
pass = "your secret that you generate in the Azure app regitration"
aad_host = "https://login.microsoftonline.com/common/oauth2"

Token <- AzureAuth::get_azure_token( resource,tenant,
                                       app,
                                       password = pass,
                                       auth_type="authorization_code",
                                       authorize_args=list(redirect_uri=redirect),
                                       use_cache=FALSE,
                                       auth_code=opts$code,
                                       version = 2,
                                       aad_host = aad_host
  )


###############Importing the app R files#########
# load ui elements
source("ui.R")
# load server function
source("server.R")
#################################################

ui_func <- function(req)
{
  opts <- parseQueryString(req$QUERY_STRING)
  if(is.null(opts$code))
  {
    auth_uri <- build_authorization_uri(resource, tenant, app, redirect_uri=redirect, version=2)
    redir_js <- sprintf("location.replace(\"%s\");", auth_uri)
    tags$script(HTML(redir_js))
  }
  else ui
}

# Run the application
shinyApp(ui = ui_func, server = server)

I tried to explain each factor in the comment in the code above. If you use version=1 Authentication you need to save access token and refresh token somewhere in your code later on and get a new access token after the expiry. if you use version=2 ,you simply Token$refresh somewhere in your server.R and it'll extends your certificate. The ui_func function in the end is to establish the azure authorization sign in page if the users are not authenticated.

Yasin Amini
  • 53
  • 11
  • Code-only answers arent particularly helpful, and often a security concern for complex use-cases. Please edit your answer to include sources and descriptions of your solution and what it does. – Stephan Oct 15 '21 at 19:37
  • The examples used in the code above are well-maintained and long time support. I don't think anything above can raise any security alerts – Yasin Amini Jun 02 '22 at 12:41
  • you still need to provide links and sources per the user guidelines – Stephan Jun 03 '22 at 19:15
  • Much better, thank you! – Stephan Jun 13 '22 at 01:18