Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /**
- * Vuex
- */
- import {
- AUTH_NAMESPACE,
- AuthGetterTypes,
- AuthActionTypes,
- } from '@/store/modules/auth';
- /**
- * Data
- */
- import PageNames from '@/data/enum/page-names';
- import Statuses from '@/data/enum/generic-statuses';
- import Roles from '@/data/enum/roles';
- import API_CONFIG from '@/data/api-config';
- /**
- * Utils
- */
- import axios from 'axios';
- import { findLast, get } from 'lodash';
- import { inNavRoutes } from '@/router';
- /**
- * @desc Will store an instance of VueRouter after applying the auth-plugin.
- *
- * @type {WeakMap<Object, any>}
- */
- const routerInstance = new WeakMap();
- /**
- * @desc Will store an instance of Vuex after applying the auth-plugin.
- *
- * @type {WeakMap<Object, any>}
- */
- const storeInstance = new WeakMap();
- /* eslint-disable dot-notation */
- export default {
- /**
- * @desc Install method to register plugin with VueJS.
- *
- * @param {Object} Vue
- * @param {Object} router
- * @param {Object} store
- */
- install(Vue, {
- router,
- store,
- }) {
- /**
- * @desc Interceptor to set Auth header.
- */
- axios.interceptors.request.use((config) => {
- const authUrl = `${API_CONFIG.XXX.HOST}${API_CONFIG.XXX.ENDPOINTS.AUTH.CREATE_TOKEN.path}`;
- if (config.url !== authUrl) {
- const accessToken = store.getters[`${AUTH_NAMESPACE}/${AuthGetterTypes.accessToken}`];
- const hasAuthHeader = !!config['Authorization'];
- if (accessToken && !hasAuthHeader) {
- this.$_setAuthHeader(config);
- }
- }
- return config;
- });
- /**
- * @desc Interceptor to refresh token if it has been overdue.
- *
- * @modifies {refreshCounter}
- */
- axios.interceptors.response.use(
- response => response,
- async (error) => {
- if (this.$_isInvalidToken(error)) {
- await this.$_refreshToken(error);
- return this.$_retryRequest(error.config);
- }
- throw error;
- },
- );
- /**
- * @desc Guard to check does user granted to see the page.
- */
- router.beforeEach(async (to, from, next) => {
- try {
- const hasTokens = store.getters[`${AUTH_NAMESPACE}/${AuthGetterTypes.hasTokens}`];
- if (hasTokens && !this.checkRole()) {
- await store.dispatch(`${AUTH_NAMESPACE}/${AuthActionTypes.loadUser}`);
- store.dispatch(`${AUTH_NAMESPACE}/${AuthActionTypes.setAuthFulfilled}`);
- }
- if (this.checkRole() && to.name === PageNames.SIGN_IN) {
- return next({ name: PageNames.DASHBOARD });
- } else if (to.name !== PageNames.SIGN_IN) {
- this.$_routeCheckAuth(to, from, next);
- }
- return next();
- } catch (error) {
- next({
- name: PageNames.SIGN_IN,
- });
- throw error;
- }
- });
- /**
- * @desc Guard to close all notifications and messages before transition to another route.
- */
- router.beforeEach((to, from, next) => {
- document.title = to.name;
- /**
- * @desc Close all notifications.
- * @type {NodeListOf<Element>}
- */
- const notificationCloseButtons = document.querySelectorAll('.el-notification__closeBtn');
- if (notificationCloseButtons.length) {
- notificationCloseButtons.forEach((closeButton) => {
- closeButton.click();
- });
- }
- /**
- * @desc Close all messages.
- * @type {NodeListOf<Element>}
- */
- const messageCloseButtons = document.querySelectorAll('.el-message__closeBtn');
- if (messageCloseButtons.length) {
- messageCloseButtons.forEach((closeButton) => {
- closeButton.click();
- });
- }
- next();
- });
- routerInstance.set(this, router);
- storeInstance.set(this, store);
- // eslint-disable-next-line no-param-reassign
- Vue.prototype.$auth = this;
- // eslint-disable-next-line no-param-reassign
- Vue.auth = this;
- },
- /**
- * @desc Perform sign in.
- *
- * @param {Object} credentials
- * @param {string|Object} redirect
- * @returns {Promise}
- */
- async signIn(credentials, redirect) {
- let store = null;
- let router = null;
- try {
- store = storeInstance.get(this);
- router = routerInstance.get(this);
- store.dispatch(`${AUTH_NAMESPACE}/${AuthActionTypes.setAuthPending}`);
- const { username, password } = credentials;
- const response = await this.$_passwordGrant(username, password);
- this.$_storeTokens(response.access_token, response.refresh_token);
- await store.dispatch(`${AUTH_NAMESPACE}/${AuthActionTypes.loadUser}`);
- store.dispatch(`${AUTH_NAMESPACE}/${AuthActionTypes.setAuthFulfilled}`);
- if (!this.checkRole(Roles.EDITOR)) {
- this.signOut();
- throw new Error('The user credentials were incorrect.');
- }
- router.push(redirect || {
- name: PageNames.DASHBOARD,
- });
- return true;
- } catch (error) {
- if (process.env.VUE_APP_CONSOLE_MESSAGES === 'on') {
- console.warn('[Auth] An error occurred while signin in.', error || '(no data)');
- }
- store.dispatch(`${AUTH_NAMESPACE}/${AuthActionTypes.setAuthRejected}`);
- throw error;
- }
- },
- /**
- * @desc Perform sign out.
- *
- * @param {string|Object} redirect
- */
- signOut(redirect) {
- const store = storeInstance.get(this);
- const router = routerInstance.get(this);
- store.dispatch(`${AUTH_NAMESPACE}/${AuthActionTypes.resetAuthState}`);
- router.push(redirect || `/${PageNames.SIGN_IN}`);
- },
- /**
- * @desc Get the currently authenticated user.
- *
- * @returns {Object|null}
- */
- getUser() {
- const store = storeInstance.get(this);
- return store.getters[`${AUTH_NAMESPACE}/${AuthGetterTypes.user}`];
- },
- /**
- * @desc Check if the currently authenticated user has a specific role.
- *
- * @param {string} role
- * @returns {boolean}
- */
- checkRole(role) {
- const store = storeInstance.get(this);
- const user = this.getUser();
- const isAuthFulfilled = store.getters[`${AUTH_NAMESPACE}/${AuthGetterTypes.authStatus}`] === Statuses.FULFILLED;
- if (user === null || isAuthFulfilled === false) {
- return false;
- }
- if (typeof role === 'string') {
- const { granted } = user;
- if (granted) {
- return granted.findIndex(role_ => role_ === role) !== -1;
- }
- return false;
- }
- return true;
- },
- /**
- * @desc Refresh the current token.
- *
- * @param {*} error
- * @modifies {refreshCounter}
- * @returns {Promise}
- */
- async $_refreshToken(error) {
- let store = null;
- try {
- store = storeInstance.get(this);
- const hasTokens = store.getters[`${AUTH_NAMESPACE}/${AuthGetterTypes.hasTokens}`];
- if (!hasTokens) {
- throw error;
- }
- const response = await this.$_refreshTokenGrant(store.getters[`${AUTH_NAMESPACE}/${AuthGetterTypes.refreshToken}`]);
- if (response) {
- this.$_storeTokens(response.access_token, response.refresh_token);
- return true;
- }
- return false;
- } catch (error_) {
- if (process.env.VUE_APP_CONSOLE_MESSAGES === 'on') {
- console.warn('[Auth] An error occurred while refreshing token.', error_ || '(no data)');
- }
- this.signOut();
- throw error_;
- }
- },
- /**
- * @desc Retry a request.
- *
- * @param {Object} config
- * @returns {AxiosPromise}
- */
- async $_retryRequest(config) {
- try {
- const authUrl = `${API_CONFIG.XXX.HOST}${API_CONFIG.XXX.ENDPOINTS.AUTH.CREATE_TOKEN.path}`;
- if (config.url !== authUrl) {
- this.$_setAuthHeader(config);
- }
- return await axios(config);
- } catch (error) {
- if (process.env.VUE_APP_CONSOLE_MESSAGES === 'on') {
- console.warn('[Auth] An error occurred while retrying request.', error || '(no data)');
- }
- throw error;
- }
- },
- /**
- * @desc Set the Authorization header on an outgoing request.
- *
- * @param {Object} config
- */
- $_setAuthHeader(config) {
- const store = storeInstance.get(this);
- const accessToken = store.getters[`${AUTH_NAMESPACE}/${AuthGetterTypes.accessToken}`];
- // eslint-disable-next-line no-param-reassign
- config.headers['Authorization'] = `Bearer ${accessToken}`;
- },
- /**
- * @desc Store access and refresh tokens in the store.
- *
- * @param {string} accessToken
- * @param {string} refreshToken
- */
- $_storeTokens(accessToken, refreshToken) {
- const store = storeInstance.get(this);
- store.dispatch(`${AUTH_NAMESPACE}/${AuthActionTypes.storeTokens}`, {
- accessToken,
- refreshToken,
- });
- },
- /**
- * @desc Check if the response is caused by an invalid i.e., outdated token.
- *
- * @param {Object} error
- * @returns {boolean}
- */
- $_isInvalidToken(error) {
- const { response } = error;
- const authUrl = `${API_CONFIG.XXX.HOST}${API_CONFIG.XXX.ENDPOINTS.AUTH.CREATE_TOKEN.path}`;
- const STATUS_CODE_UNAUTHORIZED = 401;
- return get(response, 'config.url') !== authUrl && get(response, 'status') === STATUS_CODE_UNAUTHORIZED;
- },
- /**
- * @desc Route guard.
- *
- * @param {Object} to
- * @param {Object} from
- * @param {Function} next
- */
- $_routeCheckAuth(to, from, next) {
- const route = findLast(to.matched, route_ => route_.meta.requiresAuth);
- const requiredRole = get(route, 'meta.requiresAuth');
- if (!this.checkRole() && requiredRole) {
- return next({ name: PageNames.SIGN_IN });
- }
- if (!this.checkRole(requiredRole)) {
- // eslint-disable-next-line arrow-body-style
- const firstEligibleRoute = inNavRoutes.find((nextRoute) => {
- return !nextRoute.meta.requiresAuth || this.checkRole(nextRoute.meta.requiresAuth);
- });
- if (!firstEligibleRoute) {
- return this.signOut();
- }
- if (from.name !== firstEligibleRoute.name) {
- return next({ name: firstEligibleRoute.name });
- }
- } else {
- return next();
- }
- return false;
- },
- /**
- * @desc Method for authentication with password.
- *
- * @param {string} username
- * @param {string} password
- * @returns {Promise}
- */
- $_passwordGrant(username, password) {
- if (!(typeof username === 'string' && !!username.trim()) && !(typeof password === 'string' && !!password.trim())) {
- throw new TypeError('Username and password must be non-empty strings.');
- }
- return this.$_createToken('password', { username: username.trim(), password: password.trim() });
- },
- /**
- * @desc Method for refreshing expired access tokens.
- *
- * @param {string} refreshToken
- * @returns {Promise}
- */
- $_refreshTokenGrant(refreshToken) {
- if (!(typeof refreshToken === 'string' && !!refreshToken.trim())) {
- throw new TypeError('Refresh token must be non-empty string.');
- }
- return this.$_createToken('refresh_token', { refresh_token: refreshToken.trim() });
- },
- /**
- * @desc Method for creating access token.
- *
- * @param {string} grantType
- * @param {Object} payload
- * @returns {Promise}
- */
- async $_createToken(grantType, payload) {
- try {
- if (!(typeof grantType === 'string' && !!grantType.trim())) {
- throw new TypeError('Grant type must be non-empty string.');
- }
- const { method, path } = API_CONFIG.XXX.ENDPOINTS.AUTH.CREATE_TOKEN;
- const { HOST: host, OPTIONS: options } = API_CONFIG.XXX;
- const response = await axios[method](
- `${host}${path}`,
- { grant_type: grantType, ...payload },
- options,
- );
- return response.data.data;
- } catch (error) {
- if (process.env.VUE_APP_CONSOLE_MESSAGES === 'on') {
- console.warn(`[Auth] An error occurred while creating token with ${grantType} grant.`, error || '(no data)');
- }
- throw error;
- }
- },
- };
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement