I'm attempting to setup RBAC on the client-side using Auth0 ID token and a custom claim, but my app continues to return a type error.
Unhandled Rejection (TypeError): Cannot read property 'http://localhost:3000/roles' of undefined
▼ 2 stack frames were expanded.
getRoles
C:/Users/Logan/Documents/Development Projects/auth0 testing/testing/src/AuthorizeUser.js:14
async checkRoles
C:/Users/Logan/Documents/Development Projects/auth0 testing/testing/src/AuthorizeUser.js:17
It does not matter what route I try to navigate to for this to be thrown (protected or not) but I can make a change in the AuthorizeUser file, refresh the browser, and it will return to normal until the next time I refresh/click back. What is causing the useEffect to be undefined before it can even fire when not visiting a protected route? I set the useEffect up on a different component unrelated to the ProtectedRoute component and it worked fine - fired on page load, pulled the data I need and I was able to JSON.stringify it in a return statement to display it - without throwing any undefined errors.
I opened a ticket with Auth0 and received a helpful email that had a set of snippets that I based my code around (they were built with TypeScript which I'm not using in this project) so I restructured as necessary and as mentioned previously, it works fine when unrelated to the protected route component but once I placed it into this flow it continuously returns undefined.
Additionally, I tried to remove the return statement from the ProtectedRoute component, but that throws a "Nothing was returned from render." error even though I have the return statement in the AuthorizeUser component. What is causing this behavior or is this related to the undefined issue?
Index.js
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import { Auth0Provider } from "@auth0/auth0-react";
ReactDOM.render(
<Auth0Provider
domain={}
clientId={}
redirectUri={window.location.origin}
>
<App />
</Auth0Provider>,
document.getElementById("root")
);
App.js
import "./App.css";
import { BrowserRouter, Route } from "react-router-dom";
import Home from "./Home";
import User from "./User";
import Admin from "./Admin";
import NotAuthorized from "./NotAuthorized";
import { ProtectedRoute } from "./ProtectedRoute";
function App() {
return (
<BrowserRouter>
<Route path="/" exact component={Home} />
<ProtectedRoute path="/admin" role="Admin" component={Admin} />
<ProtectedRoute path="/user" role="User" component={User} />
<Route path="/not-authorized" component={NotAuthorized} />
</BrowserRouter>
);
}
export default App;
ProtectedRoute Component
import React from "react";
import { Route } from "react-router-dom";
import { withAuthenticationRequired } from "@auth0/auth0-react";
import { AuthorizeUser } from "./AuthorizeUser";
import Loader from "./Loader";
export const ProtectedRoute = ({ component, role, ...args }) => {
return role ? (
<Route
component={withAuthenticationRequired(
AuthorizeUser({ component, role }),
{
onRedirecting: () => <Loader />,
}
)}
{...args}
/>
) : (
<Route
component={withAuthenticationRequired(component, {
onRedirecting: () => <Loader />,
})}
{...args}
/>
);
};
AuthorizeUser component
import React, { useEffect, useState } from "react";
import { useAuth0 } from "@auth0/auth0-react";
import { Redirect } from "react-router";
const roleClaimType = "http://localhost:3000/roles";
export const AuthorizeUser = ({ component: Component, role }) => {
const [isAuthorized, setIsAuthorized] = useState(false);
const { getIdTokenClaims } = useAuth0();
useEffect(() => {
async function getRoles() {
const claims = await getIdTokenClaims();
console.log(claims);
return claims[roleClaimType] || [];
}
async function checkRoles() {
const roles = await getRoles();
console.log(roles);
if (roles.includes(role)) {
setIsAuthorized(true);
}
}
checkRoles(role);
}, [getIdTokenClaims]);
return isAuthorized ? <Component /> : <Redirect to='/not-authorized'/>;
}