Skip to content

Commit 6627518

Browse files
committed
🚚(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.
1 parent 12c18bc commit 6627518

File tree

3 files changed

+75
-8
lines changed

3 files changed

+75
-8
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ and this project adheres to
3131
and descendants views #695
3232
- 🐛(action) fix notify-argocd workflow #713
3333
- 🚨(helm) fix helmfile lint #736
34+
- 🚚(frontend) redirect to 401 page when 401 error #759
3435

3536

3637
## [2.4.0] - 2025-03-06

src/frontend/apps/e2e/__tests__/app-impress/doc-routing.spec.ts

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import { expect, test } from '@playwright/test';
22

3-
import { expectLoginPage, keyCloakSignIn, mockedDocument } from './common';
3+
import {
4+
createDoc,
5+
expectLoginPage,
6+
keyCloakSignIn,
7+
mockedDocument,
8+
verifyDocName,
9+
} from './common';
410

511
test.describe('Doc Routing', () => {
612
test.beforeEach(async ({ page }) => {
@@ -50,6 +56,42 @@ test.describe('Doc Routing', () => {
5056
timeout: 15000,
5157
});
5258
});
59+
60+
test('checks 401 on docs/[id] page', async ({ page, browserName }) => {
61+
const [docTitle] = await createDoc(page, 'My new doc', browserName, 1);
62+
await verifyDocName(page, docTitle);
63+
64+
const responsePromise = page.route(
65+
/.*\/link-configuration\/$|users\/me\/$/,
66+
async (route) => {
67+
const request = route.request();
68+
69+
if (
70+
request.method().includes('PUT') ||
71+
request.method().includes('GET')
72+
) {
73+
await route.fulfill({
74+
status: 401,
75+
json: {
76+
detail: 'Log in to access the document',
77+
},
78+
});
79+
} else {
80+
await route.continue();
81+
}
82+
},
83+
);
84+
85+
await page.getByRole('button', { name: 'Share' }).click();
86+
87+
const selectVisibility = page.getByLabel('Visibility', { exact: true });
88+
await selectVisibility.click();
89+
await page.getByLabel('Connected').click();
90+
91+
await responsePromise;
92+
93+
await expect(page.getByText('Log in to access the document')).toBeVisible();
94+
});
5395
});
5496

5597
test.describe('Doc Routing: Not loggued', () => {

src/frontend/apps/impress/src/core/AppProvider.tsx

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { CunninghamProvider } from '@openfun/cunningham-react';
22
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
3+
import { useRouter } from 'next/router';
34
import { useEffect } from 'react';
45

56
import { useCunninghamTheme } from '@/cunningham';
6-
import { Auth } from '@/features/auth';
7+
import { Auth, KEY_AUTH, setAuthUrl } from '@/features/auth';
78
import { useResponsiveStore } from '@/stores/';
89

910
import { ConfigProvider } from './config/';
@@ -15,17 +16,19 @@ import { ConfigProvider } from './config/';
1516
* - global cache duration - we decided 3 minutes
1617
* - It can be overridden to each query
1718
*/
18-
const queryClient = new QueryClient({
19-
defaultOptions: {
20-
queries: {
21-
staleTime: 1000 * 60 * 3,
22-
retry: 1,
23-
},
19+
const defaultOptions = {
20+
queries: {
21+
staleTime: 1000 * 60 * 3,
22+
retry: 1,
2423
},
24+
};
25+
const queryClient = new QueryClient({
26+
defaultOptions,
2527
});
2628

2729
export function AppProvider({ children }: { children: React.ReactNode }) {
2830
const { theme } = useCunninghamTheme();
31+
const { replace } = useRouter();
2932

3033
const initializeResizeListener = useResponsiveStore(
3134
(state) => state.initializeResizeListener,
@@ -36,6 +39,27 @@ export function AppProvider({ children }: { children: React.ReactNode }) {
3639
return cleanupResizeListener;
3740
}, [initializeResizeListener]);
3841

42+
useEffect(() => {
43+
queryClient.setDefaultOptions({
44+
...defaultOptions,
45+
mutations: {
46+
onError: (error) => {
47+
if (
48+
error instanceof Error &&
49+
'status' in error &&
50+
error.status === 401
51+
) {
52+
void queryClient.resetQueries({
53+
queryKey: [KEY_AUTH],
54+
});
55+
setAuthUrl();
56+
void replace(`/401`);
57+
}
58+
},
59+
},
60+
});
61+
}, [replace]);
62+
3963
return (
4064
<QueryClientProvider client={queryClient}>
4165
<CunninghamProvider theme={theme}>

0 commit comments

Comments
 (0)