@nitra/vite-boot
Version:
149 lines (128 loc) • 4.57 kB
JavaScript
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()
// }
// }
}
}