@websanova/vue-auth
Version:
A simple light-weight authentication library for Vue.js
832 lines (809 loc) • 26.8 kB
JavaScript
/*!
* @websanova/vue-auth v4.2.1
* https://websanova.com/docs/vue-auth
* Released under the MIT License.
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global.VueAuth = factory());
}(this, (function () { 'use strict';
function isObject(val) {
if (val !== null && typeof val === 'object' && val.constructor !== Array) {
return true;
}
return false;
}
function toArray(val) {
return typeof val === 'string' || typeof val === 'number' ? [val] : val;
}
function extend(mainObj, appendObj) {
var i,
ii,
key,
data = {};
appendObj = appendObj || {};
for (key in mainObj) {
if (isObject(mainObj[key]) && mainObj[key].constructor.name !== 'FormData') {
data[key] = extend(mainObj[key], {});
} else {
data[key] = mainObj[key];
}
}
if (appendObj.constructor !== Array) {
appendObj = [appendObj];
}
for (i = 0, ii = appendObj.length; i < ii; i++) {
for (key in appendObj[i]) {
if (isObject(appendObj[i][key]) && appendObj[i][key].constructor.name !== 'FormData') {
data[key] = extend(mainObj[key] || {}, [appendObj[i][key]]);
} else {
data[key] = appendObj[i][key];
}
}
}
return data;
}
function compare(one, two) {
var i, ii, key;
if (Object.prototype.toString.call(one) === '[object Object]' && Object.prototype.toString.call(two) === '[object Object]') {
for (key in one) {
if (compare(one[key], two[key])) {
return true;
}
}
return false;
}
one = toArray(one);
two = toArray(two);
if (!one || !two || one.constructor !== Array || two.constructor !== Array) {
return false;
}
for (i = 0, ii = one.length; i < ii; i++) {
if (two.indexOf(one[i]) >= 0) {
return true;
}
}
return false;
}
function isLocalStorage() {
try {
if (!window.localStorage) {
throw 'exception';
}
localStorage.setItem('storage_test', 1);
localStorage.removeItem('storage_test');
return true;
} catch (e) {
return false;
}
}
function isSessionStorage() {
try {
if (!window.sessionStorage) {
throw 'exception';
}
sessionStorage.setItem('storage_test', 1);
sessionStorage.removeItem('storage_test');
return true;
} catch (e) {
return false;
}
}
function isCookieStorage() {
return true;
}
function getProperty(obj, desc) {
var arr = desc.split('.');
while (arr.length) {
obj = obj[arr.shift()];
}
return obj;
}
function setCookie(key, value, params) {
var i,
cookie = key + '=' + value + ';';
for (i in params) {
// Just skip if unset or false.
if (params[i] === false || params[i] === undefined) {
continue;
}
// If null and an option method exists such ex: "getCookieDomain".
else if (params[i] === null) {
if (this.options['getCookie' + i]) {
cookie += ' ' + i + '=' + this.options['getCookie' + i]() + ';';
}
}
// If true just set the flag as in "Secure;".
else if (params[i] === true) {
cookie += ' ' + i + ';';
}
// Default key/val.
else {
cookie += ' ' + i + '=' + params[i] + ';';
}
}
document.cookie = cookie;
}
function getDate(val) {
if (typeof val === 'string') {
return val;
} else if (val !== null && val !== undefined) {
return new Date(new Date().getTime() + val).toUTCString();
}
return val;
}
function set(key, value, expires) {
var params = this.options.cookie;
params.Expires = expires === true ? '' : getDate(params.Expires);
setCookie.call(this, key, value, params);
}
function get(key) {
var i,
ii,
cookie = document.cookie;
cookie = cookie.replace(/;\s+/g, ';').split(';').map(function (s) {
return s.replace(/\s+=\s+/g, '=').split('=');
});
for (i = 0, ii = cookie.length; i < ii; i++) {
if (cookie[i][0] && cookie[i][0] === key) {
return cookie[i][1];
}
}
return null;
}
function remove(key) {
var params = Object.assign({}, this.options.cookie);
params.Expires = getDate(-12096e5);
setCookie.call(this, key, '', params);
}
var __cookie = /*#__PURE__*/Object.freeze({
get: get,
set: set,
remove: remove
});
function set$1(key, value, expires) {
if (expires) {
sessionStorage.setItem(key, value);
} else {
localStorage.setItem(key, value);
}
}
function get$1(key) {
return sessionStorage.getItem(key) || localStorage.getItem(key);
}
function remove$1(key) {
localStorage.removeItem(key);
sessionStorage.removeItem(key);
}
var __storage = /*#__PURE__*/Object.freeze({
get: get$1,
set: set$1,
remove: remove$1
});
function getTokenKey(key) {
key = key || this.currentToken;
if (key) {
return key;
}
if (this.impersonating()) {
return this.options.tokenImpersonateKey;
}
return this.options.tokenDefaultKey;
}
function processToken(action, key, token, expires) {
var i = 0,
ts = this.options.stores,
ii = ts.length,
args = [getTokenKey.call(this, key)];
if (action === 'set') {
args.push(token);
args.push(expires === true ? true : false);
}
for (; i < ii; i++) {
if (typeof ts[i][action] === 'function') {
return ts[i][action].apply(this, args);
}
if (ts[i] === 'storage' && isLocalStorage() && isSessionStorage()) {
return __storage[action].apply(this, args);
}
if (ts[i] === 'cookie' && isCookieStorage()) {
return __cookie[action].apply(this, args);
}
}
}
function get$2(key) {
return processToken.call(this, 'get', key);
}
function set$2(key, token, expires) {
return processToken.call(this, 'set', key, token, expires);
}
function remove$2(key) {
return processToken.call(this, 'remove', key);
}
var __auth = null;
var __defaultOptions = {
// Variables
rolesKey: 'roles',
rememberKey: 'auth_remember',
staySignedInKey: 'auth_stay_signed_in',
tokenDefaultKey: 'auth_token_default',
tokenImpersonateKey: 'auth_token_impersonate',
stores: ['storage', 'cookie'],
cookie: {
Path: '/',
Domain: null,
Secure: true,
Expires: 12096e5,
SameSite: 'None'
},
// Redirects
authRedirect: {
path: '/login'
},
forbiddenRedirect: {
path: '/403'
},
notFoundRedirect: {
path: '/404'
},
// Http
registerData: {
url: 'auth/register',
method: 'POST',
redirect: '/login',
autoLogin: false
},
loginData: {
url: 'auth/login',
method: 'POST',
redirect: '/',
fetchUser: true,
staySignedIn: true
},
logoutData: {
url: 'auth/logout',
method: 'POST',
redirect: '/',
makeRequest: false
},
fetchData: {
url: 'auth/user',
method: 'GET',
enabled: true
},
refreshData: {
url: 'auth/refresh',
method: 'GET',
enabled: true,
interval: 30
},
impersonateData: {
url: 'auth/impersonate',
method: 'POST',
redirect: '/',
fetchUser: true
},
unimpersonateData: {
url: 'auth/unimpersonate',
method: 'POST',
redirect: '/admin',
fetchUser: true,
makeRequest: false
},
oauth2Data: {
url: 'auth/social',
method: 'POST',
redirect: '/',
fetchUser: true
},
// External
getUrl: _getUrl,
getCookieDomain: _getCookieDomain,
parseUserData: _parseUserData
};
function _isAccess(role, key) {
if (__auth.$vm.state.authenticated === true) {
if (role) {
return compare(role, getProperty(__auth.$vm.state.data || {}, key || __auth.options.rolesKey));
}
return true;
}
return false;
}
function _isTokenExpired() {
return !get$2.call(__auth);
}
function _getAuthMeta(transition) {
var auth, authRoutes;
if (transition.to) {
auth = transition.to.auth;
} else {
authRoutes = transition.matched.filter(function (route) {
return Object.prototype.hasOwnProperty.call(route.meta, 'auth');
});
// matches the nested route, the last one in the list
if (authRoutes.length) {
auth = authRoutes[authRoutes.length - 1].meta.auth;
}
}
return auth;
}
function _getCookieDomain() {
return window.location.hostname;
}
function _getUrl() {
var port = window.location.port;
return window.location.protocol + '//' + window.location.hostname + (port ? ':' + port : '');
}
function _getRemember() {
return get$2.call(__auth, __auth.options.rememberKey);
}
function _setUser(data) {
__auth.$vm.state.data = data;
}
function _setLoaded(loaded) {
__auth.$vm.state.loaded = loaded;
}
function _setAuthenticated(authenticated) {
__auth.$vm.state.loaded = true;
__auth.$vm.state.authenticated = authenticated;
}
function _setStaySignedIn(staySignedIn) {
if (staySignedIn === true) {
set$2.call(__auth, __auth.options.staySignedInKey, 'true', false);
} else {
remove$2.call(__auth, __auth.options.staySignedInKey);
}
}
function _setRemember(val) {
if (val) {
set$2.call(__auth, __auth.options.rememberKey, val, false);
__auth.$vm.state.remember = val;
} else {
remove$2.call(__auth, __auth.options.rememberKey);
__auth.$vm.state.remember = null;
}
}
function _setTransitions(transition) {
__auth.transitionPrev = __auth.transitionThis;
__auth.transitionThis = transition;
}
function _parseUserData(data) {
return data.data || {};
}
function _parseUserResponseData(res) {
return __auth.options.parseUserData(__auth.drivers.http.httpData(res));
}
function _parseRedirectUri(uri) {
uri = uri || '';
if (/^https?:\/\//.test(uri)) {
return uri;
}
return _getUrl() + '/' + uri.replace(/^\/|\/$/g, '');
}
function _parseRequestIntercept(req) {
var token, tokenName;
if (req && req.ignoreVueAuth) {
return req;
}
if (req.impersonating === false && __auth.impersonating()) {
tokenName = __auth.options.tokenDefaultKey;
}
token = get$2.call(__auth, tokenName);
if (token) {
__auth.drivers.auth.request.call(__auth, req, token);
}
return req;
}
function _parseResponseIntercept(res, req) {
var token;
if (req && req.ignoreVueAuth) {
return;
}
_processInvalidToken(res, __auth.transitionThis);
token = __auth.drivers.auth.response.call(__auth, res);
if (token) {
set$2.call(__auth, null, token, get$2.call(__auth, __auth.options.staySignedInKey) ? false : true);
}
}
function _processInvalidToken(res, transition) {
var auth, redirect;
if (!__auth.drivers.http.invalidToken || !__auth.drivers.http.invalidToken.call(__auth, res)) {
return;
}
if (transition) {
auth = _getAuthMeta(transition);
}
if (auth) {
redirect = auth.redirect || __auth.options.authRedirect;
}
_processLogout(redirect);
}
function _processRouterBeforeEach(cb) {
var isTokenExpired = _isTokenExpired();
if (isTokenExpired && __auth.$vm.state.authenticated) {
_processLogout();
}
if (!isTokenExpired && !__auth.$vm.state.loaded && __auth.options.refreshData.enabled) {
__auth.refresh().then(function () {
_processAuthenticated(cb);
}, function () {
_processAuthenticated(cb);
});
return;
}
_processAuthenticated(cb);
}
function _processAuthenticated(cb) {
if (__auth.$vm.state.authenticated === null && get$2.call(__auth)) {
if (__auth.options.fetchData.enabled) {
__auth.fetch().then(cb, cb);
} else {
_processFetch({});
return cb.call(__auth);
}
} else {
_setLoaded(true);
return cb.call(__auth);
}
}
function _processTransitionEach(transition, routeAuth, cb) {
var authRedirect = (routeAuth || '').redirect || __auth.options.authRedirect,
forbiddenRedirect = (routeAuth || '').forbiddenRedirect || (routeAuth || '').redirect || __auth.options.forbiddenRedirect,
notFoundRedirect = (routeAuth || '').notFoundRedirect || (routeAuth || '').redirect || __auth.options.notFoundRedirect,
rolesKey = (routeAuth || '').rolesKey || __auth.options.rolesKey;
routeAuth = toArray((routeAuth || '').roles !== undefined ? routeAuth.roles : routeAuth);
if (routeAuth && (routeAuth === true || routeAuth.constructor === Array || isObject(routeAuth))) {
if (!__auth.check()) {
__auth.transitionRedirectType = 401;
if (typeof authRedirect === 'function') {
authRedirect = authRedirect(transition);
}
cb.call(__auth, authRedirect);
} else if ((routeAuth.constructor === Array || isObject(routeAuth)) && !compare(routeAuth, getProperty(__auth.$vm.state.data || {}, rolesKey))) {
__auth.transitionRedirectType = 403;
if (typeof forbiddenRedirect === 'function') {
forbiddenRedirect = forbiddenRedirect(transition);
}
cb.call(__auth, forbiddenRedirect);
} else {
if ((__auth.transitionPrev || {}).path !== __auth.transitionThis.path) {
__auth.$vm.state.redirect = __auth.transitionRedirectType ? {
type: __auth.transitionRedirectType,
from: __auth.transitionPrev,
to: __auth.transitionThis
} : null;
__auth.transitionRedirectType = null;
}
return cb();
}
} else if (routeAuth === false && __auth.check()) {
__auth.transitionRedirectType = 404;
if (typeof notFoundRedirect === 'function') {
notFoundRedirect = notFoundRedirect(transition);
}
cb.call(__auth, notFoundRedirect);
} else {
if ((__auth.transitionPrev || {}).path !== __auth.transitionThis.path) {
__auth.$vm.state.redirect = __auth.transitionRedirectType ? {
type: __auth.transitionRedirectType,
from: __auth.transitionPrev,
to: __auth.transitionThis
} : null;
__auth.transitionRedirectType = null;
}
return cb();
}
}
function _processFetch(data, redirect) {
_setUser(data);
_setAuthenticated(true);
_processRedirect(redirect);
}
function _processLogout(redirect) {
remove.call(__auth, __auth.options.tokenImpersonateKey);
remove.call(__auth, __auth.options.tokenDefaultKey);
remove$2.call(__auth, __auth.options.tokenImpersonateKey);
remove$2.call(__auth, __auth.options.tokenDefaultKey);
remove$2.call(__auth, __auth.options.staySignedInKey);
__auth.$vm.state.loaded = true;
__auth.$vm.state.authenticated = false;
__auth.$vm.state.data = null;
_processRedirect(redirect);
}
function _processImpersonate(defaultToken, redirect) {
set$2.call(__auth, __auth.options.tokenImpersonateKey, __auth.token(), get$2.call(__auth, __auth.options.staySignedInKey) ? false : true);
set$2.call(__auth, __auth.options.tokenDefaultKey, defaultToken, get$2.call(__auth, __auth.options.staySignedInKey) ? false : true);
__auth.$vm.state.impersonating = true;
_processRedirect(redirect);
}
function _processUnimpersonate(redirect) {
remove$2.call(__auth, __auth.options.tokenImpersonateKey);
__auth.$vm.state.impersonating = false;
_processRedirect(redirect);
}
function _processRedirect(redirect) {
if (redirect) {
__auth.drivers.router.routerGo.call(__auth, redirect);
}
}
function _initVm(Vue) {
__auth.$vm = new Vue({
data: function () {
return {
state: {
data: null,
loaded: false,
redirect: null,
authenticated: null,
// TODO: false ?
impersonating: undefined,
remember: undefined
}
};
}
});
}
function _initDriverCheck() {
var msg;
var i, ii;
var drivers = ['auth', 'http', 'router'];
for (i = 0, ii = drivers.length; i < ii; i++) {
if (!__auth.drivers[drivers[i]]) {
console.error('Error (@websanova/vue-auth): "' + drivers[i] + '" driver must be set.');
return false;
}
if (__auth.drivers[drivers[i]].init) {
msg = __auth.drivers[drivers[i]].init.call(__auth);
if (msg) {
console.error('Error (@websanova/vue-auth): ' + msg);
return false;
}
}
}
}
function _initRefreshInterval() {
if (__auth.options.refreshData.enabled && __auth.options.refreshData.interval > 0) {
setInterval(function () {
if (__auth.options.refreshData.enabled && !_isTokenExpired()) {
__auth.refresh();
}
}, __auth.options.refreshData.interval * 1000 * 60); // In minutes.
}
}
function _initInterceptors() {
__auth.drivers.http.interceptor.call(__auth, _parseRequestIntercept, _parseResponseIntercept);
__auth.drivers.router.beforeEach.call(__auth, _processRouterBeforeEach, _processTransitionEach, _setTransitions, _getAuthMeta);
}
function Auth(Vue, options) {
__auth = this;
options = options || {};
this.plugins = options.plugins;
this.drivers = options.drivers;
this.options = extend(__defaultOptions, options.options);
delete options.plugins;
delete options.drivers;
delete options.options;
// Init vars.
this.currentToken = null;
this.transitionPrev = null;
this.transitionThis = null;
this.transitionRedirectType = null;
_initDriverCheck();
_initVm(Vue);
_initRefreshInterval();
_initInterceptors();
}
Auth.prototype.ready = function () {
return __auth.$vm.state.loaded;
};
Auth.prototype.load = function () {
return new Promise(function (resolve) {
var timer = null;
timer = setInterval(function () {
if (__auth.$vm.state.loaded) {
clearInterval(timer);
resolve();
}
}, 50);
});
};
Auth.prototype.redirect = function () {
return __auth.$vm.state.redirect;
};
Auth.prototype.user = function (data) {
if (data !== undefined) {
_processFetch(data);
}
return __auth.$vm.state.data;
};
Auth.prototype.check = function (role, key) {
return _isAccess(role, key);
};
Auth.prototype.impersonating = function () {
var impersonating = get$2.call(__auth, __auth.options.tokenImpersonateKey) ? true : false;
if (__auth.$vm.state.impersonating === undefined) {
__auth.$vm.state.impersonating = impersonating;
}
return __auth.$vm.state.impersonating;
};
Auth.prototype.token = function (name, token, expires) {
if (token !== undefined) {
if (token === null) {
remove$2.call(__auth, name);
} else {
expires = expires === true || expires === false ? expires : get$2.call(__auth, __auth.options.staySignedInKey) ? false : true;
set$2.call(__auth, name, token, expires);
}
}
return get$2.call(__auth, name);
};
Auth.prototype.fetch = function (data) {
data = extend(__auth.options.fetchData, data);
return new Promise(function (resolve, reject) {
__auth.drivers.http.http.call(__auth, data).then(function (res) {
_processFetch(_parseUserResponseData(res), data.redirect);
resolve(res);
}, reject);
});
};
Auth.prototype.refresh = function (data) {
data = extend(__auth.options.refreshData, data);
return __auth.drivers.http.http.call(__auth, data);
};
Auth.prototype.register = function (data) {
var registerData = extend(__auth.options.registerData, data);
if (registerData.autoLogin !== true) {
_setRemember(registerData.remember);
_setStaySignedIn(registerData.staySignedIn);
}
return new Promise(function (resolve, reject) {
__auth.drivers.http.http.call(__auth, registerData).then(function (res) {
var loginData;
if (registerData.autoLogin) {
loginData = extend(__auth.options.loginData, data);
__auth.login(loginData).then(resolve, reject);
} else {
resolve(res);
_processRedirect(registerData.redirect);
}
}, reject);
});
};
Auth.prototype.login = function (data) {
data = extend(__auth.options.loginData, data);
_setRemember(data.remember);
_setStaySignedIn(data.staySignedIn);
return new Promise(function (resolve, reject) {
__auth.drivers.http.http.call(__auth, data).then(function (res) {
if (data.fetchUser || data.fetchUser === undefined && __auth.options.fetchData.enabled) {
__auth.fetch({
redirect: data.redirect
}).then(resolve, reject);
} else {
_processFetch(_parseUserResponseData(res), data.redirect);
resolve(res);
}
}, function (res) {
_setAuthenticated(false);
reject(res);
});
});
};
Auth.prototype.remember = function (val) {
if (val) {
_setRemember(val);
}
var remember = _getRemember();
if (__auth.$vm.state.remember === undefined) {
__auth.$vm.state.remember = remember;
}
return __auth.$vm.state.remember;
};
Auth.prototype.unremember = function () {
_setRemember(null);
};
Auth.prototype.logout = function (data) {
data = extend(__auth.options.logoutData, data);
return new Promise(function (resolve, reject) {
if (data.makeRequest) {
__auth.drivers.http.http.call(__auth, data).then(function (res) {
_processLogout(data.redirect);
resolve(res);
}, reject);
} else {
_processLogout(data.redirect);
resolve();
}
});
};
Auth.prototype.impersonate = function (data) {
data = extend(__auth.options.impersonateData, data);
return new Promise(function (resolve, reject) {
var token = __auth.token();
__auth.drivers.http.http.call(__auth, data).then(function (res) {
_processImpersonate(token);
if (data.fetchUser || data.fetchUser === undefined && __auth.options.fetchData.enabled) {
__auth.fetch({
redirect: data.redirect
}).then(resolve, reject);
} else {
_processRedirect(data.redirect);
resolve(res);
}
}, reject);
});
};
Auth.prototype.unimpersonate = function (data) {
data = extend(__auth.options.unimpersonateData, data);
return new Promise(function (resolve, reject) {
if (data.makeRequest) {
__auth.drivers.http.http.call(__auth, data).then(resolve, reject);
} else {
resolve();
}
}).then(function () {
return new Promise(function (resolve, reject) {
_processUnimpersonate();
if (data.fetchUser || data.fetchUser === undefined && __auth.options.fetchData.enabled) {
__auth.fetch({
redirect: data.redirect
}).then(resolve, reject);
} else {
_processRedirect(data.redirect);
resolve();
}
});
});
};
Auth.prototype.oauth2 = function (type, data) {
var params = [];
if (data.code) {
try {
if (data.state) {
data.state = JSON.parse(decodeURIComponent(data.state));
}
} catch (e) {
console.error('vue-auth:error There was an issue retrieving the state data.');
data.state = data.state || {};
}
data = extend(__auth.options.oauth2Data, [data.state, data]);
delete data.code;
delete data.state;
delete data.params;
return __auth.login(data);
}
data = extend(__auth.drivers.oauth2[type], data);
data.params.state = JSON.stringify(data.params.state || {});
data.params.redirect_uri = _parseRedirectUri(data.params.redirect_uri);
Object.keys(data.params).forEach(key => {
params.push(key + '=' + encodeURIComponent(data.params[key]));
});
window.open(data.url + '?' + params.join('&'), (data.window || {}).name || '_self', (data.window || {}).specs || {}, (data.window || {}).replace !== false);
};
Auth.prototype.enableImpersonate = function () {
if (__auth.impersonating()) {
__auth.currentToken = null;
}
};
Auth.prototype.disableImpersonate = function () {
if (__auth.impersonating()) {
__auth.currentToken = __auth.options.tokenDefaultKey;
}
};
function plugin(Vue, options) {
Vue.auth = new Auth(Vue, options);
Object.defineProperties(Vue.prototype, {
$auth: {
get: function () {
return Vue.auth;
}
}
});
}
if (typeof window !== 'undefined' && window.Vue) {
window.Vue.use(plugin);
}
return plugin;
})));