Nuxt Authentication Auth0 Provider Management API

April 3rd 2020

In this tutorial we are going to discuss how to use Auth0 identity provider service as a Nuxt authentication provider and extend the auth0 library to use the Management API to get roles and permissions for a user.

Nuxt provides an auth module out of the box which provides some basic auth services for us. It’s great library and even has provider strategies for third party providers such as Facebook, Gitbub, Google and Auth0. The auth0 strategy provided does a good job of handling the basic authentication for us to authenticate a user, but what if we want to implement user roles and permissions into our application? The Nuxt auth module provides the basic authentication object for users through the Authentication API but does not provide the Auth0 User Metadata which is accessible via the Management API. The User Metadata is where you can store additional user information such as plans/subscriptions, security roles and permissions.

App metadata: stores information (such as, support plan subscriptions, security roles, or access control groups) that can impact a user’s core functionality, such as how an application functions or what the user can access. Data stored in app_metadata cannot be edited by users.

https://auth0.com/docs

We can use App metadata to store user roles and permissions in our Nuxt application, but how do we go about getting each user’s app metadata? We can do this through the Management API but in order to access the management api we to obtain a special access token called Auth0 Management API Token. This token will then allow us to access the Management API to get a logged in user’s app_metadata information in which we are storing the user’s roles and permissions.

Creating auth0 Account and Application Setup

We will need an auth0 account since we are using the auth0 identity provider for Nuxt. We can create a free account by going to https://auth0.com/ and clicking the “Sign Up” button. Once logged in we will be directed to our Dashboard where we’ll be able to create a new “Application”. We will need to create two different types of applications. Our first will be of type “Single Page Web Application” which we will use in our Nuxt Auth Module configuration and of which gives us access to auth0’s Authentication API for basic authentication. Our second application we create will be of type “Machine to Machine” which is used to access the auth0 Management API.

First, we setup our application for the Authentication API that we will use to configure our Nuxt Auth Module. From the “Dashboard” or “Application” tab in you Auth0 account, click on “Create New Application”. For the name we can call it “Nuxt App” and for the application type we can pick “Single Page Web Application”.

Once the app is created you can click on the “Settings” tab for the application and you will want to scroll down to the “Allowed Callback URLs”. We will add our development localhost urls such as: http://localhost:3000/callback which will be the page that is redirected to after authentication from auth0. We will also click on the “Connections” tab and we can enable the Username-Password-Authentication Database connection for this application so our user information is stored in a database on auth0. Finally, we can create our first user by navigating to “Users & Roles” and clicking on sub-tab “Users” and then “Create User” button. Fill out the required information for the user and scroll down to app_metadata field for the user and add:

{
  "role": "administrator"
}

Now we have a user with a “role” defined as “administrator”.

Before we setup our “Machine to Machine” application we will need to define and “API” which we will define in order to access the Management API. Click on the “APIs” tab in the left hand column.

  1. Go to the API Explorer tab of your Auth0 Management API
  2. Click the button Create & Authorize a Test Application. A new application has been created and it’s authorized to access the Management API.

The application created in the steps above has been granted all the Management API scopes. This means that it can access all endpoints

https://auth0.com/docs

Now we have our “Machine to Machine” application that will allow us to interface with the Management API in order to get our user’s app_metadata from our application.

With the two applications we have created we can turn our attention to configuring our Nuxt application. For this, we’re going to assume you have a Nuxt.js application installed. If you do not, you can follow the setup instructions: https://nuxtjs.org/guide/installation/

Setting Up Nuxt Auth Module

Our first step will be adding the Nuxt Auth Module to our application and configuring our auth0 provider for the Nuxt Auth module. Let’s install the Nuxt Auth module:

yarn add @nuxtjs/auth
// or
npm install @nuxtjs/auth

Now we can edit our nuxt.config.js and add:

modules: [
  '@nuxtjs/auth'
],
auth: {
  // Options
}

Inside the auth property of our nuxt.config.js file we can define our auth0 provider:

auth: {
  strategies: {
      auth0: {
        domain: 'domain.auth0.com',
        client_id: 'xxxxxxxxxxxxx'
      }
   }
}

You will need to navigate to your “Single Page Application” in Auth0 which we called Nuxt Auth app and copy over the Domain value and Client ID value:

Fill those values in under the auth0 strategy in our nuxt.config.js file. We also need to define our redirect property to equal the “Allowed Callback URLs” so we can add:

auth: {
  strategies: {
      auth0: {
        domain: 'domain.auth0.com',
        client_id: 'xxxxxxxxxxxxx'
      }
   },
   redirect: {
      callback: '/callback'
   },
}

Now that our Nuxt Auth module is configured we can use the login functionality within our file ./pages/login.vue:

<template>
  <button @click="login">Login</button>
</template>
<script>
export default {
  methods: {
    login() {
      this.$auth.loginWith('auth0')
    }
  }
}
</script>

This will redirect us to the domain.auth0.com to authenticate our users and upon authentication it will redirect us back to our http://localhost:3000/callback which is just a temporary page we can create until the nuxt auth module takes over authentication and redirects us to the 'home' key defined in redirect property in nuxt.config.js. Once we are logged in we will have a value in our vuex state that will look like:

{
  auth: {
    user: {
     "sub":"auth0|5add0a735fb3bc1b6xxxxx",
     "nickname":"troy",
     "name":"[email protected]",
     "updated_at":"2020-04-02T14:07:33.221Z",
     "email":"[email protected]",
     "email_verified":true
    }
  }
}

We are verified and we have our auth.user object filled in our Vuex store, but we are missing our user’s app_metadata as that is not returned through the Authentication API (though the endpoint call https://domain.auth0.com/userinfo), but rather the Management API. So how do we make a call to the Management API to receive the user’s app_metadata? We will have to make a “Machine to Machine” connection and use the second auth0 application that we setup to make a call to the Management API endpoint to read user’s app_metadata. We are going to do this by using an Nuxt Auth module’s plugin extension option to write a plugin that will proxy the call to our Nuxt serverMiddleware and in-turn make the call to the Management API.

Let’s start with the Nuxt Auth plugin. We can create a file in our project directory located at ./plugins/appmetadata.js. This file will look like:

import axios from 'axios'
export default async function ({ app: { $auth } }) {
  if (!$auth.loggedIn) {
    return
  }
  const { email } = $auth.$storage.getUniversal('user')
  const appMetaData = $auth.$storage.getUniversal('appMetaData')
  if(!appMetaData) {
    try{
      const { data } = await axios.post(`/api/management`, { email })
      $auth.$storage.setUniversal('appMetaData', data, false)
    }catch(err){
      console.log(err)
    }
  }
}

First, we are importing the axios module so we can use that to create a post request to our serverMiddleware endpoint at /api/management (which we’ll create in a bit). Then we are defining our plugin functionality and checking if the user is logged in with if(!$auth.loggedIn). If the user is not logged in we do not want to make a request to get a users app_metadata since there would be no user in our auth0 account to check against. We are using the $auth.$storage.getUniversal('user') to get the user and destructure the user’s object to obtain the email. We will use the email address in our proxy request to get the user’s app_metadata. Our final check is to make sure that the appMetaData isn’t already set (so we can eliminate an API request to auth0 if we already have the user’s appMetaData stored). Finally, we’re making out axios.post call to our serverMiddleware endpoint and we’re passing in the user’s email as post data to the request.

Before we forget, we need to add this plugin file to our nuxt.config.js auth definition:

auth: {
  plugins: [
    '~/plugins/appmetadata.js'
  ],
  strategies: {
      auth0: {
        domain: 'domain.auth0.com',
        client_id: 'xxxxxxxxxxxxx'
      }
   },
   redirect: {
      callback: '/callback'
   },
}

serverMiddleware to Proxy Request to Auth0

We will need to create a serverMiddleware function to handle our request from the appmetadata.js plugin and proxy the request to our Management API for auth0. You might be wondering “Why can’t I just make the call from the appmetadata.js file to the auth0 management api?” Remember we created the app for “Machine to Machine” in our auth0 account for our Management API because this is a safer way to make requests to the API:

This document describes the Auth0 Management API, which is meant to be used by back-end servers or trusted parties performing administrative tasks. Generally speaking, anything that can be done through the Auth0 dashboard (and more) can also be done through this API.

https://auth0.com/docs/api/management/v2

We will start by creating a file located at ./api/management.js which will become a serverMiddleware express app that we can access with a request to http://localhost:3000/api/management. Let’s start by defining some module requirements for our file:

import express from 'express'
import axios from 'axios'
const app = express()
app.use(express.json())

const clientId = process.env.AUTH0_CLIENT_ID 
const clientSecret = process.env.AUTH0_CLIENT_SECRET


export default {
  path: '/api/management',
  handler: app
}

We are using the dotenv() library and we’re creating two environment variables in our .env file:

AUTH0_CLIENT_ID="ysq6vu9V3GzpY9OTxxxxxxxxxxxx"
AUTH0_CLIENT_SECRET="Stx8HQ-8pwyDx6YqOkIdyf4tZRgMQm46sf5xxxxxxxxxxxxx"

You can find these credentials in your auth0 dashboard under the “Machine to Machine” Application settings:

We are also exporting our default object which defines the endpoint that we will use under the path: '/api/management'. With the two process variables defined we can start defining our express post route:

import express from 'express'
import axios from 'axios'
const app = express()
app.use(express.json())

const clientId = process.env.AUTH0_CLIENT_ID 
const clientSecret = process.env.AUTH0_CLIENT_SECRET

app.post('/', async (req, res, next) => {
  const { body: { email } } = req
})

export default {
  path: '/api/management',
  handler: app
}

We are first getting the email variable from the post body request (which we’re sending with our client request in appmetadata.js file). Now we will need to obtain a “short lived” access_token to be used in our Management API call. We are going to do this use Client Credentials Grant for Auth0 Access Tokens.

Client Credentials Access Token is used for non interactive applications (a CLI, a daemon, or a Service running on your backend) where the token is issued to the application itself, instead of an end user.

https://auth0.com/docs/api-auth/tutorials/client-credentials

Let’s continue defining our middleware post route definition by making a request for an access_token with our client credentials grant type

import express from 'express'
import axios from 'axios'
const app = express()
app.use(express.json())

const clientId = process.env.AUTH0_CLIENT_ID 
const clientSecret = process.env.AUTH0_CLIENT_SECRET

app.post('/', async (req, res, next) => {
  const { body: { email } } = req

  try{
    const { data: { access_token, token_type } } = await axios.post('https://flourish.auth0.com/oauth/token', {
      grant_type: 'client_credentials',
      client_id: clientId,
      client_secret: clientSecret,
      audience: 'https://flourish.auth0.com/api/v2/'
    })

    console.log(access_token, token_type)
    res.end()
  }catch(err){
    console.log(err)
    next(err)
  }

})

export default {
  path: '/api/management',
  handler: app
}

Now that we have a “shorted lived” access token for our Management API we can finally make a request call to the Management API to get the user’s app_metadata information and send that back to our client to set in our vuex store. We are going to get our user using the Management API users endpoint. It will look like:

import express from 'express'
import axios from 'axios'
const app = express()
app.use(express.json())

const clientId = process.env.AUTH0_CLIENT_ID 
const clientSecret = process.env.AUTH0_CLIENT_SECRET

app.post('/', async (req, res, next) => {
  const { body: { email } } = req

  try{
    const { data: { access_token, token_type } } = await axios.post('https://flourish.auth0.com/oauth/token', {
      grant_type: 'client_credentials',
      client_id: clientId,
      client_secret: clientSecret,
      audience: 'https://flourish.auth0.com/api/v2/'
    })

    try{
      const { data: [{ app_metadata: appMetaData }] } = await axios.get(`https://domain.auth0.com/api/v2/users?q=email:"${email}"&search_engine=v3`, {
        headers: {
          "Authorization": `${token_type} ${access_token}`
        }
      })
      res.json(appMetaData)
    }catch(err){
      console.log(err)
      next(err)
    }

  }catch(err){
    console.log(err)
    next(err)
  }

})

export default {
  path: '/api/management',
  handler: app
}

We are using axios to to hit the endpoint https://domain.auth0.com/api/v2/users?q=email:"${email}"&search_engine=v3 make sure you are replacing the domain.auth0.com with your application’s domain name. We are using the query parameter search_engine=v3 and sending the user’s email address to search by (since that is unique to each user). We are using the access_token we obtained in the client credentials request to set our “Authorization” header in our axios request. When the request is returned we are destructuring to the get the user’s app_metadata and we’re storing it in an object appMetaData and using res.json(appMetadata) to return the user’s app_metadata to the client side to be stored.

With our serverMiddleware api endpoint defined we can add the serverMiddleware file to our nuxt.config.js file:

module.exports {
  ...
  serverMiddleware: [
    '~/api/management'
  ],
  ...
}

Now when we restart our development server we’re able to hit our endpoint at: http://localhost:3000/api/management.

Using appMetaData For Checking Permissions In Nuxt Middleware

Nuxt Middleware (not to be confused with serverMiddleware) is a function defined that will be called before route changes (both on server and client in universal mode) which we can use to check our permissions.

Middleware lets you define custom functions that can be run before rendering either a page or a group of pages.

https://nuxtjs.org/guide/routing#middleware

Imagine we have a page that we want only user’s with role: administrator to access. Let’s say this page is located at ./pages/admin.vue we will need to define our middleware property in the page export to check our permissions. We can actually add two middleware definitions. The first is Nuxt Auth Module 'auth' middleware definition and the next is our 'permissions' middleware file (which we will define in a bit). In ./pages/admin.vue we can define:

<template>
  <div>
    <h1>Only accessible by logged in user's with role: administrator</h1>
  </div>
</template>
<script>
export default{
  middleware: ['auth', 'permissions']
}
</script>

Now with that defined we can create a file located at ./middleware/permissions.js which would look like:

export default function({app, redirect}){
  if(app.$auth.loggedIn){
    const appMetaData = app.$auth.$storage.getUniversal('appMetaData')
    if(appMetaData.role !== "administrator"){ 
      return redirect('/')
    }
  }
}

We are double checking the user is logged in using app.$auth.loggedIn and then we are using the Nuxt Auth Module Storage $storage API to get the appMetaData we set before in our appmetadata.js plugin. We are making sure that the appMetaData.role is equal to “administrator” and if not we are redirecting the user to the homepage (you can redirect to login or whatever page you would like). Now if you login by navigating to http://localhost:3000/login you can login using the user we setup in the auth0 dashboard and when you are returned to the site you should be able to successfully navigate to http://localhost:3000/admin page!

Recap Nuxt Auth Module Provider Auth0

We have successfully used the Auth0 Provider defined in our Nuxt Auth Module to call the Management API to get and set a user’s app_metadata information in our application. This is used for data that the user has read-only access to (e.g. roles, permissions, vip, etc). To review we had to:

  • Install and Configure our Nuxt Auth Module
  • Create Two Applications in the Auth0 Dashboard
  • Create a Nuxt Auth Module Plugin to Proxy a Request to serverMiddleware
  • Define serverMiddleware that makes a “Machine to Machine” call to Auth0 Management API
  • Create a permissions.js middleware function that check’s a user’s appMetadata.role and set a protected route at http://localhost:3000/admin

Hopefully this tutorial helps understand the inner-workings of the Nuxt Auth Module and using the Auth0 Provider for authentication and permission based user management. Until next time, stay curious, stay creative!