🚚(frontend) redirect to 401 page when 401 error

Users could still be able to edit a document if the
session was expired. It could give the feeling that the
document was not saved.
If during a mutation request (POST, PUT, DELETE),
the server returns a 401 error,
the user is redirected to the 401 page.
This commit is contained in:
Anthony LC
2025-03-18 11:28:35 +01:00
committed by Anthony LC
parent 12c18bc4e9
commit 6627518017
3 changed files with 75 additions and 8 deletions

View File

@@ -31,6 +31,7 @@ and this project adheres to
and descendants views #695
- 🐛(action) fix notify-argocd workflow #713
- 🚨(helm) fix helmfile lint #736
- 🚚(frontend) redirect to 401 page when 401 error #759
## [2.4.0] - 2025-03-06

View File

@@ -1,6 +1,12 @@
import { expect, test } from '@playwright/test';
import { expectLoginPage, keyCloakSignIn, mockedDocument } from './common';
import {
createDoc,
expectLoginPage,
keyCloakSignIn,
mockedDocument,
verifyDocName,
} from './common';
test.describe('Doc Routing', () => {
test.beforeEach(async ({ page }) => {
@@ -50,6 +56,42 @@ test.describe('Doc Routing', () => {
timeout: 15000,
});
});
test('checks 401 on docs/[id] page', async ({ page, browserName }) => {
const [docTitle] = await createDoc(page, 'My new doc', browserName, 1);
await verifyDocName(page, docTitle);
const responsePromise = page.route(
/.*\/link-configuration\/$|users\/me\/$/,
async (route) => {
const request = route.request();
if (
request.method().includes('PUT') ||
request.method().includes('GET')
) {
await route.fulfill({
status: 401,
json: {
detail: 'Log in to access the document',
},
});
} else {
await route.continue();
}
},
);
await page.getByRole('button', { name: 'Share' }).click();
const selectVisibility = page.getByLabel('Visibility', { exact: true });
await selectVisibility.click();
await page.getByLabel('Connected').click();
await responsePromise;
await expect(page.getByText('Log in to access the document')).toBeVisible();
});
});
test.describe('Doc Routing: Not loggued', () => {

View File

@@ -1,9 +1,10 @@
import { CunninghamProvider } from '@openfun/cunningham-react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { useRouter } from 'next/router';
import { useEffect } from 'react';
import { useCunninghamTheme } from '@/cunningham';
import { Auth } from '@/features/auth';
import { Auth, KEY_AUTH, setAuthUrl } from '@/features/auth';
import { useResponsiveStore } from '@/stores/';
import { ConfigProvider } from './config/';
@@ -15,17 +16,19 @@ import { ConfigProvider } from './config/';
* - global cache duration - we decided 3 minutes
* - It can be overridden to each query
*/
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 1000 * 60 * 3,
retry: 1,
},
const defaultOptions = {
queries: {
staleTime: 1000 * 60 * 3,
retry: 1,
},
};
const queryClient = new QueryClient({
defaultOptions,
});
export function AppProvider({ children }: { children: React.ReactNode }) {
const { theme } = useCunninghamTheme();
const { replace } = useRouter();
const initializeResizeListener = useResponsiveStore(
(state) => state.initializeResizeListener,
@@ -36,6 +39,27 @@ export function AppProvider({ children }: { children: React.ReactNode }) {
return cleanupResizeListener;
}, [initializeResizeListener]);
useEffect(() => {
queryClient.setDefaultOptions({
...defaultOptions,
mutations: {
onError: (error) => {
if (
error instanceof Error &&
'status' in error &&
error.status === 401
) {
void queryClient.resetQueries({
queryKey: [KEY_AUTH],
});
setAuthUrl();
void replace(`/401`);
}
},
},
});
}, [replace]);
return (
<QueryClientProvider client={queryClient}>
<CunninghamProvider theme={theme}>