Shiny App in Docker with HTTP Authentication

Suppose you have an app running on a Shiny server and you want to add HTTP authentication so that it’s only accessible via a username and password. This can be done using NGINX.

Test Shiny Server

The Shiny server should be accessible at http://localhost:3838/ (assuming you’re running Shiny server on localhost). 📢 Substitute another IP address or DNS entry if you’re running on another machine.

Shiny Server welcome page.

Install Packages

Install a couple of packages.

sudo apt install -y nginx-light apache2-utils

Default Configuration

Make a backup copy of /etc/nginx/nginx.conf then replace the original content with the following.

events {}

http {
  server {
    listen 80;
    
    location / {
      proxy_pass http://127.0.0.1:3838;
    }
  }
}
This is a minimal NGINX configuration. You could (and perhaps should!) provide a more extensive configuration.

Restart NGINX.

sudo service nginx restart

Once you’ve restarted NGINX the Shiny server will be accessible on port 80 at http://localhost/. 📢 Replace localhost if necessary.

Password File

Now we need to set up some credentials. We’ll use the htpasswd tool to create and populate a password file, /etc/nginx/.htpasswd.

# Create a "wookie" user.
sudo htpasswd -c /etc/nginx/.htpasswd wookie

Type and retype a password.

Check for user and hashed password.

cat /etc/nginx/.htpasswd
wookie:$apr1$6dONo8NN$169ClUPwSYtxhZ05fTE4l1

💡 You can add more usernames and passwords by running the htpasswd again. However, omit the -c flag so that the password file is not truncated each time.

Adding Authentication to NGINX Configuration

Now we need to tell NGINX to use the password file.

Authentication at the Location Level

Update /etc/nginx/nginx.conf.

events {}

http {
  server {
    listen 80;
    
    location / {
      auth_basic "Shiny Server";
      auth_basic_user_file /etc/nginx/.htpasswd;

      proxy_pass http://127.0.0.1:3838;
    }
  }
}

We’ve inserted auth_basic and auth_basic_user_file records for a specific location entry.

The `location` block passes all requests for the root URL to the Shiny server listening on port 3838.

Restart NGINX. When you try to connect to the Shiny server you should get an authentication popup.

Browser login dialog.

Authentication at the Server Level

You can also add authentication at the server level, which means that it will apply to all locations. This is moot for our configuration, which has only a single location specified.

user www-data;
worker_processes auto;
daemon on;                  # Run as daemon process

events {
  worker_connections 512;
}

http {
  gzip on;

  server {
    listen 80;
    
    auth_basic "Shiny Server";
    auth_basic_user_file /etc/nginx/.htpasswd;
    
    location / {
      proxy_pass http://127.0.0.1:3838;
    }
  }
}
Although there's now a password challenge, the actual credentials are still being transmitted over HTTP. This means that, although access to the resource is secured, it's possible for an adversary to intercept your credentials en route to the server.

To lock this down properly you’d need to also add SSL.

Try It Yourself

If you’d like to experiment with this, here are some resources:

To build and run the Docker image:

docker build -t shiny-nginx .
docker run --rm --name shiny-nginx -p 80:80 shiny-nginx