mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-08-27 01:22:49 +00:00
fix(layouts): import baseApi in BackofficeLayout and PortalLayout for API state management refactor(auth): remove clearCurrentProfile call on logout and improve cookie removal logic chore(auth-storage): enhance cookie management with secure and sameSite attributes, and streamline cleanup process
184 lines
4.7 KiB
TypeScript
184 lines
4.7 KiB
TypeScript
import Cookies from "js-cookie";
|
|
|
|
function getCookieExpiresDays(): number {
|
|
if (typeof window === "undefined") return 1;
|
|
try {
|
|
const rememberMe = localStorage.getItem("rememberMe") === "true";
|
|
return rememberMe ? 15 : 1;
|
|
} catch {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
const isHttps =
|
|
typeof window !== "undefined" && window.location.protocol === "https:";
|
|
|
|
function removeCookieCompletely(name: string) {
|
|
if (typeof document === "undefined") return;
|
|
|
|
const paths = ["/", ""];
|
|
const samesites: Array<"strict" | "lax" | "none" | undefined> = [
|
|
"strict",
|
|
"lax",
|
|
undefined,
|
|
];
|
|
const secures = [true, false];
|
|
|
|
for (const path of paths) {
|
|
for (const secure of secures) {
|
|
for (const sameSite of samesites) {
|
|
try {
|
|
Cookies.remove(name, { path, secure, sameSite });
|
|
} catch {
|
|
// ignore
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const pastDate = "Thu, 01 Jan 1970 00:00:00 GMT";
|
|
const expireStrings = [
|
|
`${name}=; path=/; expires=${pastDate}; max-age=0`,
|
|
`${name}=; path=/; expires=${pastDate}; max-age=0; SameSite=Strict`,
|
|
`${name}=; path=/; expires=${pastDate}; max-age=0; SameSite=Strict; Secure`,
|
|
`${name}=; path=/; expires=${pastDate}; max-age=0; SameSite=Lax`,
|
|
`${name}=; path=/; expires=${pastDate}; max-age=0; SameSite=Lax; Secure`,
|
|
`${name}=; expires=${pastDate}; max-age=0`,
|
|
];
|
|
|
|
try {
|
|
const domain = window.location.hostname;
|
|
const domainParts = domain.split(".");
|
|
if (domainParts.length > 1) {
|
|
expireStrings.push(
|
|
`${name}=; path=/; domain=${domain}; expires=${pastDate}; max-age=0`
|
|
);
|
|
expireStrings.push(
|
|
`${name}=; path=/; domain=.${domain}; expires=${pastDate}; max-age=0`
|
|
);
|
|
}
|
|
} catch {
|
|
// ignore
|
|
}
|
|
|
|
expireStrings.forEach((str) => {
|
|
try {
|
|
document.cookie = str;
|
|
} catch {
|
|
// ignore
|
|
}
|
|
});
|
|
}
|
|
|
|
interface Backend {
|
|
get(k: string): string | null;
|
|
set(k: string, v: string): void;
|
|
remove(k: string): void;
|
|
}
|
|
|
|
const localStore: Backend = {
|
|
get: (k) => localStorage.getItem(k),
|
|
set: (k, v) => localStorage.setItem(k, v),
|
|
remove: (k) => localStorage.removeItem(k),
|
|
};
|
|
|
|
const cookieStore: Backend = {
|
|
get: (k) => Cookies.get(k) ?? null,
|
|
set: (k, v) =>
|
|
Cookies.set(k, v, {
|
|
path: "/",
|
|
secure: isHttps,
|
|
sameSite: "strict",
|
|
expires: getCookieExpiresDays(),
|
|
}),
|
|
remove: (k) => removeCookieCompletely(k),
|
|
};
|
|
|
|
const AUTH_KEY_NAMES = [
|
|
"auth-token",
|
|
"refresh-token",
|
|
"auth-user",
|
|
"profile-id",
|
|
"current-profile",
|
|
];
|
|
|
|
const KNOWN_PREFIXES = ["ema-backoffice", "ema-portal", "ema-auth", ""];
|
|
|
|
let _prefix = "ema-auth";
|
|
let backend: Backend = localStore;
|
|
|
|
export function configureAuthStorage(prefix: string, useCookies = false) {
|
|
_prefix = prefix;
|
|
backend = useCookies ? cookieStore : localStore;
|
|
if (useCookies) {
|
|
// drop pre-migration localStorage leftovers so tokens live in cookies only
|
|
AUTH_KEY_NAMES.forEach((k) => localStorage.removeItem(`${prefix}-${k}`));
|
|
}
|
|
}
|
|
|
|
function key(k: string) {
|
|
return `${_prefix}-${k}`;
|
|
}
|
|
|
|
export const authStorage = {
|
|
getToken: () => backend.get(key("auth-token")) ?? undefined,
|
|
setToken: (token: string) => backend.set(key("auth-token"), token),
|
|
getRefreshToken: () => backend.get(key("refresh-token")) ?? undefined,
|
|
setRefreshToken: (t: string) => backend.set(key("refresh-token"), t),
|
|
getUser: <T = unknown>(): T | null => {
|
|
try {
|
|
return JSON.parse(backend.get(key("auth-user")) ?? "null") as T | null;
|
|
} catch {
|
|
return null;
|
|
}
|
|
},
|
|
setUser: <T>(u: T) => backend.set(key("auth-user"), JSON.stringify(u)),
|
|
getProfileId: () => backend.get(key("profile-id")) ?? undefined,
|
|
setProfileId: (id: string) => backend.set(key("profile-id"), id),
|
|
getProfile: <T = unknown>(): T | null => {
|
|
try {
|
|
return JSON.parse(
|
|
backend.get(key("current-profile")) ?? "null",
|
|
) as T | null;
|
|
} catch {
|
|
return null;
|
|
}
|
|
},
|
|
setProfile: <T>(p: T) =>
|
|
backend.set(key("current-profile"), JSON.stringify(p)),
|
|
removeProfile: () => backend.remove(key("current-profile")),
|
|
clear: () => {
|
|
const prefixes = Array.from(new Set([_prefix, ...KNOWN_PREFIXES]));
|
|
prefixes.forEach((p) => {
|
|
AUTH_KEY_NAMES.forEach((k) => {
|
|
const fullKey = p ? `${p}-${k}` : k;
|
|
removeCookieCompletely(fullKey);
|
|
try {
|
|
localStorage.removeItem(fullKey);
|
|
} catch {
|
|
// ignore
|
|
}
|
|
});
|
|
});
|
|
|
|
try {
|
|
localStorage.removeItem("rememberMe");
|
|
} catch {
|
|
// ignore
|
|
}
|
|
|
|
try {
|
|
if (typeof Cookies !== "undefined") {
|
|
const allCookies = Cookies.get();
|
|
if (allCookies) {
|
|
Object.keys(allCookies).forEach((name) => {
|
|
removeCookieCompletely(name);
|
|
});
|
|
}
|
|
}
|
|
} catch {
|
|
// ignore
|
|
}
|
|
},
|
|
};
|