UNPKG

@josempgon/vue-keycloak

Version:

Keycloak plugin for Vue 3 with Composition API

207 lines (199 loc) 6.39 kB
import Keycloak from 'keycloak-js'; import { shallowRef, reactive, toRefs } from 'vue'; function isArray(value) { return Array.isArray(value); } function isFunction(fun) { return typeof fun === 'function'; } function isNil(value) { return value === undefined || value === null; } function isString(value) { return typeof value === 'string'; } function isErrorLike(value) { return value instanceof Error || isString(value) || isString(value === null || value === void 0 ? void 0 : value.error); } const keycloak = shallowRef(); const state = reactive({ isAuthenticated: false, hasFailed: false, error: null, isPending: false, token: '', decodedToken: {}, username: '', userId: '', roles: [], resourceRoles: {}, }); const setKeycloak = (value) => { keycloak.value = value; }; const setToken = (token, tokenParsed) => { var _a, _b; state.token = token; const content = tokenParsed; state.decodedToken = content; state.roles = content.realm_access ? content.realm_access.roles : []; state.username = ((_a = content.preferred_username) !== null && _a !== void 0 ? _a : ''); state.userId = (_b = content.sub) !== null && _b !== void 0 ? _b : ''; state.resourceRoles = content.resource_access ? Object.fromEntries(Object.entries(content.resource_access).map(([key, value]) => [key, value.roles])) : {}; }; const clearToken = () => { state.token = ''; state.decodedToken = {}; state.roles = []; state.username = ''; state.userId = ''; state.resourceRoles = {}; }; const hasFailed = (value, err) => { state.hasFailed = value; if (err instanceof Error) { state.error = err; } else if (isString(err === null || err === void 0 ? void 0 : err.error)) { state.error = new Error(err.error); } else if (isString(err)) { state.error = new Error(err); } else { state.error = new Error('Unknown'); } state.error.name = '[vue-keycloak]'; console.error(state.error); }; const isPending = (value) => { state.isPending = value; }; const isAuthenticated = (value) => { state.isAuthenticated = value; }; let $keycloak = undefined; let creationFailed = false; async function updateToken(minValidity) { const keycloak = $keycloak; if (isNil(keycloak)) { throw new Error('[vue-keycloak] Keycloak is not initialised. Call createKeycloak() first.'); } try { await keycloak.updateToken(minValidity); const { token, tokenParsed } = keycloak; if (isNil(token) || isNil(tokenParsed)) { throw new Error('Failed to refresh the access token'); } setToken(token, tokenParsed); return token; } catch (err) { if (!keycloak.authenticated) { isAuthenticated(false); clearToken(); } const rejectionReason = isErrorLike(err) ? err : new Error('Failed to refresh the access token'); hasFailed(true, rejectionReason); throw rejectionReason; } } async function getToken(minValidity = 10) { return updateToken(minValidity); } function createKeycloak(config) { creationFailed = false; try { $keycloak = new Keycloak(config); setKeycloak($keycloak); } catch (err) { $keycloak = undefined; creationFailed = true; hasFailed(true, isNil(err) ? new Error('Failed to create the keycloak adapter') : err); } return $keycloak; } async function initKeycloak(initConfig) { try { isPending(true); const keycloak = $keycloak; if (isNil(keycloak)) { if (!creationFailed) { hasFailed(true, new Error('Keycloak is not initialised. Call createKeycloak() first.')); } return; } const _isAuthenticated = await keycloak.init(initConfig); isAuthenticated(_isAuthenticated); if (!isNil(keycloak.token) && !isNil(keycloak.tokenParsed)) { setToken(keycloak.token, keycloak.tokenParsed); } } catch (err) { isAuthenticated(false); hasFailed(true, isNil(err) ? new Error('Failed to initialize the keycloak adapter') : err); } finally { isPending(false); } } const useKeycloak = () => { return { keycloak, ...toRefs(state), hasRoles: (roles) => isArray(roles) && roles.length > 0 && state.isAuthenticated && roles.every(role => state.roles.includes(role)), hasResourceRoles: (roles, resource) => isArray(roles) && roles.length > 0 && !isNil(resource) && state.isAuthenticated && !isNil(state.resourceRoles) && isArray(state.resourceRoles[resource]) && roles.every(role => state.resourceRoles[resource].includes(role)), }; }; const defaultInitConfig = { flow: 'standard', checkLoginIframe: false, onLoad: 'login-required', }; const vueKeycloak = { install: async (app, options) => { isPending(true); if (isNil(options)) { isPending(false); hasFailed(true, new Error('The VueKeycloakPluginConfig is required')); return; } let keycloakPluginConfig; if (isFunction(options)) { try { keycloakPluginConfig = await options(); } catch (err) { isPending(false); hasFailed(true, isNil(err) ? new Error('The KeycloakConfigAsyncFactory failed') : err); return; } } else { keycloakPluginConfig = options; } if (isNil(keycloakPluginConfig.config)) { isPending(false); hasFailed(true, new Error('The KeycloakConfig is required')); return; } const keycloakConfig = keycloakPluginConfig.config; const keycloakInitOptions = !isNil(keycloakPluginConfig.initOptions) ? { ...defaultInitConfig, ...keycloakPluginConfig.initOptions } : defaultInitConfig; const _keycloak = createKeycloak(keycloakConfig); app.config.globalProperties.$keycloak = _keycloak; await initKeycloak(keycloakInitOptions); }, }; export { getToken, useKeycloak, vueKeycloak }; //# sourceMappingURL=index.mjs.map