Welcome back! If you missed this first part of this article, check it out here.

Digital Ocean Setup

The first thing we need is a place to host our app. In this tutorial, I'll show you how to deploy to DigitalOcean.

Create an Ubuntu Digital Ocean Droplet

Create a new droplet on Digital Ocean. Signing up with this link will issue a $10 credit to your new account. If you choose the $10 plan, that's one month free, or two months free on the $5 plan!

Go through the registration steps. When you create your new droplet, choose the Ubuntu 16.04 distribution image:

Note: Ubuntu 16.04.2 x64 is the default at the time of writing. This may change over time. If so, make sure you find applicable tutorials for the version of Ubuntu you chose.

Initial Ubuntu Server Setup

Once your droplet is set up, select it in your DigitalOcean dashboard and follow the steps here: Initial Server Setup with Ubuntu 16.04.

This tutorial instructs you on how to create a new user with sudo privileges. This is the user you should always log in with after initial setup (never as root ). The tutorial also shows you how to Add Public Key Authentication and set up a basic firewall.

Add a Host Domain

In order to easily access your app in the browser and secure it with an SSL certificate, you'll need to add a host domain name in the Digital Ocean dashboard. If you've already registered a domain, you can set it up by following the How To Set Up a Host Name with DigitalOcean tutorial.

I am using kmaida.net as my host. I also created a subdomain rsvp.kmaida.net CNAME record for my RSVP app. After setup, my DigitalOcean droplet's Domains look like this:

After following the above tutorial, yours should look similar.

Install Node.js and PM2

Now it's time to install some dependencies on our VPS. The first thing we'll need for our app is Node.js.

Please use the following tutorial to install Node on your DigitalOcean VPS: How To Install Node.js on Ubuntu 16.04.

Note: Using the PPA or nvm method will allow you to install a more recent version of Node beyond the distro-stable version for Ubuntu. Installing via PPA or nvm is recommended if you need a newer version. Using PPA, I installed the latest stable version at the time of writing, which is Node v8.1.0.

After installing Node (and with it, npm), we can install PM2, a Node production process manager. PM2 will enable us to keep our app process running and to restart it automatically if anything disrupts it.

Install PM2 globally on your VPS with the following command:

$ sudo npm install pm2 -g

You can read more about using PM2 in this tutorial: How To Set Up a Node.js Application for Production on Ubuntu 16.04 - Install PM2.

Install NGINX

The next step is to install nginx. We will use nginx as a reverse proxy to direct client requests to our MEAN stack app.

Side Note: Around the web, you may see various capitalizations of nginx/Nginx versus NGINX. Outside of the logo (which is always the same), the casual use of capitalization often has to do with the type of distribution. When denoted with lowercase letters, "nginx" (or sometimes "Nginx") generally refers to the open-source distribution. NGINX in uppercase letters frequently refers to NGINX Plus, the commercial application delivery platform. However, to make things more confusing, the website for the open-source version of nginx says "nginx," whereas the website for the commercial platform refers to both as "NGINX" (differentiating the two using the word "Plus").

Use this tutorial to install nginx on your VPS: How To Install Nginx on Ubuntu 16.04.

Note: In Step 2: Adjust the Firewall, enable Nginx Full , since we're going to set up SSL and we want to redirect traffic from HTTP to HTTPS.

Set Up SSL

Since our app deals with user authentication and potentially sensitive information, we want to secure it with SSL, an encrypted link between the browser and web server. In order to do this, we'll need to acquire an SSL certificate. We'll do this with Let's Encrypt, a free certificate authority.

There are several steps involved, so let's begin.

Install Certbot

On our VPS, we'll install the tools that Let's Encrypt needs in order to acquire and manage certificates:

$ sudo apt-get install software-properties-common $ sudo add-apt-repository ppa:certbot/certbot $ sudo apt-get update $ sudo apt-get install certbot

The tool we need to obtain certificates from Let's Encrypt is called certbot. These commands will install it and its dependencies. Throughout the installation, you will be prompted a few times to proceed.

Obtain a Certificate

Let's obtain a certificate now with the following command:

# e.g., sudo certbot certonly --standalone -d kmaida.net -d www.kmaida.net -d rsvp.kmaida.net $ sudo certbot certonly --standalone -d [example.com] -d [rsvp.example.com]

The certbot certonly command obtains a certificate but does not assume anything about the software that serves our content. We'll be using nginx, but we'll configure it manually. Because our Node app runs on a localhost webserver and not from the ~/var/www directory, we'll use the --standalone flag.

Make sure you replace the domains in square brackets ( [example.com] , etc.) with all the domains and subdomains you wish the certificate to include. You may be prompted to enter an admin email and accept terms. Once certbot is finished obtaining your certificate, you should see a Congratulations! message that looks something like this:

Automatically Renew Certificate

SSL certificates from Let's Encrypt expire every 90 days. To avoid having to manually renew the certificate every three months, we can set up a cron job to do this for us automatically.

To create a cron job, run this command:

$ sudo crontab -e

Then enter the following:

# Automatically renew certificate 46 0,12 * * * /usr/bin/certbot renew --quiet --renew-hook "/usr/local/bin/systemctl reload nginx"

This schedules certbot to check to see if the certificate is close to expiring twice a day at 12:46 AM and PM. Certbot recommends that the auto-renewal check runs at a random minute within the hour twice a day. If the certificate is close to expiration, certbot will renew it. The --quiet flag silences all output except errors. Upon successful renewal ( --renew-hook ), nginx is reloaded. If the certificate is not due for renewal, nothing will happen.

When finished, use Ctrl + x to exit the nano editor. You'll be prompted to confirm your changes, so enter y to accept. You should receive a message confirming a new crontab was installed.

Create a Diffie-Hellman Group

To further secure our connection, let's create a Diffie-Hellman group. This is a method of securely exchanging cryptographic keys over a public channel.

We can create a DH group with the following command:

$ sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048

It will take a little while for the DH to be generated. We'll add the generated file to our nginx SSL configuration shortly.

Note: You can learn more about the Diffie-Hellman key exchange here.

Nginx SSL Configuration

Let's create a file containing the SSL configuration for NGINX:

$ sudo nano /etc/nginx/snippets/ssl-params.conf

Add the following code in the new ssl-params.conf file:

# /etc/nginx/snippets/ssl-params.conf # cipherli.st ssl_protocols TLSv1.2; ssl_prefer_server_ciphers on; ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH"; ssl_ecdh_curve secp384r1; # Requires nginx >= 1.1.0 ssl_session_cache shared:SSL:10m; ssl_session_tickets off; # Requires nginx >= 1.5.9 ssl_stapling on; # Requires nginx >= 1.3.7 ssl_stapling_verify on; # Requires nginx => 1.3.7 resolver 8.8.8.8 8.8.4.4 valid=300s; resolver_timeout 5s; add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"; add_header X-Frame-Options SAMEORIGIN; add_header X-Content-Type-Options nosniff; # Diffie-Hellman group ssl_dhparam /etc/ssl/certs/dhparam.pem;

The first section is comprised of strong SSL security settings from cipherli.st's nginx example. In the code above, the resolver parameter is set to Google's DNS resolvers ( 8.8.8.8 and 8.8.4.4 ).

It's important to note that we also changed the X-Frame-Options to SAMEORIGIN . This is because Auth0's token renewal takes place in an iframe. If we don't allow the /silent callback to open in an iframe, we cannot renew authentication silently for our app.

Last, we'll add the Diffie-Hellman ssl-params.conf file we created above. Exit this file and confirm that changes should be saved.

Congratulations! We're now set up to use SSL. We'll do a little more configuration in the next section when we deploy our app.

Deploy Application on Digital Ocean

We're in the home stretch now: deployment! There are a couple of minor things we'll need to do before we deploy our application.

Don't Ignore Dist Folder

Our production app lives in the /dist directory in our project folder. By default, the Angular CLI has a .gitignore file that excludes this directory from source control. However, we need this folder to be in the Git repo if we want to avoid installing the Angular CLI on the server. This is very easy to fix.

In the .gitignore file in the project root, simply comment out /dist :

# .gitignore ... # compiled output # /dist ...

Commit this change. This also allows us to commit our production /dist folder.

Important Note: Make sure your repo is located somewhere that is accessible to your VPS server through Git, such as on GitHub or BitBucket. Alternatively, you can clone the app from the sample repo for this tutorial series, which is located at https://github.com/auth0-blog/mean-rsvp-auth0. If you clone the app from the sample repo, make sure you update the src/app/auth/auth.config.ts file with your own client settings before doing a production build.

Update Silent File

We also need to update our silent.html file with our secure production URL, like so:

<!-- silent.html --> <!doctype html> ... <script> ... const webAuth = new auth0.WebAuth({ ..., redirectUri: 'https://[YOUR_DOMAIN]' }); webAuth.parseHash(window.location.hash, function (err, response) { parent.postMessage(err || response, 'https://[YOUR_DOMAIN]'); }); </script> ...

Change the redirectUri and the postMessage() URL to your domain on HTTPS. This will prevent same origin errors on production when tokens are silently renewed.

Commit this change and push to your Git repository's remote.

Clone RSVP App on DigitalOcean VPS

You should have already set up a superuser for your VPS in the initial server setup tutorial from DigitalOcean. Now open a terminal and connect to the server. Once you're connected, clone the repo that your app lives in and install its Node dependencies, like so:

$ ssh [superuser]@[DigitalOcean_server_IP] $ cd / # Clone your own repo, or the sample one below: $ sudo git clone https://github.com/auth0-blog/mean-rsvp-auth0.git $ cd mean-rsvp-auth0 $ sudo npm install

If you're so inclined, you can clean up the app so that only the production files are present. (Or you can modify the .gitignore locally to exclude the /app folder.) A final option, of course, is to leave the entire app as it is. In production, it will only access the server files and the /dist contents.

Update App Configuration

As it is, the app is missing some backend configuration. The original config.js should never be checked into source control if the repo is available publicly. Therefore, we now need to recreate our server/config.js file with the appropriate information for the app.

On the server, execute the following commands:

$ cd server $ sudo nano config.js # in this file, paste your config.js contents

Save and exit with Ctrl + x and then y .

Configure Nginx for the App

We already set up encryption with an SSL certificate from Let's Encrypt. Now it's time to set up the reverse proxy with nginx for our application.

We're going to use an individual configuration file for our RSVP app, so first, we'll disable the default file. We can do this with the following commands:

$ cd /etc/nginx/sites-enabled $ sudo mv default default.bak

Now we can create a new file to store our app's nginx server blocks. Name the file after your full domain with .conf as an extension, like so:

$ sudo nano /etc/nginx/conf.d/[YOUR_DOMAIN].conf # e.g., rsvp.kmaida.net.conf

Configuration files in the conf.d folder are already imported in the /etc/nginx/nginx.conf file, so it's a great way to keep our configurations organized, especially if we'll have additional domains on our VPS in the future.

We'll then set up our new [YOUR_DOMAIN].conf file to redirect all traffic to HTTPS and use our SSL certificate:

# /etc/nginx/conf.d/[YOUR_DOMAIN].conf server { listen 80; listen [::]:80; server_name [YOUR_DOMAIN]; return 301 https://$server_name$request_uri; } server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name [YOUR_DOMAIN]; # SSL certificate ssl_certificate /etc/letsencrypt/live/[YOUR_CERTIFICATE]/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/[YOUR_CERTIFICATE]/privkey.pem; # SSL configuration from cipherli.st include snippets/ssl-params.conf; location / { proxy_pass http://localhost:8083/; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-NginX-Proxy true; proxy_set_header Host $http_host; proxy_ssl_session_reuse off; proxy_cache_bypass $http_upgrade; proxy_redirect off; } }

Make sure you've replaced [YOUR_DOMAIN] and [YOUR_CERTIFICATE] with your own information wherever they appear in the code above.

The first server block redirects our domain's traffic from HTTP (port 80 ) to HTTPS (port 443 ). The second block listens on port 443 and uses the SSL certificate and key as well as the SSL configuration. The location block then sets the localhost location and establishes necessary headers and settings. When you're finished, save this file.

We're almost done! Let's start our app's Node webserver. Change directories to your mean-rsvp folder on your VPS. This is likely to be:

$ cd /mean-rsvp

Once you're in your project folder, start the webserver with PM2 with the following command:

$ sudo pm2 start server.js

The final step is to start the nginx reverse proxy:

$ sudo systemctl start nginx

You should now be able to access your site in the browser. It should redirect to a secure connection if https is not specified when entering the URL.

Note: We haven't updated our Auth0 Client settings yet, so we'll get an error if we try to log in. We'll fix that next!

Production Auth0 Settings

There's only one step left, and that's to update our app's Auth0 Client settings to accommodate the production environment.

Log into Auth0 and head to your Auth0 Dashboard Clients. Select your RSVP app Client and add the production URLs to your settings:

Allowed Callback URLs - https://[YOUR_DOMAIN]/callback , https://[YOUR_DOMAIN]/silent

- , Allowed Origins (CORS) - https://[YOUR_DOMAIN]

Note: Take note that these are secure URLs using HTTPS.

If you have social connections set up for login, make sure they aren't using Auth0 dev keys. There will be an orange ! icon next to any that are. If so, you'll need to provide your own App or Client ID instead of leaving the field blank. Each connection has instructions on how to obtain your own ID.

After saving your changes in the Auth0 dashboard, you should be able to log into your production application!

Conclusion

We've now built and deployed a MEAN stack single page app to production, complete with:

API with authorized CRUD operations.

NoSQL database.

Authentication and access management with automatic JWT renewal.

Simple and complex forms with custom validation.

Lazy loading.

SSL.

Homework

There were a few topics that I didn't cover in this tutorial series in the interest of keeping it more manageable. If you'd like to dig deeper, I highly recommend that you look into the following:

Testing

Self-Hosted MongoDB

Congratulations!

You've completed a real-world, full-stack JavaScript project and even had a little taste of operations/sysadmin. You should now be prepared to build and deploy your own MEAN stack applications from the ground up!