Learner
Learner

Reputation: 773

Nuxt auth update auth on custom requests

From nuxt auth website I saw this:

setUserToken(token)
Returns: Promise
Set the auth token and fetch the user using the new token and current strategy.

TIP: This function can properly set the user after registration

this.$auth.setUserToken(token)
  .then(() => this.$toast.success('User set!'))

Tried to use it and it said method is undefined, looked up in the source files and none of methods are like this one.

I am not very good with this but, how would I set user and token with nuxt/auth module after registration or anything but login/loginWith?

If there is no option for that why is it there on documentation?

I would also need to know if I need to create custom auth do I need to use both cookies and localstorage or just one of them? It says that cookies are used for server side and storage for client side. Can I use just cookies and on nuxtServerInit get cookie for token and set token and user data fetched by api within vuex store? Then use it from there if it is needed?

Upvotes: 1

Views: 2765

Answers (1)

Learner
Learner

Reputation: 773

Nuxt/auth module hurt my brain so long and today I created custom module:

First I have this store structure:

store/
-- index.js
-- mutations.js
-- actions.js
-- state.js
-- getters.js
middleware/
-- redirectIfAuth.js
-- redirectIfNotAuth.js
layouts/
default.vue -> has redirectIfNotAuth.js
guest.vue -> has redirectIfAuth.js
pages/
-- login/
---- index.vue -> uses guest.vue as layout
-- dashboard/
----- index.vue -> uses default.vue as layout without declaration

Inside Index.js I have:

import state from './state'
import * as actions from './actions'
import * as mutations from './mutations'
import * as getters from './getters'

export default {
  state,
  getters,
  mutations,
  actions,
  modules: {}
}

Inside State.js I have:

export default () => ({
  user: null,
  token: null,
  headers: null
})

Inside Actions.js I have:

const cookieparser = process.server ? require('cookieparser') : undefined
// importing server based cookie library

export async function nuxtServerInit ({ commit }, { req, res }) {

  // If we have any axios requests we need to add async/await
  // And since this works on server mode, we don't need to check is it server

  let token = null
  if (req.headers.cookie) {
    const parsed = cookieparser.parse(req.headers.cookie)
    try {
      token = parsed.authToken
    } catch (e) {
      console.log(e)
    }
  }
  // If we have token within cookies we get user data from api and we pass Autorization headers with token
  if (token !== null && token !== false) {
    await axios.get('/api/auth/me', {
      headers: {
        'Authorization': token
      }
    }).then((response) => {
      // If  we get user data we set it to store
      commit('setUser', response.data.data)
      commit('setToken', token)
      commit('setHeaders', token)
    }).catch((error) => {
      // If we get error, we should logout user by removing data within cookies and store
      // Additionally you can create specific code error on backend to check if token is expired or invalid
      // and then check for status code and then remove data
      commit('setUser', null)
      commit('setToken', null)
      res.setHeader('Set-Cookie', [`authToken=false; expires=Thu, 01 Jan 1970 00:00:00 GMT`])
      // This is only way I found useful for removing cookies from node server
      console.warn(error)
    })
  }
}

Inside Mutations.js I have:

export const setToken = (state, payload) => state.token = payload

export const setUser = (state, payload) => state.user = payload

export const setHeaders = (state, payload) => {
  state.headers = {
    headers: {
      'Authorization': payload
    }
  }
}

Inside Getters.js I have:

export const getUser = (state) => state.user
export const getToken = (state) => state.token
export const getHeaders = (state) => state.headers

Second I created two middlewares and it seems like nuxt middlewares work on both server and client sides, so I needed to require both libraries for server and client side Then I checked which side it is and then try to get token for further investigations If you include and don't check for server and client but use one of them, your templates wont render but show undefined errors for req on client instead and on server it wont show anything.

Inside redirectIfAuth.js I have:

const cookieparser = process.server ? require('cookieparser') : undefined
const Cookie = process.client ? require('js-cookie') : undefined
export default function ({ app, redirect, req }) {
  let token = null
  if (process.server) {
    if (req.headers.cookie) {
      const parsed = cookieparser.parse(req.headers.cookie)
      try {
        token = parsed.authToken
      } catch (e) {
        console.log(e)
      }
    }
  } else if (process.client) {
    token = Cookie.get('authToken')
  }
  if (token && token !== false) {
    app.store.commit('setToken', token)
    app.store.commit('setHeaders', token)
    if (app.store.state.user) {
      if (app.store.state.user.roles.includes('customer')) {
        return redirect({
          name: 'customer-slug',
          params: { slug: app.store.state.user.username }
        })
      } else if (app.store.state.user.roles.includes('admin')) {
        return redirect({
          name: 'dashboard'
        })
      } else {
        return redirect({
          name: 'index'
        })
      }
    } else {
      return redirect({
        name: 'index'
      })
    }
  }
}

Inside redirectIfNotAuth.js I have:

const cookieparser = process.server ? require('cookieparser') : undefined
const Cookie = process.client ? require('js-cookie') : undefined
export default function ({ app, redirect, route, req }) {
  let token = null
  if (process.server) {
    if (req.headers.cookie) {
      const parsed = cookieparser.parse(req.headers.cookie)
      try {
        token = parsed.authToken
      } catch (e) {
        console.log(e)
      }
    }
  } else if (process.client) {
    token = Cookie.get('authToken')
  }
  if (token === null || token === false) {
    return redirect({
      name: 'login',
      query: {
        redirect: route.fullPath
      }
    })
  }
}

Now we use these middlewares within pages or layouts as:

export default {
  middleware: ['redirectIfAuth']
}

Or

export default {
  middleware: ['redirectIfNotAuth']
}

Login:

async login () {
      if (this.form.email !== '' && this.form.password !== '') {
        await this.$axios.post('/api/auth/login', this.form).then((response) => {
          this.$store.commit('setUser', response.data.data)
          this.$store.commit('setToken', 'Bearer ' + response.data.meta.access_token)
          this.$store.commit('setHeaders', 'Bearer ' + response.data.meta.access_token)
          Cookie.set('authToken', 'Bearer ' + response.data.meta.access_token, { expires: 365 })
          // Cookie.set('authUser', response.data.data, { expires: 365 }) if you need user data within cookies
          if (this.$route.query.redirect) {
            this.$router.push(this.$route.query.redirect)
          }
          this.$router.push('/')
        })
      }
    }

Logout:

async logout () {
   await this.$axios.post('/api/auth/logout', {}, this.headers)
   // Cookie.remove('authUser') if exists
   Cookie.remove('authToken')
   this.$router.push('/')
}

I hope this helps someone or someone get idea from this to make something else. I had million problems with official nuxt auth and only this helped me sort things out...

Upvotes: 2

Related Questions