Last active
February 22, 2025 17:12
-
-
Save hfalucas/60cb40c62e2e13e6c797f4887e43c8f6 to your computer and use it in GitHub Desktop.
[Vue.js] Authentication and Authorization
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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') | |
} | |
}) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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 file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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'}) | |
}) | |
} | |
}) | |
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import UserHasPermissions from './Middleware/UserHasPermissions' | |
import RedirectIfAuthenticated from './Middleware/RedirectIfAuthenticated' | |
export default function middleware (router) { | |
UserHasPermissions(router) | |
RedirectIfAuthenticated(router) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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() | |
}) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment