The Complete Guide to React Authentication with Auth0

Hi @dan-auth0
Great job with the guide. i am integrated auth0 in react SPA app and working fine in other tabs but getting ‘login required’ error in incognito tab
plz give any solution for reloved this error

Howdy! Welcome to the Auth0 community. Do you mean you get an error when you try to access a protected route using an Incognito Browser window?

2 Likes

Howdy! Thank you for your feedback and welcome to the Auth0 Community!

Do you happen to have a CodeSandbox or Stackblitz with a prototype that can showcase this problem, please?

1 Like

@dan-auth0 What’s the best way to be notified when this code sample is completed?

3 Likes

I’ll be putting a comment here :slight_smile:

2 Likes

Hello!

Great guide thank you!

I can’t make it work with React-router V6 though. Is there any updates about the updated code sample?

Thank you!

1 Like

Exploring and testing that this week! Stay tuned, please.

2 Likes

First @dan-auth0 great writeup you made – looking forward to the update. It was incredibly helpful jumpstarting me when I initially deployed :).

@ErnestoG – this is what I did.

Disclaimer: I’m not react expert :stuck_out_tongue:

So, I’ve just about got it working. The main item I’m still struggling on is the useEffect on the component returned on this line

 <Route path="/teleporter" element={<ProtectedRoute component={Teleporter} />} />

useEffect is firing twice for my teleporter component (component mounts, unmounts, mounts again when clicking the route link) . Can’t figure this out or why this is happening. Generally this wouldn’t be bad, but I am doing an API call to pull data so the API ends up firing twice (consumption based billing T_T).

I think I’ve narrowed it down to behavior of withAuthenticationRequired(); I don’t know why though. I’m using “@auth0/auth0-react”: “^1.9.0”, and from what I can see it has the fix for #311 (though likely not related).

I did confirm it doesn’t fire twice when using the old router and original code from the complete guide.

protected-route.js update
Need to pretty much remove the route piece

import React from "react";
import { withAuthenticationRequired } from "@auth0/auth0-react";
import { Loading } from "../components/index";

const ProtectedRoute = ({ component, ...args }) => {
    const Cp = withAuthenticationRequired( component, { onRedirecting: () => <Loading /> });
    return <Cp {...args} />
}
  
export default ProtectedRoute;

auth0-provider-with-history.js
update from useHistory to useNavigate

import React from "react";
import { useNavigate  } from "react-router-dom";
import { Auth0Provider } from "@auth0/auth0-react";

const Auth0ProviderWithHistory = ({ children }) => {
  const navigate = useNavigate();

  // personal update.  you probably want process.env
  /*
  const domain = process.env.REACT_APP_AUTH0_DOMAIN;
  const clientId = process.env.REACT_APP_AUTH0_CLIENT_ID;
  const audience = process.env.REACT_APP_AUTH0_AUDIENCE;
  */
  const domain = window._env_.REACT_APP_AUTH0_DOMAIN;
  const clientId = window._env_.REACT_APP_AUTH0_CLIENT_ID;
  const audience = window._env_.REACT_APP_AUTH0_AUDIENCE;


  const onRedirectCallback = (appState) => {
    navigate(appState?.returnTo || window.location.pathname);
  };

  return (
    <Auth0Provider
      domain={domain}
      clientId={clientId}
      redirectUri={window.location.origin}
      onRedirectCallback={onRedirectCallback}
      audience={audience}
    >
      {children}
    </Auth0Provider>
  );
};

export default Auth0ProviderWithHistory;

app.js
Update your protected routes

            <Routes>
                <Route path="/" element={<Home/>} />
                <Route path="/profile" element={<ProtectedRoute component={Profile} />} />
                <Route path="/teleporter" element={<ProtectedRoute component={Teleporter} />} />
                <Route path="/external-api" element={<ProtectedRoute component={ExternalApi} />} />
            </Routes>

navbar
activeClassName needs to be updated to a function

<li className="mb-1">

        <NavLink

        to="/profile"

       

        className={(navData) => navData.isActive ? "nav-link router-link-exact-active" : "nav-link" }

        >

        Profile&nbsp;&nbsp; {isAuthenticated ? <i className="bi bi-unlock"></i> : <i className="bi bi-lock"></i> }

        </NavLink>
3 Likes

Welcome to the Auth0 Community! Thanks for sharing the code above :pray:
Tbh, I prefer the API of React Router 5, personally. I do the majority of my production work using Next.js and I love the directory-based approach to routing.

1 Like

Thanks Dan! Hopefully you’ll have a cleaner way to go about it with your article update to avoid the 2 mounts (and maybe validate I’m not going crazy here :)).

Scratching my head on what the right way is or the root cause :). Only thing I can think of is the useEffect in with-authentication-required.tsx is now causing the two mounts due to the new v6 behavior on element={ your component}.

I’m playing around at the moment and merging some of the code from with-authentication-required.tsx directly into my protected route…

The following code for protected route results in a single mount (woo) only and handles redirect if not authed. It’s ugly code, and probably has all kinds of issues doing this approach, but seems to work at least at the surface level.

Ugly maybe not so secure code below that was my fix to my original post above’s component mounting twice [causing API to fire twice :P]

// src/auth/protected-route.js

import React from 'react';
import { useAuth0 } from '@auth0/auth0-react';
import { Loading } from "../components";


const ProtectedRoute = ({ component }) => {
    const { user, isAuthenticated, loginWithRedirect } = useAuth0();

    /**
     * Check the user object for JWT claims and return a boolean indicating
     * whether or not they are authorized to view the component.
     */
    const claimCheck = (claims) => {
        let type_checks = {
            "name": "string",
            "given_name": "string",
            "family_name": "string",
            "middle_name": "string",
            "nickname": "string",
            "preferred_username": "string",
            "profile": "string",
            "picture": "string",
            "website": "string",
            "email": "string",
            "email_verified": "boolean",
            "gender": "string",
            "birthdate": "string",
            "zoneinfo": "string",
            "locale": "string",
            "phone_number": "string",
            "phone_number_verified": "boolean",
            "address": "string",
            "updated_at": "string",
            "sub": "string"
        }


        let valid = true;
        for (const property in claims) {
            if( typeof property === "string" ) {
                if( property in type_checks ) {
                    if( typeof claims[property] !== type_checks[property] ) {
                        valid = false;
                        break;
                    }
                }
            }
            else {
                valid = false;
                break;
            }
        }
        
        return valid;
    }


    const routeIsAuthenticated = isAuthenticated && claimCheck(user);
    let redirectTo =  window.location.pathname + window.location.search;
    //console.log(redirectTo)
    // redirection code if not authed
    if( routeIsAuthenticated === false)
        loginWithRedirect( { appState: { returnTo: redirectTo } } );

    const Cp = component;
    return routeIsAuthenticated ? < Cp />: <Loading/>;
}
export default ProtectedRoute;
2 Likes

Thanks a lot for sharing this!

3 Likes

Hopefully everyone here is notified :eyes: I have a working demo with React Router 6 + React 17:

:pray: Please, I’d like for y’all to try it out and get some feedback before we go to write the guide for it. Thanks!

3 Likes

@o.o @ErnestoG @avala :eyes:

3 Likes

Thanks @dan-auth0 ! Will try to get to this when work is a hint less hectic and will report back. Appreciate the effort and sharing of this.

Some initial thoughts/comments:
*Looks like we are all typescript now so I do need to convert some items in my project to test out.
*In regards to protect route, at I glanced and nothing stood out here as new logic wise.
*From a general feedback perspective on the guide itself, as the original used non typescript, you may need to include a note or generic blurb in there just acknowledging this fact (just a nod to folks that went through your original and built something)

—Edit @dan-auth0 – I lied. Got curious and tested via a quicker route than trying on the main project I have–

I cloned out your project and modified message-services.tsx to call my API I have running:

export const getProtectedResource = async (
  accessToken: string
): Promise<ApiResponse> => {
  const config: AxiosRequestConfig = {
    url: `${apiServerUrl}teleporters`,
    method: "GET",
    headers: {
      "content-type": "application/json",
      Authorization: `Bearer ${accessToken}`,
    },
  };

And go to the protected page after login.

Check out my console from FastAPI:

INFO:     Started server process [9092]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO:     127.0.0.1:60485 - "OPTIONS /api/teleporters HTTP/1.1" 200 OK
INFO:     127.0.0.1:60485 - "OPTIONS /api/teleporters HTTP/1.1" 200 OK
INFO:     127.0.0.1:60486 - "GET /api/teleporters HTTP/1.1" 200 OK
INFO:     127.0.0.1:60485 - "GET /api/teleporters HTTP/1.1" 200 OK

Output from developer console:

react-dom.development.js:67 Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
    at ProtectedPage (http://localhost:3000/static/js/bundle.js:3006:80)
    at WithAuthenticationRequired (http://localhost:3000/static/js/bundle.js:8611:14)

This points to the issue I believe that I had identified previously on withAuthenticationRequired code being flawed/bugged with router6 causing the double mount (useEffect triggering twice – component mounts,unmounts,remounts). This type of behavior did not occur with the old router + withAuthenticationRequired.

Entirely possible my sleep deprivation is talking though and I am way off :-3.

1 Like

I am going to send it to QA today. I’ll try it with a Flask API we have that is compatible with this Hello World client apps:

I do have a FastAPI code sample in the works too but this may be the closest I can get to your environment quickly.

2 Likes

Ooph, this is great news but at the worst possible moment. We’re migrating all of our systems and Office tenants for the next 2 weeks. I’ll have some availability again towards the end of February.

2 Likes

That’s totally fine! :pray: Good luck with your migration! :four_leaf_clover:

1 Like

Hi, I modified the project I was being working a few weeks ago (which I couldn’t make it work when trying to protect routes) and it works now just by using the new ProtectedRoute component.

thank you!

1 Like

Awesome! Please let me know if you run into any other issues or have further questions. :muscle:

1 Like

This was awesome! I was struggling with the Protected Routes component and this helped me with the update!