A Modular Approach to Authentication in Nuxt (Overview)

Ifeanyi Ibekie
7 min readJul 13, 2020

--

Security is a must-have in any application no matter how basic it is. The concept of securing your app doesn’t only mean protecting against intruders, it also means guiding your users to where they should be and preventing them from accessing where they should not. These guards are necessary because you can never predict how your users will use your app at all times.

As a professional engineer, your application should be the perfect middleman between your end-users and the backend service. Your application should protect your backend from users as much as possible and also perfectly handle any errors the backend may return. This article would be in two parts. The first explaining and giving a sense of what the approach to authentication entails and the second the actual implementation.

I came up with this approach while building an e-commerce application. Before I wrote any line of code, I had to map out the users and the possible pages they could access in my application and as of that time I took a look at the awesome-nuxt ( https://github.com/nuxt-community/awesome-nuxt ) repo, I discovered the auth-module repository and I tried using it. It was a remarkable work but it didn’t really cater to my requirements. So I went under the hood and saw how it worked and added a few tweaks. That being said this approach was built of the auth-module repository.

Before we proceed with the story, you should ensure that you understand the following in Nuxt as they are key concepts to master to really appreciate the power of Nuxt and my authentication approach. However, feel free to read if you aren’t familiar with these concepts to get a sense of the architecture and see if you can re-implement it in your own style/language/framework. The concepts are:

  • Plugins
  • Middlewares
  • Context
  • Vuex in Nuxt
  • Routing
  • SSR (Server Side Rendering)

In order to fully grasp my thinking pattern towards the design of this approach, we need to look at the questions we need answers to when designing an authentication system and they are:

  • What are we protecting?
  • Where do we place our security measures?
  • How do we identify and distinguish our users?
  • How will our security measure operate?

WHAT ARE WE PROTECTING

This question is a simple one and the answer to it would be our whole application. By whole application, it would mean server and browser runtimes for UNIVERSAL mode and just the browser runtime for SPA mode. I will focus more on UNIVERSAL/SSR mode because that's where the power is and if we can perfect securing the application in such a divided state, it is a no brainer for SPA mode. Just in case you are wondering what it would mean to build an application with SSR. It simply means serving your webpages from the server (like the good old PHP Apache C-panel days) and it comes with the following benefits:

  • Ability to code some backend functionality in your frontend application (you can make your front-end application its own backend service taking some of the heavy liftings from the actual backend service for redundant tasks like IP location parsing and lots more).
  • Boosts Performance (It would also improve performance in content-heavy sites because you can configure the data required in pages to be fetched on the server-side of your application where it is rich with resources then all the browser has to do is render the HTML).
  • And lots more.

In summary, to protect our application in UNIVERSAL mode means enabling guards on our server when the first request hits and on the client over the course of subsequent hits.

WHERE DO WE PLACE OUR SECURITY MEASURES

Just as in most buildings and airports, security measures are mostly situated at the entrance and exits (before the actual resource worth protecting). So applying that same approach in Nuxt our best place is to write a middleware. We chose to write a custom middleware because any Nuxt middleware is run on every request that comes to the application whether server or browser runtimes.

HOW DO WE IDENTIFY AND DISTINGUISH OUR USERS

In a real-life scenario, every employee of an organization has an identity card or related giving them clearance at the various security measures and guests without id cards would either have to apply for guest cards or simply have limited access to the building/office, ours is no different from this approach. To identify our users both on the browser and on our application server, we need some sort of cross-compatible identification. The ID mechanism would be available on both the server and browser runtimes of Nuxt and would provide sufficient information about the user at all times for our security measures to make accurate decisions. We’ve got to be most careful here as any errors here could instantly become an exploitable vulnerability.

To implement this mechanism we would use 4 packages namely:

  • Vuex
  • Vuex-persisted state
  • js-cookie (Cookie Library for the Browser)
  • cookie (Cookie library for Nodejs (Server))

By default, Vuex is used to hold our application state as it serves as a single store of truth for shared data in our application and since we’d be needing it on both the client on the server we would wrap it with Vuex-persisted state as it allows us to read/write our Vuex store to persistent storage so that it's available even on a refresh of our application. The final question would be what storage?. The storage we are using is a cookie because cookies are shared between server and browser. Note: since cookie storage is not really large we would need to reduce our stored data to user information, any other information that would be needed can be persisted on the client with local storage because we are now ultimately using our Vuex store for identification. Just in case you are not comfortable with these constraints, you can tweak the Vuex-persisted state to your taste, the key concept here is that the authentication state should be stored in Vuex and the Vuex state related to authentication should be shared between the server and the browser using cookies.

Once we are able to achieve this, we would have every user information available to us on the server and browser runtimes meaning all we have to do now is discuss how it will operate with this data.

HOW WILL OUR SECURITY OPERATE

Now to the good stuff, Our security guards. Their mode of operation is simple. Every route registered in the application is a resource. We group each resource based on our user categories. e.g admin_routes, guest_routes, customer_routes e.t.c. Then based on the category of the user we allow the user through accordingly or redirect if there is a mismatch. That's the basic way of putting it. Wait, did I hear you say you wanted details?.

Okay here's more, Now after the categorization, we’ve gotta define a request data that would contain all the information about the incoming request. Luckily, Nuxt middlewares by default provide most of the values like the From (route the request is coming from (browser runtime only)), To (route the request is headed to (both runtimes)), User Data (necessary user data to identify the user (both runtimes)). So with our route data, the idea is to assume the operation of a conveyor belt in a production company. A little confused?. Let me explain.

Using the illustration above each document is our request data and the processing node is a single route guard configured to process request data related to its specific route group. What that means is, imagine this image to have so many processing nodes and every node is going to contain code to detect and redirect accordingly for a single route group and when it is done processing requests it passes to the next node and so on for as many route groups as there are until it gets to the end. We would call it our Navigation Guard Pipeline. From our sample route groups above since we only gave 3 routes (admin_routes, guest_routes, customer_routes), we would only have 3 nodes. The pattern I'm trying to describe here is a Functional Programming paradigm called Composition but with a little twist. The twist is that the functional paradigm uses data that must remain unchanged and at each node, the processing is done and passed to the next node with the data remaining untouched as it is supposed to be stateless. Our implementation can’t work that way, our data would have to be updated brilliantly so that requests to be processed at a particular node won’t be re-processed at another node since each node is supposed to be built to function unaware of other nodes (Plug n Play style). A realistic example would be to assume a straight line of security guards each for different locations in a building. Then a visitor that is headed for Room B would signify in his visitor's card. When the card gets scanned by Guard for Room A, he would simply skip and pass to Guard for Room B who would recognize this card, flag it as processed and then the remaining Guards won’t bother looking at it since the visitor has been attended to, then at the end of the line of guards would now be the guide to walk the visitor to the destination. This is basically what the Navigation Pipeline would do. Finally, the final output of our Pipeline would now be handed over to the redirect method who would read the output data and know to redirect the request accordingly.

Whew! That was long. I do hope you followed along, try as much as possible to understand the operation using the sample scenarios given as these would be key in the next step of the article where we would turn all this theory to proper code. But guess what, a true genius would have gotten all that is needed to implement it from this article, see if you can pull it off on your own. Note: This approach is structured this way to scale, remember I came up with it for an e-commerce application. If this is over-engineering for your application feel free to thin as you see fit, but I doubt you would need to. Happy Coding.

--

--

Ifeanyi Ibekie
Ifeanyi Ibekie

Written by Ifeanyi Ibekie

Greatness from small beginnings.

No responses yet