fix: expired token issue
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
@@ -29,7 +29,7 @@ class DefaultController extends ControllerAbstract
|
||||
return new FileResponse(
|
||||
$this->rootDir . '/public/private.html',
|
||||
Response::HTTP_OK,
|
||||
['Content-Type' => 'text/html']
|
||||
['Content-Type' => 'text/html', 'Cache-Control' => 'no-store']
|
||||
);
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ class DefaultController extends ControllerAbstract
|
||||
$response = new FileResponse(
|
||||
$this->rootDir . '/public/public.html',
|
||||
Response::HTTP_OK,
|
||||
['Content-Type' => 'text/html']
|
||||
['Content-Type' => 'text/html', 'Cache-Control' => 'no-store']
|
||||
);
|
||||
|
||||
// Clear any stale auth cookies since the user is not authenticated
|
||||
@@ -58,7 +58,7 @@ class DefaultController extends ControllerAbstract
|
||||
return new FileResponse(
|
||||
$this->rootDir . '/public/public.html',
|
||||
Response::HTTP_OK,
|
||||
['Content-Type' => 'text/html']
|
||||
['Content-Type' => 'text/html', 'Cache-Control' => 'no-store']
|
||||
);
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ class DefaultController extends ControllerAbstract
|
||||
return new FileResponse(
|
||||
$this->rootDir . '/public/private.html',
|
||||
Response::HTTP_OK,
|
||||
['Content-Type' => 'text/html']
|
||||
['Content-Type' => 'text/html', 'Cache-Control' => 'no-store']
|
||||
);
|
||||
}
|
||||
|
||||
@@ -128,7 +128,7 @@ class DefaultController extends ControllerAbstract
|
||||
$response = new FileResponse(
|
||||
$this->rootDir . '/public/public.html',
|
||||
Response::HTTP_OK,
|
||||
['Content-Type' => 'text/html']
|
||||
['Content-Type' => 'text/html', 'Cache-Control' => 'no-store']
|
||||
);
|
||||
|
||||
// Clear any stale auth cookies since the user is not authenticated
|
||||
|
||||
@@ -8,6 +8,7 @@ import { useModuleStore } from '@KTXC/stores/moduleStore'
|
||||
import { useTenantStore } from '@KTXC/stores/tenantStore'
|
||||
import { useUserStore } from '@KTXC/stores/userStore'
|
||||
import { fetchWrapper } from '@KTXC/utils/helpers/fetch-wrapper'
|
||||
import { FetchError } from '@KTXC/utils/helpers/fetch-wrapper-core'
|
||||
import { initializeModules } from '@KTXC/utils/modules'
|
||||
import { createSessionMonitor } from '@KTXC/services/authManager'
|
||||
import App from './App.vue'
|
||||
@@ -62,5 +63,11 @@ globalWindow.Pinia = PiniaLib as unknown
|
||||
app.mount('#app');
|
||||
} catch (e) {
|
||||
console.error('Bootstrap failed:', e);
|
||||
// The private shell is unusable without /init. If the server rejected
|
||||
// the session, drop the stale local identity and return to login.
|
||||
if (e instanceof FetchError && (e.status === 401 || e.status === 403)) {
|
||||
userStore.clearAuth();
|
||||
window.location.href = '/login';
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
+6
-15
@@ -2,16 +2,18 @@ import { createApp } from 'vue';
|
||||
import { createPinia } from 'pinia';
|
||||
import App from './App.vue';
|
||||
import { router } from './router';
|
||||
import { AUTH_STORAGE_KEY } from '@KTXC/stores/userStore';
|
||||
import vuetify from './plugins/vuetify/index';
|
||||
|
||||
// Material Design Icons (Vuetify mdi icon set)
|
||||
import '@mdi/font/css/materialdesignicons.min.css';
|
||||
import '@fontsource/public-sans/index.css'
|
||||
|
||||
// The public app is served when the user has no valid server session.
|
||||
// Clear any stale identity data from localStorage to ensure the client
|
||||
// state matches the server's determination that the user is unauthenticated.
|
||||
//localStorage.removeItem('identityStore.self');
|
||||
// The public app is only served when the server has determined the user is
|
||||
// unauthenticated. Clear any stale identity persisted by a previous session,
|
||||
// otherwise the shared router guard would treat the user as authenticated
|
||||
// and render the private shell without any of its state loaded.
|
||||
localStorage.removeItem(AUTH_STORAGE_KEY);
|
||||
|
||||
const app = createApp(App);
|
||||
const pinia = createPinia();
|
||||
@@ -19,15 +21,4 @@ app.use(pinia);
|
||||
app.use(router);
|
||||
app.use(vuetify);
|
||||
|
||||
// Wait for router to be ready, then ensure we're on a public route
|
||||
//router.isReady().then(() => {
|
||||
// If the current route requires auth, redirect to login
|
||||
// This handles the case where user navigates to / with an expired session
|
||||
//const currentRoute = router.currentRoute.value;
|
||||
//const requiresAuth = currentRoute.matched.some(record => record.meta?.requiresAuth);
|
||||
//if (requiresAuth || currentRoute.path === '/') {
|
||||
// router.replace('/login');
|
||||
//}
|
||||
//});
|
||||
|
||||
app.mount('#app');
|
||||
|
||||
@@ -8,7 +8,8 @@ import type { UserSettingsInterface } from '@KTXC/types/user/userSettingsTypes';
|
||||
import { UserProfile } from '@KTXC/models/userProfile';
|
||||
import { UserSettings } from '@KTXC/models/userSettings';
|
||||
|
||||
const STORAGE_KEY = 'userStore.auth';
|
||||
export const AUTH_STORAGE_KEY = 'userStore.auth';
|
||||
const STORAGE_KEY = AUTH_STORAGE_KEY;
|
||||
|
||||
// Flush pending updates before page unload
|
||||
if (typeof window !== 'undefined') {
|
||||
|
||||
@@ -22,6 +22,14 @@ export interface RequestCallOptions {
|
||||
headers?: Record<string, string>;
|
||||
}
|
||||
|
||||
/** Error thrown for non-2xx responses, carrying the HTTP status code */
|
||||
export class FetchError extends Error {
|
||||
constructor(message: string, public readonly status: number) {
|
||||
super(message);
|
||||
this.name = 'FetchError';
|
||||
}
|
||||
}
|
||||
|
||||
function getCsrfToken(): string | null {
|
||||
if (typeof document === 'undefined') return null;
|
||||
const cookie = document.cookie.split('; ').find(r => r.startsWith('X-CSRF-TOKEN='));
|
||||
@@ -61,8 +69,11 @@ export function createFetchWrapper(options: FetchWrapperOptions = {}) {
|
||||
|
||||
if (!response.ok) {
|
||||
const text = await response.text();
|
||||
const data = text ? JSON.parse(text) : null;
|
||||
throw new Error(data?.message || response.statusText);
|
||||
let data = null;
|
||||
try {
|
||||
data = text ? JSON.parse(text) : null;
|
||||
} catch { /* non-JSON error body */ }
|
||||
throw new FetchError(data?.message || response.statusText, response.status);
|
||||
}
|
||||
|
||||
if (callOptions?.onStream) {
|
||||
|
||||
Reference in New Issue
Block a user