Skip to content

Instantly share code, notes, and snippets.

@hfalucas
Last active March 13, 2024 05:49
Show Gist options
  • Star 93 You must be signed in to star a gist
  • Fork 24 You must be signed in to fork a gist
  • Save hfalucas/60cb40c62e2e13e6c797f4887e43c8f6 to your computer and use it in GitHub Desktop.
Save hfalucas/60cb40c62e2e13e6c797f4887e43c8f6 to your computer and use it in GitHub Desktop.
[Vue.js] Authentication and Authorization
/**
* Think of this "main.js" file as your application bootstrap.
*/
import Vue from 'vue'
import Resource from 'vue-resource'
import VueRouter from 'vue-router'
import routes from './routes'
import middleware from './middleware'
// Your app mounting point
import ExampleComponent from './ExampleComponent.vue'
/**
* Documentation link for Vue Router
* http://vuejs.github.io/vue-router/en/index.html
*/
Vue.use(VueRouter)
/**
* Documentation for Vue Resource
* https://github.com/vuejs/vue-resource
*/
Vue.use(Resource)
/**
* Router Configuration
*
* @type {VueRouter}
*/
var router = new VueRouter({
hashbang: false,
history: true
})
/**
* App Routes
*/
routes(router)
/**
* App Middleware
*/
middleware(router)
/**
* Application mount and initialization
*/
router.start(ExampleComponent, '#app')
export default function routes (router) {
router.map({
'/login': {
name: 'login',
component: require('./components/Auth/Login.vue')
},
'/example-component': {
name: 'example-component',
/**
* vue-routes allow us to create custom objects in the routes files.
*
* This "access" object is created by us and will be used to check the
* current logged in user permissions on every route change.
*/
access: {
requiresLogin: true,
requiredPermissions: ['admin'],
permissionType: 'AtLeastOne' // options: AtLeastOne, CombinationRequired
},
component: require('./components/ExampleComponent.vue')
},
'/other-component': {
name: 'other-component',
access: {
requiresLogin: true,
requiredPermissions: ['admin', 'manager'],
permissionType: 'AtLeastOne'
},
component: require('./components/other-component.vue')
}
})
}
<template>
<div>
<form @submit.prevent="authenticate()">
<label for="">E-mail:</label>
<input v-model="auth.email" type="email">
<label for="">Password</label>
<input v-model="auth.password" type="password">
<button type="submit">Login</button>
</form>
</div>
</template>
<script>
export default {
data () {
return {
auth: { email: '', password: '' },
user: {},
}
},
methods: {
/**
* Attempts to authenticate the user
*
* @return {mixed}
*/
authenticate () {
let credentials = this.auth
this.$http.post('http://api.dev/login', credentials).then((response) => {
/**
* Now that we successfully retrieved the token and the user information
* we have a couple of options:
*
* 1) Save the token in local storage
* - Keeps the token saved even when the browser is closed
* 2) Save the token in session storage
* - Deletes the token when user closes the browser or even the tab
* 3) Save the token in a cookie
*
* Both local and session storage api are the same so I'll use the local storage
* for the sake of the example
*
*/
window.localStorage.setItem('token', response.data.token)
window.localStorage.setItem('auth-user', JSON.stringify(response.data.user))
this.$route.router.go({name: 'example-component'})
}).catch((errors) => {
// catch errors
})
}
}
}
</script>
/**
* This code block can be directly in your main.js file if you keep it this simple
* otherwise extract it to its own file and require it in main.js
*
* Don't forget that the code below will be executed within every request.
*
*/
Vue.http.interceptors.push((request, next) => {
/**
* Here we will fecth the token from local storage and
* attach it (if exists) to the Authorization header on EVERY request.
*/
let token = window.localStorage.getItem('token')
if (token) {
request.headers = request.headers || {}
request.headers.Authorization = `Bearer ${token}`
}
/**
* Here is where we can refresh the token.
*/
next((response) => {
/**
* If we get a 401 response from the API means that we are Unauthorized to
* access the requested end point.
* This means that probably our token has expired and we need to get a new one.
*/
if (response.status === 401) {
return Vue.http.get('http://api.dev/refresh-token').then((result) => {
// Save the new token on local storage
window.localStorage.setItem('token', result.data.token)
// Resend the failed request whatever it was (GET, POST, PATCH) and return its resposne
return Vue.http(request).then((response) => {
return response
})
})
.catch(() => {
/**
* We weren't able to refresh the token so the best thing to do is
* logout the user (removing any user information from storage)
* and redirecting to login page
*/
return router.go({name: 'login'})
})
}
})
})
import UserHasPermissions from './Middleware/UserHasPermissions'
import RedirectIfAuthenticated from './Middleware/RedirectIfAuthenticated'
export default function middleware (router) {
UserHasPermissions(router)
RedirectIfAuthenticated(router)
}
export default function RedirectIfAuthenticated (router) {
/**
* If the user is already authenticated he shouldn't be able to visit
* pages like login, register, etc...
*/
router.beforeEach(({to, next, abort, redirect}) => {
let token = window.localStorage.getItem('token')
let user = JSON.parse(window.localStorage.getItem('auth-user'))
/**
* Checks if there's a token and the next page name is none of the following
*/
if ((token) && (to.name === 'login' || to.name === 'register')) {
// redirects according user role
router.go({ /*...*/})
}
if (!token) {
// Logout
}
next()
})
}
/**
* This is where all the authorization login is stored
*/
import Authorization from './../Services/Authorization'
export default function UserHasPermissions (router) {
/**
* Before each route we will see if the current user is authorized
* to access the given route
*/
router.beforeEach(({to, next, abort, redirect}) => {
let authorized = false
let user = JSON.parse(window.localStorage.getItem('atiiv.auth-user'))
/**
* Remember that access object in the routes? Yup this why we need it.
*
*/
if (to.access !== undefined) {
authorized = Authorization.authorize(
to.access.requiresLogin,
to.access.requiredPermissions,
to.access.permissionType
)
if (authorized === 'loginIsRequired') {
router.go({name: 'login'})
}
if (authorized === 'notAuthorized') {
/**
* Redirects to a "default" page
*/
router.go({ /*...*/ })
}
}
/**
* Everything is fine? Let's to the page then.
*/
next()
})
}
export default {
/*
access: {
requiresLogin: true,
requiredPermissions: ['admin'],
permissionType: 'AtLeastOne'
},
*/
authorize (requiresLogin, requiredPermissions, permissionType) {
let result = 'authorized'
let user = JSON.parse(window.localStorage.getItem('auth-user')) || undefined
let hasPermission = true
let token = window.localStorage.getItem('token') || undefined
let loweredPermissions = []
let permission, i
if (requiresLogin === true && (token === undefined && user === undefined)) {
return 'loginIsRequired'
}
if ((requiresLogin === true && token !== undefined) && (requiredPermissions === undefined || requiredPermissions.length === 0)) {
return 'authorized'
}
if (requiredPermissions) {
loweredPermissions = []
loweredPermissions.push(user.role.toLowerCase())
for (i = 0; i < requiredPermissions.length; i++) {
permission = requiredPermissions[i].toLowerCase()
if (permissionType === 'CombinationRequired') {
hasPermission = hasPermission && loweredPermissions.indexOf(permission) > -1
if (hasPermission === false) break
} else if (permissionType === 'AtLeastOne') {
hasPermission = loweredPermissions.indexOf(permission) > -1
if (hasPermission) break
}
}
result = hasPermission ? 'authorized' : 'notAuthorized'
}
return result
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment