# Adding authentication to a shiny server

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
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
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.