Skip to content

Commit a4b0345

Browse files
committed
🚧(frontend) implement Mozilla Django OIDC returnTo for login flow
Use returnTo capability to pass target URL during authentication, eliminating redundant renders when returning to app. Work around Next.js router limitations (can't pass data when replacing URL) by adding temporary target parameter. Discovered issue: 401 responses on document fetch trigger retry loop instead of redirecting to login. Will address in subsequent commits.
1 parent f1844f2 commit a4b0345

File tree

6 files changed

+47
-44
lines changed

6 files changed

+47
-44
lines changed

src/backend/impress/settings.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,9 @@ class Base(Configuration):
551551
environ_name="OIDC_STORE_REFRESH_TOKEN_KEY",
552552
environ_prefix=None,
553553
)
554+
OIDC_REDIRECT_FIELD_NAME = values.Value(
555+
"returnTo", environ_name="OIDC_REDIRECT_FIELD_NAME", environ_prefix=None
556+
)
554557

555558
# WARNING: Enabling this setting allows multiple user accounts to share the same email
556559
# address. This may cause security issues and is not recommended for production use when

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { useRouter } from 'next/router';
44
import { useEffect } from 'react';
55

66
import { useCunninghamTheme } from '@/cunningham';
7-
import { Auth, KEY_AUTH, setAuthUrl } from '@/features/auth';
7+
import { Auth, KEY_AUTH } from '@/features/auth';
88
import { useResponsiveStore } from '@/stores/';
99

1010
import { ConfigProvider } from './config/';
@@ -51,8 +51,9 @@ export function AppProvider({ children }: { children: React.ReactNode }) {
5151
void queryClient.resetQueries({
5252
queryKey: [KEY_AUTH],
5353
});
54-
setAuthUrl();
55-
void replace(`/401`);
54+
void replace(
55+
`/401?returnTo=${encodeURIComponent(window.location.pathname)}`,
56+
);
5657
}
5758
},
5859
},

src/frontend/apps/impress/src/features/auth/components/Auth.tsx

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { useConfig } from '@/core';
77

88
import { HOME_URL } from '../conf';
99
import { useAuth } from '../hooks';
10-
import { getAuthUrl, gotoLogin } from '../utils';
10+
import { gotoLogin } from '../utils';
1111

1212
export const Auth = ({ children }: PropsWithChildren) => {
1313
const { isLoading, pathAllowed, isFetchedAfterMount, authenticated } =
@@ -23,21 +23,22 @@ export const Auth = ({ children }: PropsWithChildren) => {
2323
);
2424
}
2525

26+
// todo - clarify this trick.
2627
/**
27-
* If the user is authenticated and wanted initially to access a document,
28-
* we redirect to the document page.
29-
*/
30-
if (authenticated) {
31-
const authUrl = getAuthUrl();
32-
if (authUrl) {
33-
void replace(authUrl);
34-
return (
35-
<Box $height="100vh" $width="100vw" $align="center" $justify="center">
36-
<Loader />
37-
</Box>
38-
);
39-
}
40-
}
28+
// * If the user is authenticated and wanted initially to access a document,
29+
// * we redirect to the document page.
30+
// */
31+
// if (authenticated) {
32+
// const authUrl = getAuthUrl();
33+
// if (authUrl) {
34+
// void replace(authUrl);
35+
// return (
36+
// <Box $height="100vh" $width="100vw" $align="center" $justify="center">
37+
// <Loader />
38+
// </Box>
39+
// );
40+
// }
41+
// }
4142

4243
/**
4344
* If the user is not authenticated and the path is not allowed, we redirect to the login page.

src/frontend/apps/impress/src/features/auth/utils.ts

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,12 @@
11
import { terminateCrispSession } from '@/services/Crisp';
22

3-
import { LOGIN_URL, LOGOUT_URL, PATH_AUTH_LOCAL_STORAGE } from './conf';
3+
import { LOGIN_URL, LOGOUT_URL } from './conf';
4+
import { backendUrl } from "@/api";
45

5-
export const getAuthUrl = () => {
6-
const path_auth = localStorage.getItem(PATH_AUTH_LOCAL_STORAGE);
7-
if (path_auth) {
8-
localStorage.removeItem(PATH_AUTH_LOCAL_STORAGE);
9-
return path_auth;
10-
}
11-
};
12-
13-
export const setAuthUrl = () => {
14-
if (window.location.pathname !== '/') {
15-
localStorage.setItem(PATH_AUTH_LOCAL_STORAGE, window.location.pathname);
16-
}
17-
};
18-
19-
export const gotoLogin = (withRedirect = true) => {
20-
if (withRedirect) {
21-
setAuthUrl();
22-
}
236

24-
window.location.replace(LOGIN_URL);
7+
export const gotoLogin = (returnTo = '/') => {
8+
const authenticateUrl = LOGIN_URL + `?returnTo=${backendUrl() + returnTo}`
9+
window.location.replace(authenticateUrl);
2510
};
2611

2712
export const gotoLogout = () => {

src/frontend/apps/impress/src/pages/401.tsx

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Button } from '@openfun/cunningham-react';
22
import Image from 'next/image';
33
import { useRouter } from 'next/router';
4-
import { ReactElement, useEffect } from 'react';
4+
import {ReactElement, useEffect, useState} from 'react';
55
import { useTranslation } from 'react-i18next';
66

77
import img401 from '@/assets/icons/icon-401.png';
@@ -13,7 +13,19 @@ import { NextPageWithLayout } from '@/types/next';
1313
const Page: NextPageWithLayout = () => {
1414
const { t } = useTranslation();
1515
const { authenticated } = useAuth();
16-
const { replace } = useRouter();
16+
const router = useRouter();
17+
const { replace } = router;
18+
19+
const [returnTo, setReturnTo] = useState<string | undefined>(undefined)
20+
const { returnTo: returnToParams } = router.query;
21+
22+
23+
useEffect(() => {
24+
if (returnToParams) {
25+
setReturnTo(returnToParams as string)
26+
void replace('/401')
27+
}
28+
}, [returnToParams]);
1729

1830
useEffect(() => {
1931
if (authenticated) {
@@ -42,7 +54,7 @@ const Page: NextPageWithLayout = () => {
4254
{t('Log in to access the document.')}
4355
</Text>
4456

45-
<Button onClick={() => gotoLogin(false)} aria-label={t('Login')}>
57+
<Button onClick={() => gotoLogin(returnTo)} aria-label={t('Login')}>
4658
{t('Login')}
4759
</Button>
4860
</Box>

src/frontend/apps/impress/src/pages/docs/[id]/index.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
useDoc,
1515
useDocStore,
1616
} from '@/docs/doc-management/';
17-
import { KEY_AUTH, setAuthUrl } from '@/features/auth';
17+
import { KEY_AUTH } from '@/features/auth';
1818
import { MainLayout } from '@/layouts';
1919
import { useBroadcastStore } from '@/stores';
2020
import { NextPageWithLayout } from '@/types/next';
@@ -115,8 +115,9 @@ const DocPage = ({ id }: DocProps) => {
115115
void queryClient.resetQueries({
116116
queryKey: [KEY_AUTH],
117117
});
118-
setAuthUrl();
119-
void replace(`/401`);
118+
void replace(
119+
`/401?returnTo=${encodeURIComponent(window.location.pathname)}`,
120+
);
120121
return null;
121122
}
122123

0 commit comments

Comments
 (0)