mirror of
https://github.com/suitenumerique/docs
synced 2026-04-21 13:37:20 +00:00
🚸(frontend) redirect on current url tab after 401
When multiple tabs were opened and a 401 error occurred, the user was redirected to the login page, then after login, the user was redirected to the page where the last 401 error occurred. We improved this behavior by saving the url per tab, and after login, the user is redirected to the last url of the current tab.
This commit is contained in:
parent
d9334352bb
commit
7dc7320dac
5 changed files with 85 additions and 23 deletions
|
|
@ -8,6 +8,7 @@ and this project adheres to
|
|||
|
||||
### Fixed
|
||||
|
||||
- 🚸(frontend) redirect on current url tab after 401 #2197
|
||||
- 🐛(frontend) abort check media status unmount #2194
|
||||
- ✨(backend) order pinned documents by last updated at #2028
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,6 @@
|
|||
import crypto from 'crypto';
|
||||
|
||||
import { expect, test } from '@playwright/test';
|
||||
|
||||
import {
|
||||
createDoc,
|
||||
getCurrentConfig,
|
||||
mockedDocument,
|
||||
verifyDocName,
|
||||
} from './utils-common';
|
||||
import { createDoc, getCurrentConfig, verifyDocName } from './utils-common';
|
||||
import { writeInEditor } from './utils-editor';
|
||||
import { SignIn, expectLoginPage } from './utils-signin';
|
||||
import { createRootSubPage } from './utils-sub-pages';
|
||||
|
|
@ -119,13 +112,53 @@ test.describe('Doc Routing: Not logged', () => {
|
|||
page,
|
||||
browserName,
|
||||
}) => {
|
||||
const uuid = crypto.randomUUID();
|
||||
await mockedDocument(page, { link_reach: 'public', id: uuid });
|
||||
await page.goto(`/docs/${uuid}/`);
|
||||
await expect(page.locator('h2').getByText('Mocked document')).toBeVisible();
|
||||
await page.getByRole('button', { name: 'Login' }).click();
|
||||
await page.goto('/');
|
||||
await SignIn(page, browserName);
|
||||
|
||||
const [docTitle1] = await createDoc(page, 'doc-login-1', browserName, 1);
|
||||
await verifyDocName(page, docTitle1);
|
||||
|
||||
const page2 = await page.context().newPage();
|
||||
await page2.goto('/');
|
||||
const [docTitle2] = await createDoc(page2, 'doc-login-2', browserName, 1);
|
||||
await verifyDocName(page2, docTitle2);
|
||||
|
||||
// Remove cookies `docs_sessionid` to simulate the user being logged out
|
||||
await page2.context().clearCookies();
|
||||
await page2.reload();
|
||||
|
||||
// Tab 2 - 401 triggered, user should be redirected to login page
|
||||
await expect(
|
||||
page2
|
||||
.getByRole('main', { name: 'Main content' })
|
||||
.getByRole('button', { name: 'Login' }),
|
||||
).toBeVisible({
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
// Tab 1 - 401 triggered, user should be redirected to login page
|
||||
await page.reload();
|
||||
await expect(
|
||||
page
|
||||
.getByRole('main', { name: 'Main content' })
|
||||
.getByRole('button', { name: 'Login' }),
|
||||
).toBeVisible({
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
// Reconnected
|
||||
await page
|
||||
.getByRole('main', { name: 'Main content' })
|
||||
.getByRole('button', { name: 'Login' })
|
||||
.click();
|
||||
await SignIn(page, browserName, false);
|
||||
await expect(page.locator('h2').getByText('Mocked document')).toBeVisible();
|
||||
|
||||
// Tab 1 - Should be on its doc
|
||||
await verifyDocName(page, docTitle1);
|
||||
|
||||
// Tab 2 - Should be on its doc
|
||||
await page2.reload();
|
||||
await verifyDocName(page2, docTitle2);
|
||||
});
|
||||
|
||||
// eslint-disable-next-line playwright/expect-expect
|
||||
|
|
|
|||
|
|
@ -3,5 +3,5 @@ import { baseApiUrl } from '@/api';
|
|||
export const HOME_URL = '/home/';
|
||||
export const LOGIN_URL = `${baseApiUrl()}authenticate/`;
|
||||
export const LOGOUT_URL = `${baseApiUrl()}logout/`;
|
||||
export const PATH_AUTH_LOCAL_STORAGE = 'docs-path-auth';
|
||||
export const PATH_AUTH_SESSION_STORAGE = 'docs-path-auth';
|
||||
export const SILENT_LOGIN_RETRY = 'silent-login-retry';
|
||||
|
|
|
|||
|
|
@ -1,35 +1,36 @@
|
|||
import { terminateCrispSession } from '@/services/Crisp';
|
||||
import { safeLocalStorage } from '@/utils/storages';
|
||||
import { safeLocalStorage, safeSessionStorage } from '@/utils/storages';
|
||||
|
||||
import {
|
||||
HOME_URL,
|
||||
LOGIN_URL,
|
||||
LOGOUT_URL,
|
||||
PATH_AUTH_LOCAL_STORAGE,
|
||||
PATH_AUTH_SESSION_STORAGE,
|
||||
SILENT_LOGIN_RETRY,
|
||||
} from './conf';
|
||||
|
||||
/**
|
||||
* Get the stored auth URL from local storage
|
||||
* Get the stored auth URL from session storage (per-tab)
|
||||
*/
|
||||
export const getAuthUrl = () => {
|
||||
const path_auth = safeLocalStorage.getItem(PATH_AUTH_LOCAL_STORAGE);
|
||||
const path_auth = safeSessionStorage.getItem(PATH_AUTH_SESSION_STORAGE);
|
||||
if (path_auth) {
|
||||
safeLocalStorage.removeItem(PATH_AUTH_LOCAL_STORAGE);
|
||||
safeSessionStorage.removeItem(PATH_AUTH_SESSION_STORAGE);
|
||||
return path_auth;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Store the current path in local storage if it's not the homepage or root
|
||||
* so we can redirect the user to this path after login
|
||||
* Store the current path in session storage (per-tab) if it's not the
|
||||
* homepage or root, so we can redirect the user to this path after login.
|
||||
* Using sessionStorage ensures each tab independently tracks its own URL.
|
||||
*/
|
||||
export const setAuthUrl = () => {
|
||||
if (
|
||||
window.location.pathname !== '/' &&
|
||||
window.location.pathname !== `${HOME_URL}/`
|
||||
) {
|
||||
safeLocalStorage.setItem(PATH_AUTH_LOCAL_STORAGE, window.location.href);
|
||||
safeSessionStorage.setItem(PATH_AUTH_SESSION_STORAGE, window.location.href);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -50,3 +50,30 @@ export const safeLocalStorage: SyncStorage = {
|
|||
localStorage.removeItem(key);
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* @namespace safeSessionStorage
|
||||
* @description A utility for safely interacting with sessionStorage.
|
||||
* sessionStorage is scoped to the current browser tab, making it suitable
|
||||
* for per-tab state that should not be shared across tabs.
|
||||
*/
|
||||
export const safeSessionStorage: SyncStorage = {
|
||||
getItem: (key: string): string | null => {
|
||||
if (typeof window === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
return sessionStorage.getItem(key);
|
||||
},
|
||||
setItem: (key: string, value: string): void => {
|
||||
if (typeof window === 'undefined') {
|
||||
return;
|
||||
}
|
||||
sessionStorage.setItem(key, value);
|
||||
},
|
||||
removeItem: (key: string): void => {
|
||||
if (typeof window === 'undefined') {
|
||||
return;
|
||||
}
|
||||
sessionStorage.removeItem(key);
|
||||
},
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue