Adding authentication to a shiny server
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.
Umph, that was a tough one. I spent ages figuring out how to do it correctly. I have a server running apache (on port 80) and shiny on port (say) 11111. Shiny has its own document root, and within this root, we have a shiny app, say, “example”. So to view this app you need to type http://server:1111/example/. So far, so good. What I wanted, though, was (i) some kind of password protection for the app, and (ii) calling the app from the URL http://server/example/. Turns out it can be done, but it was not trvial.
First, I modified configuration of the shiny server to listen only to the specified port and only to the localhost; this prevents anyone from any other machine to connect to shiny:
server { listen 11111 127.0.0.1; location / { site_dir /srv/shiny-server; log_dir /var/log/shiny-server; directory_index off; } }
Now to apache. In the httpd.conf file, I have added The following:
<VirtualHost *:80> Redirect /example /example/ ProxyPass /example/ http://127.0.0.1:11111/example/ ProxyPassReverse /example/ http://127.0.0.1:11111/example/ <Location /example> AuthType Basic AuthName "Enter your login name and password" AuthUserFile /etc/httpd/htpasswd.users Require valid-user </Location> </VirtualHost>
This makes apache work as a proxy to the shiny server; however, with the added benefit of a simple authentication for the shiny contents.
It took me quite some time to figure out that without the Redirect directive above, http://server/example/ works, but http://server/example (without the slash) doesn’t.
Finally, I created new users with htpasswd.
Update: Interestingly, shiny cannot handle HEAD requests. HEAD request is when a program asks whether a page is there rather than downloading the whole page. Apparently, this is how CRAN checks whether a site is available. In any case, just after the Virtualhosts directive, I have added the following:
RewriteEngine on RewriteCond %{REQUEST_METHOD} ^HEAD RewriteRule ^/example(.*) /foo/index.html
This rewrites the requested URL to example only if the request method is HEAD, and instead asking the shiny server, it asks itself — and since the file /foo/index.html exists, we get 200 OK.
Update 2: I found why shiny is returning 404 to HEAD requests. In the file shiny/R/server.R
, in line 177, you have the statement
if (!identical(req$REQUEST_METHOD, 'GET')) return(NULL)
Obviously, any request other than “GET” gets turned down, as NULL results in a 404.
As a workaround, I have changed it to
if(identical(req$REQUEST_METHOD, 'HEAD')) return(httpResponse(200, content="OK")) if (!identical(req$REQUEST_METHOD, 'GET')) return(NULL)
Of course, the down side is that it replies with “OK” even if the given resource does not exist. I am testing a proper way of handling these requests, but at the moment this will have to do.
R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.