Error: Invalid state when calling handleRedirectCallback on React App

Platform: “react”: “^18.2.0”

Routing Library: “react-router-dom”: “6.4.2”

Auth0 library: “@auth0/auth0-spa-js”: “^2.0.0”

Issue: getting “Error: Invalid state” when calling handleRedirectCallback. Not sure what’s going on on the background but the login completes successfully after the error.

Setup:

Auth.js

const auth0 = new Auth0Client({
	authorizationParams: {
		audience: <my-audience>,
		redirect_uri: <my-url>/callback,
	},
	cacheLocation: 'localstorage',
	clientId: <my-clientId>,
	domain: <my-domain>,
	useRefreshTokens: true,
});

export const login = async () => {
	await auth0.loginWithRedirect();
};

export const isAuthenticated = () => {
      return auth0.isAuthenticated();
};

export const handleRedirectCallback = () => {
	return auth0.handleRedirectCallback();
};

export const login = async () => {
	await auth0.loginWithRedirect();
};

App.js

<Routes>
	<Route path="/login" element={<Login />} />
	<Route path="/callback" element={<Auth0Callback />} />
	<Route path="/" element={<AuthenticatedRoute component={AppLayout} />}>
                // routes children of AuthenticatedRoute
    </Route>
</Routes>

Auth0Callback.js

export default function Auth0Callback() {
	const navigate = useNavigate();

	useEffect(() => {
		(async () => {
			try {
				await handleRedirectCallback();
				navigate(localStorage.getItem('redirectTo') || '/');
			} catch (e) {
				console.log('e', e); // ERROR IS LOGGED HERE
			}
		})();
	}, [navigate]);

	return (
		<SpinnerContainer>
			<Spinner />
		</SpinnerContainer>
	);
}

AuthenticatedRoute.js

export const AuthenticatedRoute = ({ component: Component }) => {
	const location = useLocation();
	const [authenticated, setAuthenticated] = useState(false);

	useEffect(() => {
		(async () => {
			const authenticated = await isAuthenticated();
			setAuthenticated(authenticated);

			if (!authenticated) {
				localStorage.setItem('redirectTo', location.pathname);
				login();
			}
		})();

		return;
	}, [location.pathname]);

	return authenticated ? (
		<Component />
	) : (
		<SpinnerContainer>
			<Spinner />
		</SpinnerContainer>
	);
};

export default AuthenticatedRoute;

The issue is that in React 18 the useEffect hook is called twice (thus handleRedirectCallback is called twice as well) because the component mounts, unmounts, then mounts again. I removed <React.StrictMode> to get the old behavior of useEffect and the bug is not happening anymore. This is not the final solution, but at least it shows what is causing the bug.

I’ll mark it temporarily as a solution but can I also ask you to raise it as aGitHub issue so we can discuss it directly with the SDK maintainers? Thank you!

This is my final solution. I am not sure how to raise as a github issue.

export default function Auth0Callback() {
	const navigate = useNavigate();
	const shouldRedirect = useRef(true);

	useEffect(() => {
		if (shouldRedirect.current) {
			shouldRedirect.current = false;

			(async () => {
				try {
					await handleRedirectCallback();
					navigate(localStorage.getItem('redirectTo') || '/');
				} catch (e) {
					navigate('/500');
				}
			})();
		}
	}, [navigate]);

	return (
		<SpinnerContainer>
			<Spinner />
		</SpinnerContainer>
	);
}

1 Like

Thanks for sharing it with the rest of community!

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.

Hello, I recommend using the Auth0 React SDK instead of the Auth0 SPA SDK for your React application.

You can also consult our newly released React Authentication By Example: Using React Router 6 :tada: for guidance on how to integrate Auth0 with a React Router v6 application. While the guide uses React v18, the same architecture would apply to an app that uses React v17.

1 Like