How to save JWT Token in Vuex with Nuxt Auth Module?

11,731

I think I can answer my own question.

I searched the whole time for an error regarding to my api response. The problem was the "propertyName" on user endpoint in the nuxt.config.js.

It is set to "user" as default. When I set it to "propertyName: false", than everything works as it should.

 auth: {
    strategies: {
      local: {
        endpoints: {
          login: {url: '/login', method: 'post', propertyName: 'token' },
          user: {url: '/user', method: 'get', propertyName: false },
          logout: false,
        }
      }
    }
  },
Share:
11,731
Jakob Kühne
Author by

Jakob Kühne

Updated on June 04, 2022

Comments

  • Jakob Kühne
    Jakob Kühne over 1 year

    I am currently trying to convert a VueJS page to NuxtJS with VueJS. Unfortunately I have some problems with authenticating the user and I can't find a solution in Google. I only use Nuxt for the client. The API is completely separate in express and works with the existing VueJS site.

    In Nuxt I send now with the Auth module a request with username and password to my express Server/Api. The Api receives the data, checks it, and finds the account in MongoDB. This works exactly as it should. Or as I think it should. Now I take the user object and generate the jwt from it. I can debug everything up to here and it works. Now I probably just don't know how to keep debugging it. I send an answer with res.json(user, token) back to the Nuxt client (code follows below). As I said, in my current VueJS page I can handle this as well. Also in the Nuxt page I see the answer in the dev console and to my knowledge the answer fits.

    Now some code. The login part on the express Api:

    
        const User = require('../models/User')
        const jwt = require('jsonwebtoken')
        const config = require('../config/config')
    
        function jwtSignUser(user){
            const ONE_YEAR = 60 * 60 * 24 * 365
            return jwt.sign(user,config.authentication.jwtSecret, {
                expiresIn: ONE_YEAR
            })
        }
        module.exports = {
            async login (req, res){
                console.log(req.body)
                try{
                    const {username, password} = req.body
                    const user = await User.findOne({
                        username: username
                    })
    
                    if(!user){
                        return res.status(403).send({
                            error: `The login information was incorrect.`
                        })
                    }
    
                    const isPasswordValid = await user.comparePassword(password)
                    if(!isPasswordValid) {
                        return res.status(403).send({
                            error: `The login information was incorrect.`
                        })
                    }
    
                    const userJson = user.toJSON()
                    res.json({
                        user: userJson,
                        token: jwtSignUser(userJson)
                    })
    
                } catch (err) {
                    console.log(err)
                    res.status(500).send({
                        error: `An error has occured trying to log in.`
                    })
                }
            }
        }
    
    

    nuxt.config.js:

    
        auth: {
            strategies: {
              local: {
                endpoints: {
                  login: {url: '/login', method: 'post' },
                  user: {url: '/user', method: 'get' },
                  logout: false,
                }
              }
            },
            redirect: {
              login: '/profile',
              logout: '/',
              user: '/profile',
              callback:'/'
            }
          }
    
    

    even tried it with nearly any possible "propertyName".

    and, last but not least, the method on my login.vue:

    
    
        async login() {
              try {
                console.log('Logging in...')
                await this.$auth.loginWith('local', {
                  data: {
                    "username": this.username,
                    "password": this.password
                  }
                }).catch(e => {
                  console.log('Failed Logging In');
                })
                if (this.$auth.loggedIn) {
                  console.log('Successfully Logged In');
                }
              }catch (e) {        
                console.log('Username or Password wrong');
                console.log('Error: ', e);
              }
            }
    
    

    What I really don't understand here... I always get "Loggin in..." displayed in the console. None of the error messages.

    I get 4 new entries in the "Network" Tag in Chrome Dev Tools every time I make a request (press the Login Button). Two times "login" and directly afterwards two times "user".

    The first "login" entry is as follow (in the General Headers):

    Request URL: http://localhost:3001/login
    Request Method: OPTIONS
    Status Code: 204 No Content
    Remote Address: [::1]:3001
    Referrer Policy: no-referrer-when-downgrade
    

    The first "user" entry:

    Request URL: http://localhost:3001/user
    Request Method: OPTIONS
    Status Code: 204 No Content
    Remote Address: [::1]:3001
    Referrer Policy: no-referrer-when-downgrade
    

    Both without any Response.

    The second login entry:

    Request URL: http://localhost:3001/login
    Request Method: POST
    Status Code: 200 OK
    Remote Address: [::1]:3001
    Referrer Policy: no-referrer-when-downgrade
    

    and the Response is the object with the token and the user object.

    The second user entry:

    Request URL: http://localhost:3001/user
    Request Method: GET
    Status Code: 200 OK
    Remote Address: [::1]:3001
    Referrer Policy: no-referrer-when-downgrade
    

    and the Response is the user object.

    I think for the login should only the login request be relevant, or I'm wrong? And the user request works because the client has asked for the user route and the user route, always send the answer with the actual user object in my Express API.

    Because I think, the problem is in the login response? Here some screenshots from the Network Tab in Chrome Dev Tools with the Request/Response for login.

    First login request without response
    Second login request
    Response to second login request

    Do I have to do something with my Vuex Store? I never found any configured Vuex Stores in examples for using the Auth Module while using google so I thougt I do not have to change here anything.

    Thats my Vuex Store (Vue Dev Tools in Chrome) after trying to login without success:

    {"navbar":false,"token":null,"user":null,"isUserLoggedIn":false,"access":false,"auth":{"user":"__vue_devtool_undefined__","loggedIn":false,"strategy":"local","busy":false},"feedType":"popular"}
    

    There is also some logic I use for my actual VueJS site. I will remove that when the Auth Module is working.

    Asked by @imreBoersma : My /user endpoint on Express looks like:

    
        app.get('/user', 
                isAuthenticated,
                UsersController.getUser)
    
    

    I first check if the User is authenticated:

    
        const passport = require('passport')
    
        module.exports = function (req, res, next) {
            passport.authenticate('jwt', function (err, user) {
                if(err || !user) {
                    res.status(403).send({
                        error: 'You are not authorized to do this.'
                    })
                } else {
                    req.user = user
                    next()
                }
            })(req, res, next)
        }
    
    

    After that I search the User document in MongoDB and send the document to the client:

    
        const User = require('../models/User')
    
        module.exports = {
            [...]
            getUser (req, res) {
                User.findById(req.user._id, function (error, user){
                    if (error) { console.error(error); }
                    res.send(user)
                })
            }
            [...]
    
        }
    
    

    Feel free to ask for more information.

  • Syam kumar KK
    Syam kumar KK almost 4 years
    How is your api response json ?
  • Jakob Kühne
    Jakob Kühne almost 4 years
    Hey @SyamkumarKK, thanks for that edit. My response is the total User document (MongoDB) right now exept the logged in devices, because they change to much. But yes, I know it's not the best way and I will change that behaviour any time in future.