Using Okta-react with react-router-dom v6

Using Okta-react with react-router-dom v6

Currently, the Okta-react SecureRoute component has a compatibility issue with react-router-dom 6.

To fix this issue you will need to implement your own SecureRoute component.

The following steps will help you implement your own Okta-react SecureRoute Component.

On your project create a new component, SecureRoute, and paste the following code:

/*!
 * Copyright (c) 2017-Present, Okta, Inc. and/or its affiliates. All rights reserved.
 * The Okta software accompanied by this notice is provided pursuant to the Apache License, Version 2.0 (the "License.")
 *
 * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *
 * See the License for the specific language governing permissions and limitations under the License.
 */

import React, { useEffect } from 'react';
import { useOktaAuth } from '@okta/okta-react';
import { toRelativeUrl } from '@okta/okta-auth-js';
import { Outlet } from 'react-router-dom';
import Loading from './Loading';

export const RequiredAuth = () => {
    const { oktaAuth, authState } = useOktaAuth();

    useEffect(() => {
        if (!authState) {
            return;
        }

        if (!authState?.isAuthenticated) {
            const originalUri = toRelativeUrl(window.location.href, window.location.origin);
            oktaAuth.setOriginalUri(originalUri);
            oktaAuth.signInWithRedirect();
        }
    }, [oktaAuth, !!authState, authState?.isAuthenticated]);

    if (!authState || !authState?.isAuthenticated) {
        return (<Loading />);
    }

    return (<Outlet />);
}

After creating the SecureRoute component, create a Loading component. This will be the default page to show while Okta-react is checking if your authenticated.

/*!
 * Copyright (c) 2017-Present, Okta, Inc. and/or its affiliates. All rights reserved.
 * The Okta software accompanied by this notice is provided pursuant to the Apache License, Version 2.0 (the "License.")
 *
 * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *
 * See the License for the specific language governing permissions and limitations under the License.
 */

import React from 'react';

const Loading = () => {
  return (
    <h3 id='loading-icon'>Loading...</h3>
  );
};

export default Loading;

After creating the SecureRoute and Loading component, we are now going to create another component that holds the routes of our application. To do this add a new component Routes.

/*!
 * Copyright (c) 2017-Present, Okta, Inc. and/or its affiliates. All rights reserved.
 * The Okta software accompanied by this notice is provided pursuant to the Apache License, Version 2.0 (the "License.")
 *
 * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *
 * See the License for the specific language governing permissions and limitations under the License.
 */

import React from 'react';
import { Routes, Route } from 'react-router-dom';
import { LoginCallback } from '@okta/okta-react';
import { RequiredAuth } from './SecureRoute';
import { Protected } from "./Protected";
import { Home } from "./Home";
import Loading from './Loading';
import { useOktaAuth } from '@okta/okta-react';

const AppRoutes = () => {
    const { oktaAuth } = useOktaAuth();
    
    return (
        <Routes>
            <Route path='/callback' element={<LoginCallback loadingElement={<Loading />} />} />
            <Route path='/' element={<RequiredAuth />}>
                <Route path='' element={<Home oktaAuth={oktaAuth}/>} />
            </Route>
            <Route path='/Protected' element={<RequiredAuth />}>
                <Route path='' element={<Protected oktaAuth={oktaAuth}/>} />
            </Route>
        </Routes>
    );
};

export default AppRoutes;

You will see on the code that Home and Protected are inside a Route that has an element RequiredAuth. This means that both of the components are secured and the user needs to be authenticated to access these pages. If the user is not authenticated, Okta-react will redirect the user to the login page.

The LoginCallback is the component used by Okta-react to authenticate the user after logging in to the application.

After creating the Routes component, the next step is to update the App.js and use the Routes component that we created.

import React, { Component } from 'react';
import { useNavigate } from 'react-router-dom';
import { Security } from '@okta/okta-react';
import { OktaAuth, toRelativeUrl } from '@okta/okta-auth-js';
import Layout from './components/Layout';
import Routes from './components/Routes';
const OKTA_OIDC = {
    clientId: <Client_Ids>,
    issuer: <Issuer>,
    redirectUri: <Redirect_URI>,
    pkce: false,
    clientSecret: <Client_Secret>,
    postLogoutRedirectUri: <Post_Logout_Redirect_Uri>,
    scopes: ["openid", "role", "profile"]
};

const oktaAuth = new OktaAuth(OKTA_OIDC);

export const App = props => {
    const navigate = useNavigate();
    
    const restoreOriginalUri = (_oktaAuth, originalUri) => {
        navigate(toRelativeUrl(originalUri|| '/', originalUri));
    };
    
    return(
        <Security oktaAuth={oktaAuth} restoreOriginalUri={restoreOriginalUri} >
            <Layout>
                <Routes {...oktaAuth}/>
            </Layout>
      </Security>
    );
}

After updating the App.js your application should now work even if you have used react-router-dom version 6.