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(
|
return new FileResponse(
|
||||||
$this->rootDir . '/public/private.html',
|
$this->rootDir . '/public/private.html',
|
||||||
Response::HTTP_OK,
|
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(
|
$response = new FileResponse(
|
||||||
$this->rootDir . '/public/public.html',
|
$this->rootDir . '/public/public.html',
|
||||||
Response::HTTP_OK,
|
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
|
// Clear any stale auth cookies since the user is not authenticated
|
||||||
@@ -58,7 +58,7 @@ class DefaultController extends ControllerAbstract
|
|||||||
return new FileResponse(
|
return new FileResponse(
|
||||||
$this->rootDir . '/public/public.html',
|
$this->rootDir . '/public/public.html',
|
||||||
Response::HTTP_OK,
|
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(
|
return new FileResponse(
|
||||||
$this->rootDir . '/public/private.html',
|
$this->rootDir . '/public/private.html',
|
||||||
Response::HTTP_OK,
|
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(
|
$response = new FileResponse(
|
||||||
$this->rootDir . '/public/public.html',
|
$this->rootDir . '/public/public.html',
|
||||||
Response::HTTP_OK,
|
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
|
// 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 { useTenantStore } from '@KTXC/stores/tenantStore'
|
||||||
import { useUserStore } from '@KTXC/stores/userStore'
|
import { useUserStore } from '@KTXC/stores/userStore'
|
||||||
import { fetchWrapper } from '@KTXC/utils/helpers/fetch-wrapper'
|
import { fetchWrapper } from '@KTXC/utils/helpers/fetch-wrapper'
|
||||||
|
import { FetchError } from '@KTXC/utils/helpers/fetch-wrapper-core'
|
||||||
import { initializeModules } from '@KTXC/utils/modules'
|
import { initializeModules } from '@KTXC/utils/modules'
|
||||||
import { createSessionMonitor } from '@KTXC/services/authManager'
|
import { createSessionMonitor } from '@KTXC/services/authManager'
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
@@ -62,5 +63,11 @@ globalWindow.Pinia = PiniaLib as unknown
|
|||||||
app.mount('#app');
|
app.mount('#app');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Bootstrap failed:', 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 { createPinia } from 'pinia';
|
||||||
import App from './App.vue';
|
import App from './App.vue';
|
||||||
import { router } from './router';
|
import { router } from './router';
|
||||||
|
import { AUTH_STORAGE_KEY } from '@KTXC/stores/userStore';
|
||||||
import vuetify from './plugins/vuetify/index';
|
import vuetify from './plugins/vuetify/index';
|
||||||
|
|
||||||
// Material Design Icons (Vuetify mdi icon set)
|
// Material Design Icons (Vuetify mdi icon set)
|
||||||
import '@mdi/font/css/materialdesignicons.min.css';
|
import '@mdi/font/css/materialdesignicons.min.css';
|
||||||
import '@fontsource/public-sans/index.css'
|
import '@fontsource/public-sans/index.css'
|
||||||
|
|
||||||
// The public app is served when the user has no valid server session.
|
// The public app is only served when the server has determined the user is
|
||||||
// Clear any stale identity data from localStorage to ensure the client
|
// unauthenticated. Clear any stale identity persisted by a previous session,
|
||||||
// state matches the server's determination that the user is unauthenticated.
|
// otherwise the shared router guard would treat the user as authenticated
|
||||||
//localStorage.removeItem('identityStore.self');
|
// and render the private shell without any of its state loaded.
|
||||||
|
localStorage.removeItem(AUTH_STORAGE_KEY);
|
||||||
|
|
||||||
const app = createApp(App);
|
const app = createApp(App);
|
||||||
const pinia = createPinia();
|
const pinia = createPinia();
|
||||||
@@ -19,15 +21,4 @@ app.use(pinia);
|
|||||||
app.use(router);
|
app.use(router);
|
||||||
app.use(vuetify);
|
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');
|
app.mount('#app');
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ import type { UserSettingsInterface } from '@KTXC/types/user/userSettingsTypes';
|
|||||||
import { UserProfile } from '@KTXC/models/userProfile';
|
import { UserProfile } from '@KTXC/models/userProfile';
|
||||||
import { UserSettings } from '@KTXC/models/userSettings';
|
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
|
// Flush pending updates before page unload
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
|
|||||||
@@ -22,6 +22,14 @@ export interface RequestCallOptions {
|
|||||||
headers?: Record<string, string>;
|
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 {
|
function getCsrfToken(): string | null {
|
||||||
if (typeof document === 'undefined') return null;
|
if (typeof document === 'undefined') return null;
|
||||||
const cookie = document.cookie.split('; ').find(r => r.startsWith('X-CSRF-TOKEN='));
|
const cookie = document.cookie.split('; ').find(r => r.startsWith('X-CSRF-TOKEN='));
|
||||||
@@ -61,8 +69,11 @@ export function createFetchWrapper(options: FetchWrapperOptions = {}) {
|
|||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const text = await response.text();
|
const text = await response.text();
|
||||||
const data = text ? JSON.parse(text) : null;
|
let data = null;
|
||||||
throw new Error(data?.message || response.statusText);
|
try {
|
||||||
|
data = text ? JSON.parse(text) : null;
|
||||||
|
} catch { /* non-JSON error body */ }
|
||||||
|
throw new FetchError(data?.message || response.statusText, response.status);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (callOptions?.onStream) {
|
if (callOptions?.onStream) {
|
||||||
|
|||||||
Reference in New Issue
Block a user