UNPKG

@nitra/vite-boot

Version:
149 lines (128 loc) 4.57 kB
import jwtDecode from '@nitra/jwt-decode' import { cleanToken, setUser, unsetUser } from './user.js' export { cleanToken } from './user.js' const allowedRoles = import.meta.env.VITE_HASURA_ROLE.split(',') /** * Перевіряє коректність JWT токена * @param {string} token - JWT токен * @returns {{result: 'ok'|'expired'|'cleaned'|'broken-role', decoded?: object}} - Результат перевірки */ export function checkToken(token) { /** @type {{ exp?: number, [k: string]: unknown }} */ const decoded = jwtDecode(token) if (!decoded || !decoded['https://hasura.io/jwt/claims']) { cleanToken() return { result: 'cleaned' } } if (!decoded['https://hasura.io/jwt/claims']['x-hasura-allowed-roles'].some((x) => allowedRoles.includes(x))) { unsetUser() // Виходимо return { result: 'broken-role' } } const isExpired = !decoded || Math.floor(Date.now() / 1000) > decoded.exp if (isExpired) { unsetUser() refreshToken() return { result: 'expired' } } setUser(decoded, token) return { result: 'ok', decoded } } /** * Вхід або на попередню або на кореневу сторінку * @param {object} decoded - Розкодований JWT з Hasura claims * @param {string} raw - JWT як рядок (для localStorage) * @returns {void} */ export function defaultLogin(decoded, raw) { // Якщо задано що токен з localStorage localStorage.setItem('__session', raw) setUser(decoded, raw) // @ts-ignore const route = sessionStorage.getItem('url-before-logout') || import.meta.env.BASE_URL globalThis.location.href = route // router.push(route) } /** * Автоматичний вхід користувача * @returns {void} */ export function autoLogin() { const token = getToken() if (!token) { return } const check = checkToken(token) if (check.result !== 'ok') { return } defaultLogin(check.decoded, token) } /** * Токен з кукі * @returns {string} токен */ export function getToken() { // інакше з кукі const cookieObj = new URLSearchParams(document.cookie.replaceAll('; ', '&')) const token = cookieObj.get('__session') if (token) { return token } // Якщо задано що токен беремо з localStorage return localStorage.getItem('__session') } /** * */ export async function refreshToken() { try { const token = getToken() // Чекаємо, можливо це паралельний JWTExpired // виходимо якщо вже пішов рефреш токена від іншого запиту if (!token) { return } const response = await fetch(`${import.meta.env.BASE_URL}auth/refresh-token`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ token }) }) const result = await response.json() // Якщо НЕ успішно отримали токен if (!result.token) { return } const check = checkToken(result.token) if (check.result !== 'ok') { return } // Якщо запущений аполо // допускаємо що ми всередині прикладення if (globalThis.location.pathname === '/login') { defaultLogin(check.decoded, result.token) } else { setUser(check.decoded, result.token) // Перезавантажуємо WS, щоб він використовував новий токен try { const { wsClient } = await import('./apollo.js') // імпорт тільки тут, на логіні не потрібно + уникнення можливих циклічних залежностей зі статичним імпортом wsClient.dispose() } catch { // Якщо apollo недоступний, хоча це не повинно бути, бо ми вже у прикладенні } } } catch (error) { console.error('Error:', error) // } finally { // // Якщо невдача отримання токену // if (!successToken) { // // Якщо користувач не анонімний зараз // // TODO: допрацювати // if (Object.keys(connectionParam).length === 0) { // // перезавантажуємо щоб він вступив в дію як анонімний // wsClient.restart() // } // } } }