This article belongs to the series How to deploy a Shiny app on AWS, divided into 7 parts. To access the other articles, use the following table of contents:
- Part 1: Create a Shiny app.
- Part 2: Create an AWS server.
- Part 3: Install R and R Shiny on your new server.
- Part 4: Deploy the app on the server.
- Part 5: Extra: Create a nice domain name.
- Part 6: Extra: Secure your app with HTTPS.
- Part 7: Extra: Protect your app with a password.
It’s time to learn how to protect your Shiny app behind an authentication portal.
Most Shiny apps I create for clients are internal apps.
They use confidential data and the dashboard is intended to be used by a specific team.
Not by the public Internet.
In some companies, they isolate the app thanks to a VPN.
But sometimes, it’s not possible.
Sometimes you want to open your app to the Internet and restrict it to some users.
And you want to manage their rights.
You are at the right place.
From the simplest solution to the most complex one:
- How to set up a simple password with
- How to use a third-party service like Auth0
- How to create your own authentication portal
Before we start, a small disclaimer.
We’re talking about security.
I am NOT a security expert.
It’s a real job. My job is analyzing data. Not security.
You are responsible for the security of your applications.
You are responsible for your data.
This article is purely informative.
Now that this is stated, let’s start!
There is no best way to secure a Shiny app.
Here are your possibilities:
Shiny Server Pro
Use your web server -
We have already used
nginx as a reverse proxy.
nginx is your doorman.
It redirects visitors to the right service. To the right port number.
Another role for this doorman could be to accept or deny access to these visitors.
Rather than letting anyone pass, it could ask for credentials.
This solution works well. It’s easy to set up. And it’s secured.
Use a third-party service - Auth0
auth0 is an authentication service you can integrate into any application.
It’s free up to 7000 users.
The biggest advantage is that you don’t have to worry about security.
That’s THEIR job.
Not yours, nor mine.
So they know better than us!
However, using a third-party might be an issue for you. It creates dependency. You need to trust them.
Build your own authentication portal
Or: Do It Yourself.
If you’ve red all articles up until this one, well… you probably like to do things by yourself.
This is the most flexible solution, but also the less secure.
Since you’re not a security expert.
And me neither.
You’ve got to ask yourself…
Is Shiny secure?
Imagine that you add an authentication portal at the start of your Shiny app.
The logic of the code is:
“If the user logs in, then we show the rest of the app.”
First, it means the user does access to the Shiny Server to log in.
Is the rest of the code well hidden? How well?
Is the authentication portal resistant to SQL injection? To code injection? To brute force? To DoS attacks?
Answers aren’t always clear.
And even if you can answer to these questions, what about what you don’t know.
This solution works.
And it’s not optimal for security.
Now let’s dig into each of the other solutions.
nginx as an authentication server
This is the simplest solution.
And the least flexible.
You write the credentials in a file.
Then you get an unwelcoming popup at each connexion to the app.
It’s a quick and dirty solution.
But it’s efficient if you have an app that you want to show only to a few people.
For example, to a client.
You want to show him what your work looks like, but to him only.
The simplest way is to host it on your server, create a password with
nginx, and share it with your client.
To set up this solution, we use the console once again.
As a prerequisite, you need to have followed along the instructions of the two previous articles:
Your config file (the one at
/etc/nginx/sites-available/shiny.conf) should look like this:
This file is getting big! But as we completed it step by step, it’s not that scary.
Now, we add a new
location bloc that’s almost identical to the existing one, with two differences:
- The location won’t be the root (i.e. the slash
/) but the
- Then, we’ll add the password.
Here is the new
location bloc that you add below the existing one:
Why adding a location?
We could add only the last two lines to the previous
But if you do that, ALL your Shiny apps will be protected by the same passwords.
That’s not what we want.
We want to have some apps that are protected, and others that stay accessible by anyone.
With my solution and a second
location bloc, you can choose.
Save the config file.
Now, we fill the
/etc/nginx/.htpasswd with the credentials.
It’s super simple.
First, run the following instruction to create the login:
Of course, you can replace
charles by any other login.
Then, the following instruction will enable you to type a password but store only a hashed version of it (for security):
You can repeat the operation as much as you want to create other logins.
Now, restart the
And your app is protected.
Try for example my app at https://shiny.charlesbordet.com/movie-explorer-secure/ and notice the authentication popup.
No credentials? No access.
- login = guest
- password = trololo
And now you can access.
This method is secured because you force visitors to walk through
And by the way: Don’t forget to block the 3838 port in your firewall.
You can do so in AWS (reverse what we did in part 4)or by using
Otherwise people could walk around
Another important point: Use a strong password.
This method is not resistant against bruteforce.
To protect yourself against it, use
This method works well.
It’s pretty secured.
It’s not flexible at all!
It’s complicated to create new accounts.
You have to use the console.
And only you or someone technical enough can do it. It’s a bit complicated.
You can’t automate it.
It’s not user-friendly.
Could you imagine a pop-up like that to log in to an e-commerce website?
It doesn’t meet all possible needs.
Either you get in.
No account management.
No user right.
You don’t even know who the user is in the Shiny app.
So let’s try an other solution.
Use Auth0 as an authentication server
This second solution is a bit more complicated to set up, but has interesting features:
- Security is optimal,
- You can manage users,
- It’s simple.
Auth0 is an authentication and authorization service provider.
Rather than creating our own authentication portal, Auth0 will deal with this itself.
How to set up Auth0
Create an Auth0 account.
Go to https://auth0.com/signup and create an account.
The service is free up to 7000 users.
Once the account is created, log in and click on the big CREATE APPLICATION button:
Give it a name and choose Regular Web Applications:
You won’t find Shiny in the default app, so let’s configure the portal manually.
Click on Settings.
Here are the important information to note:
- Client ID
- Client Secret
You will need it later.
A bit below, in the Allowed Callback URLs cell, you have to fill in your URL followed by callback.
For me, it’s
Warning: No trailing slash
Save the modifications.
Install the portal on your server.
SSH into it and follow these steps:
1. Start by installing NodeJS.
2. Then, install the Auth0 application:
3. Finally, configure Auth0 with the information you got earlier.
Create a new file named
.env (by typing
nano .env for example) and enter the following:
The first four fields are the information you got earlier in your Auth0 account.
The COOKIE_SECRET must be a random string. I used a password generator to get mine.
You can leave the rest as it is.
Is our app secured?
Shiny listens on port 3838.
But the authentication portal listens on port 3000.
And we configured
nginx to redirects to port 3838.
We have to change that.
nginx config file (at
/etc/nginx/sites-available/shiny.conf) and change the instructions for
I changed the port number
Save the file and restart
Is our app secured?
We broke the app.
It’s not even accessible anymore!
Indeed, we’re redirecting to the Auth0 portal now, but we haven’t started it!
Go back to the
shiny-auth0 directory and run the following:
The console shouldn’t display anything. And the prompt is gone as well.
That’s because this instruction manually starts the authentication portal server.
It stays this way and will display the access logs.
At least now we can test the portal.
Go to your Shiny app and you should see this:
You can create an account. Log in and then access to the app.
If you want to get back your console prompt, you have to stop the server with Ctrl + C.
Something is weird though.
We have two issues:
- This manual start is not convenient. Are you supposed to keep the console open all the time?
- The goal is to restrict access, not to offer the possibility to anyone to open an account!
Let’s take care of this.
Create a service for Auth0
We have a few web servers on our machine.
The Shiny Server: On port 3838.
nginxServer: On port 80.
And now, the Auth0 Server. On port 3000.
Why do we have to start the Auth0 server manually and not the others?
The others are services. They start automatically as soon as the machine boots. And the service configuration has been done during the installation of these web servers.
For Auth0, the service configuration hasn’t been done.
We have to do it. Then, everything will be automatic.
It’s not complicated.
Create a new file:
And fill it with this service configuration:
Make sure you replace
charles with your username.
In general, it’s better to create a new username for each service. Like the
shiny username that was created for the Shiny Server.
If the service is compromised, the rest of the server stays safe.
Once the configuration is done, let’s activate and start the service:
Now, your authentication portal is online, and there’s no need for a manual start anymore!
Let’s tackle the other issue: How to restrict the access?
Create access rules
Go back to the Auth0 account management.
Click on CREATE RULE and you will see a ton of possibilities:
For example, you can:
- Specify a list of authorized emails,
- Forbid social logins (like Google, Facebook, etc.),
- Connect to an API to check if an email is authorized,
- Create your own custom rules.
I must admit this part is a bit intimidating to me.
For example, for a client who wants the app accessible only to employees, it’s easy to limit the access only to emails that look like
You could imagine having a SaaS product with your Shiny app. The buyer is added to your Mailchimp account on a specific list. Then, the rule calls Mailchimp API to check if the email is authorized.
Or the other way around. Your Shiny app is free, but users must create an account and their contact information is sent to Mailchimp, so that you can talk with your users or notify them of new versions.
So. This Auth0 service is quite good.
Except I don’t use it much except in very specific cases such as the exemples I just mentionned.
- I don’t like much it uses NodeJS. I don’t know this language at all, so that’s scary to me.
- Using a third party creates a dependency. What if they change their conditions tomorrow?
- While the rules system is flexible, the portal isn’t. What if I want to create a nice landing page?
Hence the third and last solution: Create your own authentication portal.
How to create your own authentication portal with Shiny
Here is the package you’ve been looking for:
It’s available on CRAN:
You will find some documentation on their Github repo.
But I find it a bit light.
Let’s start with their minimalist example to add the authentication portal.
So let’s try to adapt it to our movie explorer app.
As a reminder, the code of the app is available here.
After reading the documentation, we understand we need to:
- Create credentials
- Load the
- Wrap the UI function with
- Add the authentication module in the
Let’s do it!
The easiest part.
I use the piece of code from their example and put it in the
Credentials are stored like that.
We’ll come back to it later :)
Well, this is the easiest part.
I load it in the
global.R file as well:
Wrap the UI with
ui.R file, find the
That’s where the UI starts.
I add the
secure_app function around
And don’t forget to close the ending bracket.
Add the authentication module
Finally, I’ll copy/paste another piece of code at the beginning of the
Except… We created a bug.
It doesn’t like empty
And because of the authentication part, that’s what we have.
I tell it to plot something empty as long as we’re not logged in:
Now it works!
I’ve put my code here: https://gitlab.charlesbordet.com/charles/movie-explorer-login.
And hosted it here: https://shiny.charlesbordet.com/movie-explorer-login/.
You can try to log in with the
shinymanager doesn’t stop there.
You can also:
- have an administration mode to manage users,
- use an encrypted database to store credentials,
- change the language of the portal.
If you want to offer the possibility to create accounts, you have to do it yourself.
There are still a lot of things you have to do by yourself.
shinymanager is a good starting point.
Not a full solution.
Whenever I create authentication portals with Shiny, I do the following:
- I clone
shinymanagerand add the code to my project
- I get rid of all features I don’t need (logs or the admin interface)
- I add my own features
The first time was time-consuming as I had to go through the intricacies of the code.
But now I’m good.
I recommend you do the same.
Plus, you will better understand the strengths and weaknesses of the portal in terms of security.
I add these features:
- Createan account,
- Use rules to restrict who can create an account,
- Add a “Forgot password” link,
- Add cookies to remember sessions,
- Hash & Salt passwords.
In the end…
I like this solution.
Because of the flexibility.
The only drawback is security.
I don’t know what I don’t know.
So be careful.
If you deal with confidential data, ask a professional.
Here is a short recap of all the pros & cons of each solution:
|Shiny Server Pro||Expensive||Easy||Independent||Good||No|
|Auth0||Free up until 7k users||Medium||Dependent||Good||Yes|
Which one will you choose?
This article ends my series on How to deploy a Shiny app on AWS.
I hope you found it useful!
I know there are still plenty of topics to cover.
Docker, shinyproxy.io, scalability, …
I will write about these.
To get updates, you can subscribe below.
Leave a Comment
Required fields are marked *