Files
FictionArchive/fictionarchive-web-astro/src/lib/auth/authStore.ts

116 lines
3.2 KiB
TypeScript

import { writable, derived } from 'svelte/store';
import type { User } from 'oidc-client-ts';
import { userManager, isOidcConfigured } from './oidcConfig';
// Stores
export const user = writable<User | null>(null);
export const isLoading = writable(true);
export const isAuthenticated = derived(user, ($user) => $user !== null);
export const isConfigured = isOidcConfigured;
// Cookie management
function setCookieFromUser(u: User) {
if (!u?.access_token) return;
const isProduction = window.location.hostname !== 'localhost';
const domain = isProduction ? '.orfl.xyz' : undefined;
const secure = isProduction;
const sameSite = isProduction ? 'None' : 'Lax';
const cookieValue = `fa_session=${u.access_token}; path=/; ${secure ? 'secure; ' : ''}samesite=${sameSite}${domain ? `; domain=${domain}` : ''}`;
document.cookie = cookieValue;
}
function clearFaSessionCookie() {
const isProduction = window.location.hostname !== 'localhost';
const domain = isProduction ? '.orfl.xyz' : undefined;
const cookieValue = `fa_session=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT${domain ? `; domain=${domain}` : ''}`;
document.cookie = cookieValue;
}
// Track if callback has been handled to prevent double processing
let callbackHandled = false;
export async function initAuth() {
if (!userManager) {
isLoading.set(false);
return;
}
// Handle callback if auth params are present
const url = new URL(window.location.href);
const hasAuthParams =
url.searchParams.has('code') ||
url.searchParams.has('id_token') ||
url.searchParams.has('error');
if (hasAuthParams && !callbackHandled) {
callbackHandled = true;
try {
const result = await userManager.signinRedirectCallback();
user.set(result ?? null);
if (result) {
setCookieFromUser(result);
}
} catch (e) {
console.error('Failed to complete sign-in redirect', e);
} finally {
const cleanUrl = `${url.origin}${url.pathname}`;
window.history.replaceState({}, document.title, cleanUrl);
}
}
// Load existing user
try {
const loadedUser = await userManager.getUser();
user.set(loadedUser ?? null);
if (loadedUser) {
setCookieFromUser(loadedUser);
}
} catch (e) {
console.error('Failed to load user', e);
}
isLoading.set(false);
// Event listeners
userManager.events.addUserLoaded((u) => {
user.set(u);
setCookieFromUser(u);
});
userManager.events.addUserUnloaded(() => {
user.set(null);
clearFaSessionCookie();
});
userManager.events.addUserSignedOut(() => {
user.set(null);
clearFaSessionCookie();
});
}
export async function login() {
if (!userManager) {
console.warn('OIDC is not configured; set PUBLIC_OIDC_* environment variables.');
return;
}
await userManager.signinRedirect();
}
export async function logout() {
if (!userManager) {
console.warn('OIDC is not configured; set PUBLIC_OIDC_* environment variables.');
return;
}
try {
clearFaSessionCookie();
await userManager.signoutRedirect();
} catch (error) {
console.error('Failed to sign out via redirect, clearing local session instead.', error);
await userManager.removeUser();
user.set(null);
}
}