adminforth 3.24.4 → 3.24.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/spa/src/App.vue
CHANGED
|
@@ -286,7 +286,14 @@ async function loadConfig() {
|
|
|
286
286
|
publicConfigLoaded.value = true;
|
|
287
287
|
|
|
288
288
|
await initRouter();
|
|
289
|
-
if (resp
|
|
289
|
+
if (resp?.loggedIn) {
|
|
290
|
+
// the flag the login route guard reads is only ever set by a login, so a 401 can leave it
|
|
291
|
+
// saying "signed out" for a session the server still honours
|
|
292
|
+
userStore.authorize();
|
|
293
|
+
if (route.name === 'login') {
|
|
294
|
+
await router.replace(route.query.next?.toString() || { name: 'home' });
|
|
295
|
+
}
|
|
296
|
+
} else if (resp && route.meta.sidebarAndHeader !== 'none') {
|
|
290
297
|
// for custom layouts we don't force login, they are allowed to be rendered for anonymous user
|
|
291
298
|
await handleNotAuthorized();
|
|
292
299
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// The auth cookie is HttpOnly, so the server is the only place that knows whether a session is live.
|
|
2
|
+
export async function hasLiveSession(): Promise<boolean> {
|
|
3
|
+
const baseUrl = (import.meta as any).env?.VITE_ADMINFORTH_PUBLIC_PATH || '';
|
|
4
|
+
try {
|
|
5
|
+
const response = await fetch(`${baseUrl}/adminapi/v1/check_auth`, {
|
|
6
|
+
method: 'POST',
|
|
7
|
+
headers: { 'Content-Type': 'application/json' },
|
|
8
|
+
body: '{}',
|
|
9
|
+
signal: AbortSignal.timeout(5000),
|
|
10
|
+
});
|
|
11
|
+
if (response.status !== 200) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
// a proxy or captive portal can answer 200 with its own page, so the body has to say so too
|
|
15
|
+
return (await response.json().catch(() => null))?.ok === true;
|
|
16
|
+
} catch {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let inFlight: Promise<boolean> | null = null;
|
|
22
|
+
|
|
23
|
+
/** A 401 can be transient while the cookie is still good. Only worth asking if we thought we were signed in. */
|
|
24
|
+
export async function sessionSurvives401(): Promise<boolean> {
|
|
25
|
+
if (localStorage.getItem('isAuthorized') !== 'true') {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
// an outage 401s every call on the page at once, and they all want the same answer
|
|
29
|
+
inFlight ??= hasLiveSession().finally(() => { inFlight = null; });
|
|
30
|
+
return inFlight;
|
|
31
|
+
}
|
|
@@ -14,6 +14,7 @@ import { useI18n } from 'vue-i18n';
|
|
|
14
14
|
import { onBeforeRouteLeave } from 'vue-router';
|
|
15
15
|
import { reconnect } from '@/websocket';
|
|
16
16
|
import { ADMINFORTH_CLIENT_ID_HEADER, getAdminForthClientId } from './clientId';
|
|
17
|
+
import { sessionSurvives401 } from './session';
|
|
17
18
|
|
|
18
19
|
|
|
19
20
|
|
|
@@ -103,10 +104,16 @@ async function tryAutologin(autologin: string): Promise<boolean> {
|
|
|
103
104
|
return !!coreStore.adminUser;
|
|
104
105
|
}
|
|
105
106
|
|
|
106
|
-
|
|
107
|
+
/** @returns false when the session turned out to be alive and nothing was signed out. */
|
|
108
|
+
export async function handleNotAuthorized(): Promise<boolean> {
|
|
109
|
+
// one 401 during a restart must not throw away a session the server still honours
|
|
110
|
+
if (await sessionSurvives401()) {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
107
113
|
useUserStore().unauthorize();
|
|
108
114
|
useCoreStore().resetAdminUser();
|
|
109
115
|
await redirectToLogin();
|
|
116
|
+
return true;
|
|
110
117
|
}
|
|
111
118
|
|
|
112
119
|
export async function redirectToLogin() {
|
|
@@ -157,7 +164,10 @@ export async function callApi({path, method, body, headers, silentError = false,
|
|
|
157
164
|
try {
|
|
158
165
|
const r = await fetch(fullPath, options);
|
|
159
166
|
if (r.status == 401 && !path.includes('/login')) {
|
|
160
|
-
|
|
167
|
+
// when the session survived there is no redirect coming, so the failure has to be visible
|
|
168
|
+
if (!await handleNotAuthorized() && !silentError) {
|
|
169
|
+
adminforth.alert({variant:'danger', message: t('Something went wrong, please try again later'),})
|
|
170
|
+
}
|
|
161
171
|
return null;
|
|
162
172
|
}
|
|
163
173
|
return await r.json();
|