If you’re building a single-page app with Vue.js, you may need to handle the permissions for each user on the client-side. In most cases, permissions are only a set of keys assigned for each user, indicate whether the user allowed to do something or not.

Here I’m going to cover how to handle the permissions across the entire Vue.js app.

The can mixin

First, I would build a global mixin and inject a can method to all Vue components. This method takes the permission key as input and would determine whether the user has this permission or not.

I hard-coded the permissions set, in your case you should grant this list from the back-end of your app.


const userPermissions = ['dashboard', 'users', 'tasks']

Vue.mixin({
    methods: {
       can: (key) => userPermissions.includes(key)
    }
})

As simple as that? Yes.

Now in any Vue component you can just type can('permission_key') to allow / disallow the user from doing something, or hide / show certain UI components based on what can returns.

For example.. only fetch the tasks if the user has tasks permission

mounted() {
  this.can('tasks') && fetch_tasks()
}

Or.. only show the Dashboard link if the user has the dashboard permission

<li v-if="can('dashboard')">
  <a href="/dashboard">Dashboard</a>
</li>

Vue-router with Permissions

Now if the permission occurs on the level of pages / routes, and not only in components or primitive actions, the same can logic should be integrated with the Vue router.

Let’s have the blow Router object:

export const router = new Router({
    routes: [
        {
            path: '/',
            name: 'dashboard',
            meta: {
                permissions: ['dashboard']
            },

            component: Dashboard,
        {
            path: '/users',
            name: 'users',
            meta: {
                permissions: ['users']
            },

            component: Users,
        },
        {
            path: '/login',
            name: 'login',
            component: Login
        }

        // ...
        // ...
        // etc...
    ],
})

With each guarded route, I added a permissions array to the route’s optional meta field.

Next, let’s hook into beforEach, and handle the authorization logic:

const userPermissions = ['dashboard', 'users']

router.beforeEach((to, from, next) => {
    if (to.meta.permissions.length > 0) {
        let is_allowed = userPermissions.some(p => to.meta.permissions.includes(p))
        
        if (! is_allowed) return next('/')
    }

    next()
})

Any route with permissions meta field will be guarded against the user’s permissions.

That’s it!
Thanks for reading.