Saul Salazar
  • About
  • Posts

Vue/Quasar Authentication & Authorization - Thu, Jul 22, 2021

Secure your application updating your Vue Router. Add Role-Based Access Control easily.

Notes

  • Example used on Vue/Quasar projects using localStorage.
  • Expects the user object to have an array of strings, each one represents a role { roles: ["user"] }
  • Redirects to /login on failure with param redirectTo

How to

  1. Create and copy the file src/plugins/auth.js
  2. Update you quasar.conf.js plugins property
  3. Update your src/router/routes.js, each vue route can have a meta property
    • We will use requiresAuth (boolean) and allowedRoles ([string array])

Route Example

{
  path: '/',
  component: () => import('layouts/MyLayout.vue'),
  meta: {
    requiresAuth: true, allowedRoles: ['user', 'admin']
  },
  children: [
    { path: 'user/my-profile', component: () => import('pages/user/my-profile.vue') }
  ]
}

Functions

  • validateAxiosAuthHeader is used as a hotfix on production, tech debt no documentation except that it was related to reloading the browser process
  • validateRouteAuthentication works using localStorage item token, check if is null or not
  • validateRouteAuthorization works using localStorage item user, the property roles from the user object, RBAC-like

src/plugins/auth.js

import { Notify } from 'quasar'
import { axios } from './axios'

const notifyAuthenticationError = (message) => Notify.create({
  color: 'negative',
  position: 'top',
  icon: 'report_problem',
  message: `Error de autentificación: ${message || 'Por favor revisa tus permisos'}`
})

function validateAxiosAuthHeader (to, from, next) {
  const jsonWebToken = localStorage.getItem('token')

  if (jsonWebToken) {
    localStorage.setItem('token', jsonWebToken)
    axios.setJsonWebToken(jsonWebToken)
  }

  next()
}

// to, from and next and route objects (see https://router.vuejs.org/api/#the-route-object)
// router.matched or to.matched
//   returns an Array containing route records for
//   all nested path segments of the current route.
// Array.prototype.some()
//   tests whether at least one element in the array passes the test implemented by the provided function.
function validateRouteAuthentication (to, from, next) {
  const requiresAuthorization = to.matched.some(route => route.meta.requiresAuth)
  const jsonWebToken = localStorage.getItem('token')

  // this route requires auth
  if (requiresAuthorization) {
    if (jsonWebToken) {
      return next()
    } else {
      const redirect = {
        path: '/login',
        query: { redirect: to.fullPath }
      }

      console.log(`validateRouteAuthentication failed, redirecting to ${redirect.path} (trying to access ${redirect.query.redirect})`)
      notifyAuthenticationError('Por favor inicia sesión con tus credenciales de Softtek')
      return next(redirect)
    }
  }

  // route doesn't require authentication
  return next()
}

function validateRouteAuthorization (to, from, next) {
  const requiresAuthorization = to.matched.some(route => route.meta.allowedRoles)
  const user = JSON.parse(localStorage.getItem('user'))

  // this route requires auth
  if (requiresAuthorization) {
    for (var i = 0; i < to.matched.length; i++) {
      const route = to.matched[i]

      if (route.allowedRoles) {
        const userHasValidRole = route.meta.allowedRoles.some(allowedRole => user.roles.some(userRole => userRole === allowedRole))

        if (userHasValidRole) {
          return next()
        } else {
          const redirect = {
            path: '/login',
            query: { redirect: to.fullPath }
          }

          console.log(`validateRouteAuthorization failed, redirecting to ${redirect.path} (trying to access ${redirect.query.redirect})`)
          notifyAuthenticationError('No autorizado')
          return next(redirect)
        }
      }
    }
  }

  // route doesn't require authentication
  return next()
}

// leave the export, even if you don't use it
export default ({ app, router, Vue }) => {
  router.beforeEach(validateAxiosAuthHeader)
  router.beforeEach(validateRouteAuthentication)
  router.beforeEach(validateRouteAuthorization)
}

Back to Home


© 2024

Linkedin GitHub GitLab

▄