Creating an Authentication Navigation Guard in Vue
Tags: webdev, javascript, vue, learning
Published: 2019-10-08 10:54:52

---
title: Creating an Authentication Navigation Guard in Vue
published: true
description: Being able to navigate to pages is a crucial part of any application. But sometimes your users will try and access a page they don't belong on. In Vue you can check for this using a navigation guard!
tags: webdev, javascript, vue, learning
cover_image: https://thepracticaldev.s3.amazonaws.com/i/rcx0hhtindm83psdrcbt.png
canonical_url: https://tenmilesquare.com/creating-an-authentication-navigation-guard-in-vue/
---

So, you've built a login page and authentication! You route everyone there before they can go anywhere else on your site. But what happens if they just type another path in the url? If they're unauthenticated, can they still get in?

😳 Oops. That's not very secure at all.

What we really want, is to make sure they're always sent to the login page, no matter where they try to go, as long as their unauthenticated. A great way to do this in Vue, is to use a navigation guard.

Whenever a user on your site attempts to route to a page, you know about it. A navigation guard enables you to introduce a logic check at that point. And then you decide whether the user is allowed to go to their destination, or if they have to go somewhere else.

### The set up

Let's assume we have a router all set up called `router`. If you haven't done that before the [docs](https://router.vuejs.org/) are wonderful.

We've wired it up and defined some routes. Now what?

### The Skeleton

To start, know that there are multiple navigation guard functions available to us. In this case, we will use `beforeEach` which fires every time a user navigates from one page to another and resolves before the page is rendered. 

We link the function up to our `router`. We pass three arguments to the function. The route they're attempting to go `to`, the route they came `from` and `next`. 

```javascript
router.beforeEach((to, from, next) => {
})
```

### Next

`next` is actually a function and it's very interesting. `next` has to be called in order to resolve our guard. So every logic path needs to hit `next` in some way.

There are multiple ways to call `next`, but I want to point out three.

- `next()` sends you to the next set of logic. If there isn't any, the navigation is confirmed and the user gets sent to `to`.

- `next(false)` this sends the user back to `from` and aborts their attempted navigation.

- `next(<route>)` this sends the user elsewhere, wherever you determine that is.

We're going to make use of the first and last options in our navigation guard.

### Our logic
Ok, so now we need to determine in what circumstances we're sending the user one place or the next. In our case, we want to check for authenticated users. However, not all of our pages require you to be authenticated. We can define that in our route metadata so we know if we care about checking or not.

```javascript
const routes = [
  {
    path: '/',
    component: Home,
    meta: {
      requiresAuth: false,
    },
  }
]
```

That means that the first thing we want to look at is whether our `to` route `requiresAuth`.

If it does, we have more to write. If it doesn't, we've decided the user can navigate there, so we'll call `next()`. In this case, nothing follows that call, so `next()` will confirm the navigation.

```javascript
router.beforeEach((to, from, next) => {
  if (to.matched.some((record) => record.meta.requiresAuth)) {
  } else {
     next()
  }
})
```
*As it turns out, this works just fine without the else, and just letting `next()` be the catch-all. However, it causes problems later.*

### Our check
Now we're adding the last piece of the puzzle. If `requiresAuth` is `true`, then we want to check if our user is authenticated. 

> Note that we're not showing the implementation of `isAuthenticated`. This can be any number of things. We're just making the assumption we have a way to check.

If our user is authenticated, we want to confirm the navigation. Otherwise, we'll send them to the `login` page. 

```javascript
router.beforeEach((to, from, next) => {
  if (to.matched.some((record) => record.meta.requiresAuth)) {
    if (isAuthenticated()) {
      next()
    } else {
      next('/login')
    }
  } else {
    next()
  }
})
```

### Minor refactor

To be honest, the implementation below is a bit cleaner. No need to call `next()` twice, less `if/else` logic. But for some reason I've never liked checking on a false case, it just seems a bit confusing. However, others may feel differently, so know that this is also an option.

```javascript
router.beforeEach((to, from, next) => {
  if (to.matched.some((record) => record.meta.requiresAuth)) {
    if (!isAuthenticated()) {
       next('/login')
    }
  } else {
     next()
  }
})
```

### My Rabbit Hole

Initially, I had code that looked like this. And it works just the same! But I couldn't figure out the `return` piece of the puzzle and why I needed it. So I wanted to explain.


```javascript
router.beforeEach((to, from, next) => {
  if (to.matched.some((record) => record.meta.requiresAuth)) {
    if (isAuthenticated()) {
      next()
      return
    }
    next('/login')
  }
  next()
})
```
You could also write `return next()`, whichever you prefer.


`next()` only confirms the navigation if there are no hooks left in the pipeline. Since there were no `else` statements, only fall through behavior, `next()` didn't confirm anything, it just sent you to the "next" thing.

That didn't matter for records that didn't require auth, because you were sent to the final `next()` which was the end of the road. But for authenticated users, they'd always end up on the login page. So in order to make it work, the `return` is needed. It prevents the code that follows from being executed and confirms the navigation.

> Thanks to a conversation with Eduardo (posva on twitter), a Vue maintainer, I wanted to add this clarification. There should only be one instance of `next`, in any of its forms, for each logical path through your navigation guard code block. Being able to hit it more than once will cause errors and bugs.

### Conclusion

And that's it! We've built a navigation guard to check authentication for all our pages. Navigation guards, and `vue-router` in general, are incredibly powerful. There are tons of other things you can do and other options for how you accomplish it. Check out the docs and play around!

> If you're interested in learning even more about vue-router I'm doing a live workshop on Oct 22nd! Use [this link](https://ti.to/egghead-live-online-events/a-journey-with-vue-router-with-laurie-barth-2019-10-22/discount/early) for early bird pricing that expires Monday (Oct 14th).