Skip to content

Commit

Permalink
Merge pull request #469 from InseeFr/v2-auth-oidc-spa
Browse files Browse the repository at this point in the history
Fix: auth with oidc-spa library
  • Loading branch information
laurentC35 authored Nov 6, 2023
2 parents 8a0ba22 + 27a29cb commit a6d8b2c
Show file tree
Hide file tree
Showing 10 changed files with 125 additions and 341 deletions.
5 changes: 2 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "stromae",
"version": "2.4.12",
"version": "2.4.13",
"description": "Web application for the management of questionnaires powered by Lunatic",
"repository": {
"type": "git",
Expand All @@ -18,8 +18,7 @@
"@testing-library/react": "^11.1.0",
"@testing-library/user-event": "^12.1.10",
"date-fns": "^2.29.3",
"jwt-decode": "^3.1.2",
"oidc-client-ts": "^2.3.0",
"oidc-spa": "^2.0.3",
"powerhooks": "^1.0.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand Down
10 changes: 6 additions & 4 deletions src/components/auth/hoc/hoc.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import { useContext } from 'react';
import { AuthContext } from '../provider/component';
import { useAuth } from 'utils/hooks/useAuth';

const secure = (WrappedComponent) => {
const Component = (props) => {
const { isUserLoggedIn, login } = useContext(AuthContext);
const { oidc } = useAuth();
const { isUserLoggedIn, login } = oidc;
const { otherProps } = props;

const ReturnedComponent = <WrappedComponent {...otherProps} />;

if (isUserLoggedIn) {
return ReturnedComponent;
}
login();
login({
doesCurrentHrefRequiresAuth: true,
});
return null;
};

Expand Down
82 changes: 43 additions & 39 deletions src/components/auth/provider/component.js
Original file line number Diff line number Diff line change
@@ -1,56 +1,60 @@
import React, { useEffect, useState } from 'react';
import { errorDictionary } from '../../../i18n';
import { createOidcClient } from '../../../utils/auth';
import { NONE, OIDC } from '../../../utils/constants';
import { listenActivity } from '../../../utils/events';
import { LoaderSimple } from 'components/shared/loader';
import { createOidcProvider } from 'oidc-spa/react';
import React from 'react';
import { OIDC, READ_ONLY } from '../../../utils/constants';
import { environment, oidcConf } from '../../../utils/read-env-vars';
import { LoaderSimple } from '../../shared/loader';

const dummyOidcClient = {
isUserLoggedIn: true,
getUser: () => ({ accessToken: null, sub: '' }),
logout: () => (window.location.href = '/'),
getTokens: () => ({
accessToken: null,
idToken: null,
refreshToken: null,
refreshTokenExpirationTime: null,
accessTokenExpirationTime: null,
}),
renewToken: () => {},
};

const { AUTH_TYPE, IDENTITY_PROVIDER, PORTAIL_URL } = environment;
const { authUrl, realm, client_id } = oidcConf;

function getCurrentSurvey(path) {
const temp = path.split('/questionnaire/');
if (temp.length > 1) {
const idQ = temp[1].slice(0, temp[1].indexOf('/'));
return idQ.substr(0, idQ.indexOf('2')).toLowerCase();
}
return '';
}

export const getLogoutUrl = () =>
`${PORTAIL_URL}/${getCurrentSurvey(window.location.href)}`;

const isReadOnlyMode = window.location.pathname.startsWith(`/${READ_ONLY}`);

const getExtraQueryParams = () => {
if (isReadOnlyMode) return { kc_idp_hint: IDENTITY_PROVIDER };
return {};
};

export const AuthContext = React.createContext();

export function AuthProvider({ children }) {
const [oidcClient, setOidcClient] = useState(() => {
switch (AUTH_TYPE) {
case OIDC:
return null;
case NONE:
return dummyOidcClient;
default:
throw new Error(errorDictionary.noAuthFile);
}
});

useEffect(() => {
if (AUTH_TYPE !== OIDC) {
return;
}

(async () => {
const oidcClient = await createOidcClient({
url: oidcConf.authUrl,
realm: oidcConf.realm,
clientId: oidcConf.client_id,
identityProvider: IDENTITY_PROVIDER,
urlPortail: PORTAIL_URL,
evtUserActivity: listenActivity,
});

setOidcClient(oidcClient);
})();
}, []);

if (oidcClient === null) return <LoaderSimple />;
if (AUTH_TYPE === OIDC) {
const { OidcProvider } = createOidcProvider({
issuerUri: `${authUrl}/realms/${realm}`,
clientId: client_id,
getExtraQueryParams: getExtraQueryParams,
// See above for other parameters
});
return <OidcProvider fallback={<LoaderSimple />}>{children}</OidcProvider>;
}

return (
<AuthContext.Provider value={oidcClient}>{children}</AuthContext.Provider>
<AuthContext.Provider value={dummyOidcClient}>
{children}
</AuthContext.Provider>
);
}
7 changes: 2 additions & 5 deletions src/components/navigation/burgerMenu/burgerMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ import Close from '@material-ui/icons/Close';
import ExitToApp from '@material-ui/icons/ExitToApp';
import Help from '@material-ui/icons/Help';
import MenuIcon from '@material-ui/icons/Menu';
import { useContext, useEffect, useState } from 'react';
import { useEffect, useState } from 'react';
import { burgerDictionary } from '../../../i18n';
import { HOUSEHOLD } from '../../../utils/constants';
import { SIMPLE_CLICK_EVENT, paradataHandler } from '../../../utils/events';
import { AuthContext } from '../../auth/provider/component';
import { AppVersion } from '../../designSystem/AppVersion';
import { AssistanceConfirm } from '../../modals/assistance';
import './burgerMenu.css';
Expand All @@ -22,8 +21,6 @@ const BurgerMenu = ({ metadata, currentPage, logoutAndClose }) => {
const [assistance, setAssistance] = useState(false);
const { inseeContext } = metadata;

const { isUserLoggedIn } = useContext(AuthContext);

useEffect(() => {
window.addEventListener('scroll', closeMenu);
return () => {
Expand Down Expand Up @@ -53,7 +50,7 @@ const BurgerMenu = ({ metadata, currentPage, logoutAndClose }) => {
&nbsp;
<span className='slideBarButtonText'>{burgerDictionary.help}</span>
</IconButton>
{isUserLoggedIn && inseeContext === HOUSEHOLD && (
{inseeContext === HOUSEHOLD && (
<IconButton
className='burgerMenuButton'
aria-label='Déconnexion'
Expand Down
15 changes: 10 additions & 5 deletions src/components/orchestrator/manager/orchestratorManager.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import Box from '@material-ui/core/Box';
import Typography from '@material-ui/core/Typography';
import { makeStyles } from '@material-ui/core/styles';
import { useContext, useEffect, useState } from 'react';
import { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
import { useAuth, useUser } from 'utils/hooks/useAuth';
import {
ORCHESTRATOR_COLLECT,
ORCHESTRATOR_READONLY,
Expand All @@ -19,7 +20,7 @@ import {
useConstCallback,
useGetReferentiel,
} from '../../../utils/hooks';
import { AuthContext } from '../../auth/provider/component';
import { getLogoutUrl } from '../../auth/provider/component';
import { LoaderSimple } from '../../shared/loader';
import { Orchestrator } from '../collector';

Expand Down Expand Up @@ -52,7 +53,11 @@ const OrchestratorManager = () => {

const { putData, putStateData, postParadata } = useAPI(idSU, idQ);

const { logout, getUser, isUserLoggedIn } = useContext(AuthContext);
const {
oidc: { logout, isUserLoggedIn },
} = useAuth();

const { user } = useUser();

const { getReferentiel } = useGetReferentiel();

Expand All @@ -74,12 +79,12 @@ const OrchestratorManager = () => {
});

const logoutAndClose = useConstCallback(() => {
logout('portail');
logout({ redirectTo: 'specific url', url: getLogoutUrl() });
});

useEffect(() => {
if (isUserLoggedIn && questionnaire) {
LOGGER.addMetadata({ idSession: getUser().sub });
LOGGER.addMetadata({ idSession: user.sub });
LOGGER.log(INIT_SESSION_EVENT);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
Expand Down
1 change: 0 additions & 1 deletion src/utils/auth/index.js

This file was deleted.

Loading

0 comments on commit a6d8b2c

Please sign in to comment.