Auth in Nuxt 3: Protecting Routes in Nuxt 3 (3 of 4)

In the third part of this series of auth with Supabase, we’ll be using middleware to block users who aren’t logged in.

Michael Thiessen
Nuxt 3

Mastering Nuxt 3 course is here!

Get notified when we release new tutorials, lessons, and other expert Nuxt content.

Click here to view the Nuxt 3 course

The whole point of adding auth to our Nuxt 3 app is to restrict access to certain parts of our application.

That’s where route middleware comes in, which we’ll be using to block users who aren’t logged in.

In this series, we’re covering how to use Supabase with Nuxt 3 to add auth to our apps:

  1. Setting up Supabase Auth with Nuxt 3
  2. Logging in and out with Github
  3. Protecting Routes 👈 we’re here
  4. Protecting Server Routes

In this third article, we’ll create the middleware that will prevent users from accessing routes that they shouldn’t.

Route Middleware

Here’s a quick refresher on route middleware in Nuxt 3.

Route middleware are run every single time your Vue app changes to a new route. This is done within Vue itself, but the middleware may run on the server during server-side rendering.

Each middleware receives a to and from route. They must return one of:

  • Nothing — navigation continues normally
  • abortNavigation — this is how you’d return an error in order to stop navigation completely
  • navigateTo — if you want to redirect to somewhere else

It might look something like this:

function(to, from) {
  // We can use the `to` and `from` routes to figure
  // out what we should be doing in this middleware 

  if (notValidRoute(to)) {
    // Shows a "Page not found" error by default
    return abortNavigation();
  } else if (useDifferentError(to)) {
    // Pass in a custom error
    return abortNavigation(
      createError({
        statusCode: 404,
        message: 'The route could not be found :(',
      })
    );
  } else if (shouldRedirect(to)) {
    // Redirect back to the home page
    return navigateTo('/');
  } else {
    // If everything looks good, we won't do anything
    return;
  }
}

Let’s see how we can use this protect specific routes using the authentication we set up in the previous articles.

Protecting Routes

We’ll create a basic auth middleware:

function(to, from) {
  const user = useSupabaseUser();

  if (!user.value) {
    return navigateTo('/login');
  }
};

This middleware will redirect back to the /login page if the user is not logged in. If there is a logged in user, we don’t need to do anything!

To use the middleware on a route, we need to add it to a page we want to protect using definePageMeta:

<script setup>
definePageMeta({
  middleware: function(to, from) {
    const user = useSupabaseUser();

    if (!user.value) {
      return navigateTo('/login');
    }
  },
});
</script>

This is an inline middleware, because we’ve defined it directly in the page. If we use an array for middleware, we can actually add as many middleware functions as we want:

<script setup>
definePageMeta({
  middleware: [
    function(to, from) {
      const user = useSupabaseUser();

      if (!user.value) {
        return navigateTo('/login');
      }
    },
    // Add in more middleware here
  ]
});
</script>

But this isn’t great if we want to add this middleware to multiple pages. For that, we’ll want to use a named middleware.

We’ll do this by creating a new file at ~/middleware/auth.js, and wrapping the middleware function in defineNuxtRouteMiddleware:

export default defineNuxtRouteMiddleware((to, from) => {
  const user = useSupabaseUser();

  if (!user.value) {
    return navigateTo('/login');
  }
});

Now, we can simply refer to the name of the middleware as a string in our middleware array:

<script setup>
definePageMeta({
  middleware: [
    'auth',
    // Add in more middleware here
  ]
});
</script>

This is now much easier to add to multiple pages!

If we wanted to add this middleware to all routes, we can simply add the .global suffix to the filename. We’ll rename ~/middleware/auth.js to ~/middleware/auth.global.js.

But global middleware can present some problems. Now every single route will require the user to be logged in, which sort of makes it impossible to log in in the first place!

We also need to be careful with redirects in global middleware, as we can easily get stuck in an infinite redirect loop.

Wrapping Up

At this point, our users can log in and out, and they can only access protected routes once logged in.

But route middleware don’t run for our API endpoints. Right now they are completely unprotected.

This presents a big flaw in our security. We’ll patch that up next using server middleware.

Next Article: Protecting Server Routes

Michael Thiessen
Michael is a passionate full time Vue.js and Nuxt.js educator. His weekly newsletter is sent to over 11,000 Vue developers, and he has written over a hundred articles for his blog and VueSchool.

Follow MasteringNuxt on