build(deps): update dependency react-router-dom to v6 (#2565)

* build(deps): update dependency react-router-dom to v6

* fix(web): update code to conform to react-router 6 convention

* refactor(web): react-router -> react-router-dom

* refactor(web): make 2fa consts relative

* refactor(web): rename 2fa consts for clarity

Co-authored-by: Renovate Bot <bot@renovateapp.com>
Co-authored-by: Amir Zarrinkafsh <nightah@me.com>
pull/2529/head^2
renovate[bot] 2021-11-05 13:36:52 +11:00 committed by GitHub
parent c8cddf4f16
commit eae353e315
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 527 additions and 609 deletions

View File

@ -19,8 +19,7 @@
"react-ga": "3.3.0",
"react-loading": "2.0.3",
"react-otp-input": "2.4.0",
"react-router": "5.2.1",
"react-router-dom": "5.3.0",
"react-router-dom": "6.0.0",
"u2f-api": "1.2.1"
},
"scripts": {
@ -127,7 +126,6 @@
"@types/qrcode.react": "1.0.2",
"@types/react": "17.0.34",
"@types/react-dom": "17.0.11",
"@types/react-router-dom": "5.3.2",
"@typescript-eslint/eslint-plugin": "5.3.0",
"@typescript-eslint/parser": "5.3.0",
"@vitejs/plugin-react": "1.0.7",

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,7 @@ import React, { useState, useEffect } from "react";
import { config as faConfig } from "@fortawesome/fontawesome-svg-core";
import { CssBaseline, ThemeProvider } from "@material-ui/core";
import { BrowserRouter as Router, Route, Switch, Redirect } from "react-router-dom";
import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
import NotificationBar from "@components/NotificationBar";
import {
@ -64,32 +64,18 @@ const App: React.FC = () => {
<NotificationsContext.Provider value={{ notification, setNotification }}>
<Router basename={getBasePath()}>
<NotificationBar onClose={() => setNotification(null)} />
<Switch>
<Route path={ResetPasswordStep1Route} exact>
<ResetPasswordStep1 />
</Route>
<Route path={ResetPasswordStep2Route} exact>
<ResetPasswordStep2 />
</Route>
<Route path={RegisterSecurityKeyRoute} exact>
<RegisterSecurityKey />
</Route>
<Route path={RegisterOneTimePasswordRoute} exact>
<RegisterOneTimePassword />
</Route>
<Route path={LogoutRoute} exact>
<SignOut />
</Route>
<Route path={ConsentRoute} exact>
<ConsentView />
</Route>
<Route path={FirstFactorRoute}>
<LoginPortal rememberMe={getRememberMe()} resetPassword={getResetPassword()} />
</Route>
<Route path="/">
<Redirect to={FirstFactorRoute} />
</Route>
</Switch>
<Routes>
<Route path={ResetPasswordStep1Route} element={<ResetPasswordStep1 />} />
<Route path={ResetPasswordStep2Route} element={<ResetPasswordStep2 />} />
<Route path={RegisterSecurityKeyRoute} element={<RegisterSecurityKey />} />
<Route path={RegisterOneTimePasswordRoute} element={<RegisterOneTimePassword />} />
<Route path={LogoutRoute} element={<SignOut />} />
<Route path={ConsentRoute} element={<ConsentView />} />
<Route
path={`${FirstFactorRoute}*`}
element={<LoginPortal rememberMe={getRememberMe()} resetPassword={getResetPassword()} />}
/>
</Routes>
</Router>
</NotificationsContext.Provider>
</ThemeProvider>

View File

@ -2,10 +2,10 @@ export const FirstFactorRoute: string = "/";
export const AuthenticatedRoute: string = "/authenticated";
export const ConsentRoute: string = "/consent";
export const SecondFactorRoute: string = "/2fa";
export const SecondFactorU2FRoute: string = "/2fa/security-key";
export const SecondFactorTOTPRoute: string = "/2fa/one-time-password";
export const SecondFactorPushRoute: string = "/2fa/push-notification";
export const SecondFactorRoute: string = "/2fa/";
export const SecondFactorU2FSubRoute: string = "security-key";
export const SecondFactorTOTPSubRoute: string = "one-time-password";
export const SecondFactorPushSubRoute: string = "push-notification";
export const ResetPasswordStep1Route: string = "/reset-password/step1";
export const ResetPasswordStep2Route: string = "/reset-password/step2";

View File

@ -1,5 +1,5 @@
import queryString from "query-string";
import { useLocation } from "react-router";
import { useLocation } from "react-router-dom";
export function useRedirectionURL() {
const location = useLocation();

View File

@ -1,5 +1,5 @@
import queryString from "query-string";
import { useLocation } from "react-router";
import { useLocation } from "react-router-dom";
export function useRequestMethod() {
const location = useLocation();

View File

@ -6,7 +6,7 @@ import { makeStyles, Typography, Button, IconButton, Link, CircularProgress, Tex
import { red } from "@material-ui/core/colors";
import classnames from "classnames";
import QRCode from "qrcode.react";
import { useHistory, useLocation } from "react-router";
import { useLocation, useNavigate } from "react-router-dom";
import AppStoreBadges from "@components/AppStoreBadges";
import { GoogleAuthenticator } from "@constants/constants";
@ -18,7 +18,7 @@ import { extractIdentityToken } from "@utils/IdentityToken";
const RegisterOneTimePassword = function () {
const style = useStyles();
const history = useHistory();
const navigate = useNavigate();
const location = useLocation();
// The secret retrieved from the API is all is ok.
const [secretURL, setSecretURL] = useState("empty");
@ -32,7 +32,7 @@ const RegisterOneTimePassword = function () {
const processToken = extractIdentityToken(location.search);
const handleDoneClick = () => {
history.push(FirstFactorRoute);
navigate(FirstFactorRoute);
};
const completeRegistrationProcess = useCallback(async () => {

View File

@ -1,7 +1,7 @@
import React, { useState, useEffect, useCallback } from "react";
import { makeStyles, Typography, Button } from "@material-ui/core";
import { useHistory, useLocation } from "react-router";
import { useLocation, useNavigate } from "react-router-dom";
import u2fApi from "u2f-api";
import FingerTouchIcon from "@components/FingerTouchIcon";
@ -13,7 +13,7 @@ import { extractIdentityToken } from "@utils/IdentityToken";
const RegisterSecurityKey = function () {
const style = useStyles();
const history = useHistory();
const navigate = useNavigate();
const location = useLocation();
const { createErrorNotification } = useNotifications();
const [, setRegistrationInProgress] = useState(false);
@ -21,7 +21,7 @@ const RegisterSecurityKey = function () {
const processToken = extractIdentityToken(location.search);
const handleBackClick = () => {
history.push(FirstFactorPath);
navigate(FirstFactorPath);
};
const registerStep1 = useCallback(async () => {
@ -43,14 +43,14 @@ const RegisterSecurityKey = function () {
const registerResponse = await u2fApi.register(registerRequests, [], 60);
await completeU2FRegistrationProcessStep2(registerResponse);
setRegistrationInProgress(false);
history.push(FirstFactorPath);
navigate(FirstFactorPath);
} catch (err) {
console.error(err);
createErrorNotification(
"Failed to register your security key. The identity verification process might have timed out.",
);
}
}, [processToken, createErrorNotification, history]);
}, [processToken, createErrorNotification, navigate]);
useEffect(() => {
registerStep1();

View File

@ -1,7 +1,7 @@
import React from "react";
import { Grid, makeStyles, Button } from "@material-ui/core";
import { useHistory } from "react-router";
import { useNavigate } from "react-router-dom";
import { LogoutRoute as SignOutRoute } from "@constants/Routes";
import LoginLayout from "@layouts/LoginLayout";
@ -13,10 +13,10 @@ export interface Props {
const AuthenticatedView = function (props: Props) {
const style = useStyles();
const history = useHistory();
const navigate = useNavigate();
const handleLogoutClick = () => {
history.push(SignOutRoute);
navigate(SignOutRoute);
};
return (

View File

@ -2,8 +2,9 @@ import React, { useEffect, Fragment, ReactNode } from "react";
import { Button, Grid, List, ListItem, ListItemIcon, ListItemText, Tooltip, makeStyles } from "@material-ui/core";
import { AccountBox, CheckBox, Contacts, Drafts, Group } from "@material-ui/icons";
import { useHistory } from "react-router-dom";
import { useNavigate } from "react-router-dom";
import { FirstFactorRoute } from "@constants/Routes";
import { useRequestedScopes } from "@hooks/Consent";
import { useNotifications } from "@hooks/NotificationsContext";
import { useRedirector } from "@hooks/Redirector";
@ -30,17 +31,17 @@ function showListItemAvatar(id: string) {
const ConsentView = function (props: Props) {
const classes = useStyles();
const history = useHistory();
const navigate = useNavigate();
const redirect = useRedirector();
const { createErrorNotification, resetNotification } = useNotifications();
const [resp, fetch, , err] = useRequestedScopes();
useEffect(() => {
if (err) {
history.replace("/");
navigate(FirstFactorRoute);
console.error(`Unable to display consent screen: ${err.message}`);
}
}, [history, resetNotification, createErrorNotification, err]);
}, [navigate, resetNotification, createErrorNotification, err]);
useEffect(() => {
fetch();

View File

@ -2,7 +2,7 @@ import React, { MutableRefObject, useEffect, useRef, useState } from "react";
import { makeStyles, Grid, Button, FormControlLabel, Checkbox, Link } from "@material-ui/core";
import classnames from "classnames";
import { useHistory } from "react-router";
import { useNavigate } from "react-router-dom";
import FixedTextField from "@components/FixedTextField";
import { ResetPasswordStep1Route } from "@constants/Routes";
@ -24,7 +24,7 @@ export interface Props {
const FirstFactorForm = function (props: Props) {
const style = useStyles();
const history = useHistory();
const navigate = useNavigate();
const redirectionURL = useRedirectionURL();
const requestMethod = useRequestMethod();
@ -74,7 +74,7 @@ const FirstFactorForm = function (props: Props) {
};
const handleResetPasswordClick = () => {
history.push(ResetPasswordStep1Route);
navigate(ResetPasswordStep1Route);
};
return (

View File

@ -1,14 +1,14 @@
import React, { Fragment, ReactNode, useCallback, useEffect, useState } from "react";
import { Redirect, Route, Switch, useHistory, useLocation } from "react-router";
import { Route, Routes, useLocation, useNavigate } from "react-router-dom";
import {
AuthenticatedRoute,
FirstFactorRoute,
SecondFactorPushRoute,
SecondFactorPushSubRoute,
SecondFactorRoute,
SecondFactorTOTPRoute,
SecondFactorU2FRoute,
SecondFactorTOTPSubRoute,
SecondFactorU2FSubRoute,
} from "@constants/Routes";
import { useConfiguration } from "@hooks/Configuration";
import { useNotifications } from "@hooks/NotificationsContext";
@ -34,7 +34,7 @@ const RedirectionErrorMessage =
"Redirection was determined to be unsafe and aborted. Ensure the redirection URL is correct.";
const LoginPortal = function (props: Props) {
const history = useHistory();
const navigate = useNavigate();
const location = useLocation();
const redirectionURL = useRedirectionURL();
const requestMethod = useRequestMethod();
@ -46,7 +46,7 @@ const LoginPortal = function (props: Props) {
const [userInfo, fetchUserInfo, , fetchUserInfoError] = userUserInfo();
const [configuration, fetchConfiguration, , fetchConfigurationError] = useConfiguration();
const redirect = useCallback((url: string) => history.push(url), [history]);
const redirect = useCallback((url: string) => navigate(url), [navigate]);
// Fetch the state when portal is mounted.
useEffect(() => {
@ -128,11 +128,11 @@ const LoginPortal = function (props: Props) {
redirect(AuthenticatedRoute);
} else {
if (userInfo.method === SecondFactorMethod.U2F) {
redirect(`${SecondFactorU2FRoute}${redirectionSuffix}`);
redirect(`${SecondFactorRoute}${SecondFactorU2FSubRoute}${redirectionSuffix}`);
} else if (userInfo.method === SecondFactorMethod.MobilePush) {
redirect(`${SecondFactorPushRoute}${redirectionSuffix}`);
redirect(`${SecondFactorRoute}${SecondFactorPushSubRoute}${redirectionSuffix}`);
} else {
redirect(`${SecondFactorTOTPRoute}${redirectionSuffix}`);
redirect(`${SecondFactorRoute}${SecondFactorTOTPSubRoute}${redirectionSuffix}`);
}
}
}
@ -165,38 +165,41 @@ const LoginPortal = function (props: Props) {
location.pathname === FirstFactorRoute;
return (
<Switch>
<Route path={FirstFactorRoute} exact>
<ComponentOrLoading ready={firstFactorReady}>
<FirstFactorForm
disabled={firstFactorDisabled}
rememberMe={props.rememberMe}
resetPassword={props.resetPassword}
onAuthenticationStart={() => setFirstFactorDisabled(true)}
onAuthenticationFailure={() => setFirstFactorDisabled(false)}
onAuthenticationSuccess={handleAuthSuccess}
/>
</ComponentOrLoading>
</Route>
<Route path={SecondFactorRoute}>
{state && userInfo && configuration ? (
<SecondFactorForm
authenticationLevel={state.authentication_level}
userInfo={userInfo}
configuration={configuration}
onMethodChanged={() => fetchUserInfo()}
onAuthenticationSuccess={handleAuthSuccess}
/>
) : null}
</Route>
<Route path={AuthenticatedRoute} exact>
{userInfo ? <AuthenticatedView name={userInfo.display_name} /> : null}
</Route>
{/* By default we route to first factor page */}
<Route path="/">
<Redirect to={FirstFactorRoute} />
</Route>
</Switch>
<Routes>
<Route
path={FirstFactorRoute}
element={
<ComponentOrLoading ready={firstFactorReady}>
<FirstFactorForm
disabled={firstFactorDisabled}
rememberMe={props.rememberMe}
resetPassword={props.resetPassword}
onAuthenticationStart={() => setFirstFactorDisabled(true)}
onAuthenticationFailure={() => setFirstFactorDisabled(false)}
onAuthenticationSuccess={handleAuthSuccess}
/>
</ComponentOrLoading>
}
/>
<Route
path={`${SecondFactorRoute}*`}
element={
state && userInfo && configuration ? (
<SecondFactorForm
authenticationLevel={state.authentication_level}
userInfo={userInfo}
configuration={configuration}
onMethodChanged={() => fetchUserInfo()}
onAuthenticationSuccess={handleAuthSuccess}
/>
) : null
}
/>
<Route
path={AuthenticatedRoute}
element={userInfo ? <AuthenticatedView name={userInfo.display_name} /> : null}
/>
</Routes>
);
};

View File

@ -1,15 +1,14 @@
import React, { useState, useEffect } from "react";
import { Grid, makeStyles, Button } from "@material-ui/core";
import { useHistory, Switch, Route, Redirect } from "react-router";
import { Route, Routes, useNavigate } from "react-router-dom";
import u2fApi from "u2f-api";
import {
LogoutRoute as SignOutRoute,
SecondFactorTOTPRoute,
SecondFactorPushRoute,
SecondFactorU2FRoute,
SecondFactorRoute,
SecondFactorPushSubRoute,
SecondFactorTOTPSubRoute,
SecondFactorU2FSubRoute,
} from "@constants/Routes";
import { useNotifications } from "@hooks/NotificationsContext";
import LoginLayout from "@layouts/LoginLayout";
@ -38,7 +37,7 @@ export interface Props {
const SecondFactorForm = function (props: Props) {
const style = useStyles();
const history = useHistory();
const navigate = useNavigate();
const [methodSelectionOpen, setMethodSelectionOpen] = useState(false);
const { createInfoNotification, createErrorNotification } = useNotifications();
const [registrationInProgress, setRegistrationInProgress] = useState(false);
@ -85,7 +84,7 @@ const SecondFactorForm = function (props: Props) {
};
const handleLogoutClick = () => {
history.push(SignOutRoute);
navigate(SignOutRoute);
};
return (
@ -108,42 +107,48 @@ const SecondFactorForm = function (props: Props) {
</Button>
</Grid>
<Grid item xs={12} className={style.methodContainer}>
<Switch>
<Route path={SecondFactorTOTPRoute} exact>
<OneTimePasswordMethod
id="one-time-password-method"
authenticationLevel={props.authenticationLevel}
// Whether the user has a TOTP secret registered already
registered={props.userInfo.has_totp}
totp_period={props.configuration.totp_period}
onRegisterClick={initiateRegistration(initiateTOTPRegistrationProcess)}
onSignInError={(err) => createErrorNotification(err.message)}
onSignInSuccess={props.onAuthenticationSuccess}
/>
</Route>
<Route path={SecondFactorU2FRoute} exact>
<SecurityKeyMethod
id="security-key-method"
authenticationLevel={props.authenticationLevel}
// Whether the user has a U2F device registered already
registered={props.userInfo.has_u2f}
onRegisterClick={initiateRegistration(initiateU2FRegistrationProcess)}
onSignInError={(err) => createErrorNotification(err.message)}
onSignInSuccess={props.onAuthenticationSuccess}
/>
</Route>
<Route path={SecondFactorPushRoute} exact>
<PushNotificationMethod
id="push-notification-method"
authenticationLevel={props.authenticationLevel}
onSignInError={(err) => createErrorNotification(err.message)}
onSignInSuccess={props.onAuthenticationSuccess}
/>
</Route>
<Route path={SecondFactorRoute}>
<Redirect to={SecondFactorTOTPRoute} />
</Route>
</Switch>
<Routes>
<Route
path={SecondFactorTOTPSubRoute}
element={
<OneTimePasswordMethod
id="one-time-password-method"
authenticationLevel={props.authenticationLevel}
// Whether the user has a TOTP secret registered already
registered={props.userInfo.has_totp}
totp_period={props.configuration.totp_period}
onRegisterClick={initiateRegistration(initiateTOTPRegistrationProcess)}
onSignInError={(err) => createErrorNotification(err.message)}
onSignInSuccess={props.onAuthenticationSuccess}
/>
}
/>
<Route
path={SecondFactorU2FSubRoute}
element={
<SecurityKeyMethod
id="security-key-method"
authenticationLevel={props.authenticationLevel}
// Whether the user has a U2F device registered already
registered={props.userInfo.has_u2f}
onRegisterClick={initiateRegistration(initiateU2FRegistrationProcess)}
onSignInError={(err) => createErrorNotification(err.message)}
onSignInSuccess={props.onAuthenticationSuccess}
/>
}
/>
<Route
path={SecondFactorPushSubRoute}
element={
<PushNotificationMethod
id="push-notification-method"
authenticationLevel={props.authenticationLevel}
onSignInError={(err) => createErrorNotification(err.message)}
onSignInSuccess={props.onAuthenticationSuccess}
/>
}
/>
</Routes>
</Grid>
</Grid>
</LoginLayout>

View File

@ -1,7 +1,7 @@
import React, { useEffect, useCallback, useState } from "react";
import { Typography, makeStyles } from "@material-ui/core";
import { Redirect } from "react-router";
import { Navigate } from "react-router-dom";
import { FirstFactorRoute } from "@constants/Routes";
import { useIsMountedRef } from "@hooks/Mounted";
@ -48,7 +48,7 @@ const SignOut = function (props: Props) {
if (redirectionURL && safeRedirect) {
redirector(redirectionURL);
} else {
return <Redirect to={FirstFactorRoute} />;
return <Navigate to={FirstFactorRoute} />;
}
}

View File

@ -1,7 +1,7 @@
import React, { useState } from "react";
import { Grid, Button, makeStyles } from "@material-ui/core";
import { useHistory } from "react-router";
import { useNavigate } from "react-router-dom";
import FixedTextField from "@components/FixedTextField";
import { FirstFactorRoute } from "@constants/Routes";
@ -14,7 +14,7 @@ const ResetPasswordStep1 = function () {
const [username, setUsername] = useState("");
const [error, setError] = useState(false);
const { createInfoNotification, createErrorNotification } = useNotifications();
const history = useHistory();
const navigate = useNavigate();
const doInitiateResetPasswordProcess = async () => {
if (username === "") {
@ -35,7 +35,7 @@ const ResetPasswordStep1 = function () {
};
const handleCancelClick = () => {
history.push(FirstFactorRoute);
navigate(FirstFactorRoute);
};
return (

View File

@ -2,7 +2,7 @@ import React, { useState, useCallback, useEffect } from "react";
import { Grid, Button, makeStyles } from "@material-ui/core";
import classnames from "classnames";
import { useHistory, useLocation } from "react-router";
import { useLocation, useNavigate } from "react-router-dom";
import FixedTextField from "@components/FixedTextField";
import { FirstFactorRoute } from "@constants/Routes";
@ -20,7 +20,7 @@ const ResetPasswordStep2 = function () {
const [errorPassword1, setErrorPassword1] = useState(false);
const [errorPassword2, setErrorPassword2] = useState(false);
const { createSuccessNotification, createErrorNotification } = useNotifications();
const history = useHistory();
const navigate = useNavigate();
// Get the token from the query param to give it back to the API when requesting
// the secret for OTP.
const processToken = extractIdentityToken(location.search);
@ -69,7 +69,7 @@ const ResetPasswordStep2 = function () {
try {
await resetPassword(password1);
createSuccessNotification("Password has been reset.");
setTimeout(() => history.push(FirstFactorRoute), 1500);
setTimeout(() => navigate(FirstFactorRoute), 1500);
setFormDisabled(true);
} catch (err) {
console.error(err);
@ -83,7 +83,7 @@ const ResetPasswordStep2 = function () {
const handleResetClick = () => doResetPassword();
const handleCancelClick = () => history.push(FirstFactorRoute);
const handleCancelClick = () => navigate(FirstFactorRoute);
return (
<LoginLayout title="Enter new password" id="reset-password-step2-stage">