DEV Community

Mandeep Singh Gulati
Mandeep Singh Gulati

Posted on

How to add authentication to your universal Nuxt app using nuxt/auth module?

nuxt

Recently I was working on a Nuxt.js app and had to add authentication to it. First thing I thought was to use vuex to store two fields in a state:

  • isLoggedIn: a boolean representing whether user is logged in or not
  • loggedInUser: an object containing the user details for the session that we get from server

And then I added a middleware on pages where I wanted to restrict access to logged in users only. The thought process for this approach is right but problem is that when you refresh the page, the state from vuex is lost. In order to handle that, you would need to use localStorage but that would work only if your app is running in spa mode, that is, on client side only. If you are running your app in universal mode (server side rendered) then you will also need to use cookies and write a custom middleware that checks whether it is running on client side or server side and then use localStorage or cookies accordingly. Doing all of this would be a good exercise to learn how everything works but adding it to a project where multiple people are working might not be a great idea in my opinion. Nuxt has an officially supported module just for this purpose. It's the auth module. In this post, I will talk about how to integrate the auth module to your nuxt app to support authentication using email and password.

Assumptions for the server API

We are making the assumption that the API server:

  • Is running on http://localhost:8080/v1
  • Uses cookie based sessions
  • Has a JSON based API
  • Has the following API endpoints:
    • POST /v1/auth/login: accepts email and password in request body and authenticates the user
    • POST /v1/auth/logout: does not need request body and deletes the user session from server
    • GET /v1/auth/profile: returns the logged in user's object

Overview of the steps involved

We will divide this post into following steps:

  • Installation of axios and auth modules
  • Configuration needed in nuxt.config.js
  • Using the state from auth module to check if user is logged in or not and accessing logged in user in our app components
  • Using the auth module to authenticate the user using email and password based authentication
  • Using middleware provided by the auth module to restrict access to pages to logged in users only

Step 1: Install the axios and auth modules

Open the terminal, navigate to the root directory of your project and run the following command:

npm install @nuxtjs/auth @nuxtjs/axios
Enter fullscreen mode Exit fullscreen mode

Step 2: Configure axios and auth modules

Open your nuxt.config.js file, find the modules section and include the axios and auth modules and add their configuration:

  modules: [
    '@nuxtjs/axios',
    '@nuxtjs/auth'
  ],

  auth: {
    strategies: {
      local: {
        endpoints: {
          login: {
            url: '/auth/login',
            method: 'post',
            propertyName: false
          },
          logout: { 
            url: '/auth/logout', 
            method: 'post' 
          },
          user: { 
            url: '/auth/profile', 
            method: 'get', 
            propertyName: false 
          }
        },
        tokenRequired: false,
        tokenType: false
      }
    }
  },

  axios: {
    baseURL: 'http://localhost:8080/v1',
    credentials: true
  },

Enter fullscreen mode Exit fullscreen mode

The auth object here includes the configuration. The auth module supports local strategy as well as OAuth2. Since we only have email and password based authentication in our case, we only need to provide the configuration for local strategy.

The endpoints section is where we specify the details about our API server's endpoints for login, logout and logged in user's profile and each of the config looks like this:

  user: { 
    url: '/auth/profile', 
    method: 'get', 
    propertyName: false 
  }          
Enter fullscreen mode Exit fullscreen mode

url and method should be consistent with your server API. The url here needs to be relative to the baseUrl config. The propertyName tells the auth module which property in the response object to look for. For example, if your API server reponse for GET /auth/profile is like this:

{
  "user": {
    "id: 1,
    "name": "Jon Snow",
    "email": "jon.snow@asoiaf.com"
  }
}
Enter fullscreen mode Exit fullscreen mode

Then you can set the propertyName as user to look for only the user key in the API response. If you want to use the entire API response, you need to set propertyName to false.

Since our API server has cookie based sessions, we are setting the tokenRequired and tokenType to false.

tokenRequired: false,
tokenType: false
Enter fullscreen mode Exit fullscreen mode

For a complete list of options supported by the auth module, you can visit their official documentation here

The axios object in the above config is used to provide the axios configuration. Here, we are setting the following properties:

  axios: {
    baseURL: 'http://localhost:8080/v1',
    credentials: true
  },
Enter fullscreen mode Exit fullscreen mode

baseUrl here is the root url of our API and any relative url that we hit using axios in our app will be relative to this url. Setting credentials as true ensures that we send the authentication headers to the API server in all requests.

Step 3: Activate vuex store in your app

In order to use the auth module, we need to activate vuex store in our application since that's where the session related information will be stored. This can be done by adding any .js file inside the store directory of your app and nuxt will register a namespaced vuex module with the name of the file. Let's go ahead and add a blank file called index.js to the store directory of our app. It's not mandatory to add index.js file. You could have added any file for example xyz.js in the store directory and that would have activated vuex store in your app.

The auth module that we have included in our project will automatically register a namespaced module named auth with the vuex store. And it has the following fields in the state:

  • loggedIn: A boolean denoting if the user is logged in or not
  • user: the user object as received from auth.strategies.local.user endpoint configured in our nuxt.config.js file.
  • strategy: This will be local in our case

It also adds the necessary mutations for setting the state. So, even though we haven't created any auth.js file in the store directory of our app, the auth module has automatically taken care of all this. If it helps to understand, imagine that a file named auth.js is automatically created by auth module in the store directory of your app even though this file doesn't actually exists. This means that using mapState on auth module of your vuex store will work. For example, you can use this in any of your components or pages:

  computed: {
    ...mapState('auth', ['loggedIn', 'user'])
  },
Enter fullscreen mode Exit fullscreen mode

Here is a complete example of a component using these properties:

<template>
  <b-navbar type="dark" variant="dark">
    <b-navbar-brand to="/">NavBar</b-navbar-brand>
    <b-navbar-nav class="ml-auto">
      <b-nav-item v-if="!loggedIn" to="/login">Login</b-nav-item>
      <b-nav-item v-if="!loggedIn" to="/register">Register</b-nav-item>
      <b-nav-item v-if="loggedIn" @click="logout">
        <em>Hello {{ user.name }}</em>
      </b-nav-item>
      <b-nav-item v-if="loggedIn" @click="logout">Logout</b-nav-item>
    </b-navbar-nav>
  </b-navbar>
</template>

<script>
import { mapState } from 'vuex'
export default {
  name: 'NavBar',
  computed: {
    ...mapState('auth', ['loggedIn', 'user'])
  },
  methods: {
    async logout() {
      await this.$auth.logout()
      this.$router.push('/login')
    }
  }
}
</script>

<style></style>

Enter fullscreen mode Exit fullscreen mode

Alternative approach

Instead of using the mapState, you can also reference the loggedIn and user by this.$auth.loggedIn and this.$auth.user. So, in the above example, you could have re-written the computed properties as mentioned below and it would have still worked fine:

  computed: {
    loggedIn() {
      return this.$auth.loggedIn
    },
    user() {
      return this.$auth.user
    }
  },
Enter fullscreen mode Exit fullscreen mode

Step 4: Authenticating user using the auth module

We know how to use the auth module's APIs to check whether a user is logged in or not, or access the logged in user's details. But we haven't yet covered the part of how to authenticate the user. This is done by using the this.$auth.loginWith method provided by the auth module in any of your components or pages. The first argument to this function is the name of the strategy. In our case this will be local. It's an async function which returns a promise. Here is an example of how to use it:

  try {
    await this.$auth.loginWith('local', {
      data: {
        email: 'email@xyz.com'
        password: 'password',
      }
    })
    // do something on success
  } catch (e) {    
    // do something on failure 
  }

Enter fullscreen mode Exit fullscreen mode

So, typically you would have a login page with a form with email and password fields mapped to data of the component using v-model. And once you submit the form, you can run this function to authenticate using the auth module. Here is an example of login page:

<template>
  <div class="row">
    <div class="mx-auto col-md-4 mt-5">
      <b-card>
        <b-form @submit="submitForm">
          <b-form-group
            id="input-group-1"
            label="Email address:"
            label-for="email"
          >
            <b-form-input
              id="email"
              v-model="email"
              type="email"
              required
              placeholder="Enter email"
            ></b-form-input>
          </b-form-group>

          <b-form-group
            id="input-group-2"
            label="Password:"
            label-for="password"
          >
            <b-form-input
              id="password"
              v-model="password"
              type="password"
              required
              placeholder="Enter password"
            ></b-form-input>
          </b-form-group>

          <b-button type="submit" variant="primary">Login</b-button>
        </b-form>
      </b-card>
    </div>
  </div>
</template>

<script>
export default {
  name: 'LoginPage',
  data() {
    return {
      email: '',
      password: ''
    }
  },
  methods: {
    async submitForm(evt) {
      evt.preventDefault()
      const credentials = {
        email: this.email,
        password: this.password
      }
      try {
        await this.$auth.loginWith('local', {
          data: credentials
        })
        this.$router.push('/')
      } catch (e) {
        this.$router.push('/login')
      }
    }
  }
}
</script>

<style></style>

Enter fullscreen mode Exit fullscreen mode

In order to logout a logged in user, you can use the this.$auth.logout method provided by the auth module. This one doesn't need any arguments. Here is an example:

  methods: {
    async logout() {
      await this.$auth.logout()
      this.$router.push('/login')
    }
  }
Enter fullscreen mode Exit fullscreen mode

Step 5: Using auth middleware to restrict access to certain pages

The auth module also provides middleware to restrict access to logged in users. So, for example if you want to restrict the /profile route of your application to logged in users only, you can add the auth middleware to the profile.vue page like this:

export default {
  name: 'ProfilePage',
  middleware: ['auth']
}
Enter fullscreen mode Exit fullscreen mode

For more details on how you can configure your components and pages to use the auth middleware, you can check out the official docs here.

Conclusion and References

This was kind of a getting started post for axios and auth modules with NuxtJS. We only covered the local strategy but the auth module also supports OAuth2 and can be used to support login using Auth0, Facebook, Github and Google. I would definitely recommend checking out the Guide and API section of the auth module:

https://auth.nuxtjs.org/

The axios module also provides us many configuration options. Although we didn't cover much of it in this post, but I would definitely recommend checking out the official docs for that as well:

https://axios.nuxtjs.org/

I hope this post was helpful in understanding the basics of auth module in Nuxt and makes it easier for you to navigate the rest of the official documentation on your own.

Happy coding :-)

Top comments (13)

Collapse
 
slidenerd profile image
slidenerd

I appreciate this post but you have not shown anything about what you have done on the server side

Collapse
 
cds profile image
cds

how can i save user to database(mongodb). Can we save the callback/ response from google (user/profile ) to database while adding to vuex store. If yes then how ? I had done some (hack) approch now i am saving user data to db after the user/ profile save to vuex store or store is set to user but i need to save callback directly from google or better approch

Collapse
 
cds profile image
cds

I am working on my own project i dont want to save users password in my db just want to implement google/facebook login without local auth. But i need user email (uniqueid) to save other data for reference later

Collapse
 
bbarbour profile image
Brian Barbour

What's happening on your API end points? Are you returning a token or something?

Collapse
 
richardcullen profile image
RichardCullen

Need to include some sample code to show how to implement the server-side of the Auth API.

Collapse
 
zloypacifist profile image
zloypacifist • Edited

I've made 2 api endpoints on my server localhost:8080 for login and profile.
On login endpoint: if user credentials is valid, set cookie 'myauth' with some unique value (token), and save this token for user in DB.
On profile endpoint: if cookie 'myauth' set and user found by token, return json with it's profile info, for example:
{
"id: 1,
"name": "Jon Snow",
"email": "jon.snow@asoiaf.com"
}
else return 401 status code with empty response.

After this manipulating my nuxt app start to login users. I've used auth middleware in nuxt config for all routings. I've used Lumen framework for api server

Collapse
 
dkwlb profile image
DKWLB

Good article, thanks. But what should i fix with cookie approach if i redirect always to login page if i refresh the page?
Other words if i refresh - i get loggedIn false for some seconds

Collapse
 
johnywhyte profile image
johnywhyte

i can't preserve my auth sate. everything is cleared when i refresh

Collapse
 
muriithigachanja profile image
Ken Gachanja

Nice article but there is no data persistence

Collapse
 
leyterpalacios profile image
leyter palacios

hello, thanks for the tutorial, very good ... you will have an example of this tutorial using auth token from spring security please!

Collapse
 
johnywhyte profile image
johnywhyte

I can't preserve my auth sate. everything is cleared when I refresh

Collapse
 
yiyasha15 profile image
Aishwarya Shrestha • Edited

I followed the same method but my auth state still goes to null on page refresh!
What could be the issue?

Collapse
 
yiyasha15 profile image
Aishwarya Shrestha

Solved it using vuex-persist lugin!