baked-recipe-admin
Version:
Baked is an opinionated framework for .NET in backend and Nuxt in frontend. This is a recipe package that brings together all the components one needs for an Admin UI.
100 lines (79 loc) • 2.11 kB
JavaScript
import { useRuntimeConfig } from "#app";
import { createError, useMutex } from "#imports";
export default function() {
const mutex = useMutex();
const { public: { auth, composables } } = useRuntimeConfig();
async function current(
shouldRefresh = true
) {
const tokenString = localStorage.getItem("token");
if(!tokenString) { return null; }
const result = Token(tokenString);
if(!result.access) { return null; }
if(!result.refresh) { return null; }
if(result.refreshIsExpired()) { return null; }
if(result.accessIsExpired() && shouldRefresh) {
await refresh();
return current(false);
}
return result;
}
async function refresh() {
await mutex.run(async() => {
const token = await current(false);
if(!token?.accessIsExpired()) { return; }
const result = await $fetch(auth.refreshApiRoute,
{
baseURL: composables.useDataFetcher.baseURL,
method: "POST",
headers: { "Authorization": `Bearer ${token?.refresh}` }
}
);
setCurrent(result, false);
});
}
function setCurrent(value,
dispatch = true
) {
if(!value) {
localStorage.removeItem("token");
} else {
localStorage.setItem("token", JSON.stringify(value));
}
if(dispatch) {
window.dispatchEvent(new CustomEvent("token-changed"));
}
}
function onChanged(callback) {
window.addEventListener("token-changed", callback);
}
return {
current,
setCurrent,
onChanged
};
};
function Token(tokenString) {
const { access, refresh, displayName } = JSON.parse(tokenString);
function accessIsExpired() {
return isExpired(access);
}
function refreshIsExpired() {
return isExpired(refresh);
}
function isExpired(token) {
try {
const claims = JSON.parse(atob(token.split(".")[1]));
return parseInt(claims.exp) * 1000 < Date.now();
} catch {
throw createError({ statusCode: 401 });
}
}
return {
access,
refresh,
displayName,
accessIsExpired,
refreshIsExpired
};
}