Authentication Options - Realtime Thought Process

Somehow I find myself in the world of authentication again! I thought I’d struggled through it enough in the past that surely I’d be able to add auth to a new app I’m working on without much trouble. WRONG. 😂

I’m making a private website that needs to be password protected. It used to be a Wordpress site, and apparently this is a little more straightforward on apache servers with an .htaccess file? (That might be wrong, but it’s what I understand now, and a rabbit hold I decided not to go down!) But this new site is on a Node server so I would need to find another way to implement auth.

How to approach it this time?

Just copy what I did before—this was my first instinct. I’ve been through auth on a number of projects and got it to work several ways, so this should work, right? Well…

Passport could work with the passport-local strategy. But must I associate a username with the password (yes)? And so I really need to create a new database User model to store it (yes)? And wait, now I need a session store (not technically but it’s best practice and we can’t avoid those!)? And OMG look at the Passport documentation, it explains nothing. Even the helpful articles are tomes! 😩

Still I tried this anyway, and found that the best documented way to implement passport-local seems to be combining a MongoDB data store and passport-local-mongoose. Well my app already has a Postgres data store. So then I look at previous projects where I implemented passport-local-sequelize, check the docs, and see that it hasn’t been kept up to date with the latest releases of sequelize.

And isn’t all of this overkill anyway? I think so, so I look for a simpler approach!

I could go the roll your own route, and looked at a few ways to do this. This article and repo used a similar method to what I used in the value app where you store a hashed password and send cookies to the client. I also looked at a new-to-me package express-basic-auth which seemed to do the trick with the benefit of being bare-bones, only it’s not extendable to authenticating the user on specific routes, which for me is the whole purpose of this…another dead end.

Honestly going through all of these options was a lot! So then I thought I could go back to the old faithful Auth0…that is, until I re-read some of my old posts detailing all of the nitpicky issues I had on previous projects. Knowing I will deploy this app on Heroku (and we know how that turned out), I decided it wasn’t worth the trouble or cost again.

JWT Try Out

All of the above led me to try a new method I’ve not used before: JSON Web Tokens! This article and repo gave a relatively simple walk-through of how to implement JWTs using the jsonwebtoken package to encrypt the tokens, and argon2 to hash the passwords.

I adapted the code from this walk-through successfully and was able to register a user and give them a JWT. That user was also able to log in with a valid JWT.

Then I got to the part of protecting routes…roadblock. This method will work for the API where I can attach headers to each request, but to protect routes on a normal GET or POST app-level route, I’d still need to implement some kind of session store. There were also some gotchas around invalidating a token, for example if you want the user to be able to log out (as opposed to just letting their token expire).

Lesson learned!

Back to the Tried & True Method

I’m tired of working on this and want to actually work on the app now. I can see why people default to a tool like Auth0 or Okta!

So I decided to use a separate database for the user & session store following this step-by-step which uses Passport, MongoDB and passport-local-mongoose, and it worked great in the end.

The only issue was getting Express to serve the file directory of the Vue app’s static files…

Serving Protected Static Files

I could serve the login page (and Express EJS template) and the Vue app by setting up two static file locations:

// Serve client static files
app.set('view engine', 'ejs');
app.use(express.static(__dirname + '/public'));
app.use(express.static(require('path').join(__dirname, './views/client/dist')));

The problem was that the Vue app would render whether or not the user was logged in, even if the client/dist/index.html file was only served from a protected route.

I learned how to layer in the authentication by stacking my loggedIn middleware instead:

app.use('/home', isLoggedIn());
app.use('/home', express.static(require('path').join(__dirname, './views/client/dist')));

Same exact line of code, but called this way it only rendered the client/dist/index.html file, but ignores all of the other static files. The JavaScript file of a Vue app is kind of important 😑

The solution was to update the root path of the Vue app—with this setup, it was looking for JavaScript like this:

localhost:5000/js/bundlepack.js

…when it’s actually now located here:

localhost:5000/home/js/bundlepack.js

So adding some Vue config to the client’s package.json file did the trick:

...
"vue": {
"publicPath": "/home/"
},
...

Note: depending on how your Vue app was originally set up, you might need to add this setting to the vue.config.js file instead. See docs for all settings.

And that’s it!

I now have auth running successfully, and the static pages can only be accessed if the user is logged in. 🎉🎉🎉