This is one of those posts I debated writing about because by its nature, relaying how I’m implementing authorization on my site (not this one, this is WordPress’ fault) is inherently insecure. But then again, this isn’t Fort Knox[1] we’re talking about here.

There are two things that I need to apply authorization to: page access and operational access.
Page access means what it says: does a user have access to a specific page. There seems to be many ways to approach this depending on how deep one wants to bury their code. For me, I opted to go with modifying the route based on the user’s access rights via React Router.
React doesn’t work with the concept of “pages”, although to the user the result is the same. Instead, React loads components based on requests made to the app. In order to discern what a browser is asking for — since all of a React app is basically a pile of interconnected components — it uses a definition of which request routes map to which components.
const router = createBrowserRouter([
{
path: "/",
element: <Home />,
},
{
path: "account",
element: <Outlet />,
children: [
{
path: "login",
element: <Login />
}
]
},
{
path: "controls",
element: <Outlet />,
children: [
{
path: "examples",
element: <ControlExamples />
}
]
}
]);
The bad news is that this is an annoying step that we never had to do in the Dark Ages of The Web; we just threw pages into directories and let the web server sort them out when we called for a file by name. The good news is that if we don’t represent a path in this routing structure, a user can’t access it because the server doesn’t know what to serve. React Router gives us some ways in which we can check a user’s status before we define the component served, but because of my user identification model, that wasn’t working out for me, so I had to rely on a new-to-me feature called loader
:
{
path: "members",
element: <Outlet />,
children: [{
path: "dashboard",
element: <MembersDashboard />,
loader: async() => {
return AuthorizeRoute('MDASH');
}
}]
}
The loader
section allows for us to perform some data loading functionality ahead of serving the component defined by the route. That data is then available as soon as we get to the component itself.
import { useLoaderData, Navigate } from "react-router-dom";
const MembersDashboard = () => {
const _auth = useLoaderData();
if (!_auth){
return <Navigate to="/account/login" />
}
return (
<div>Membership Dashboard</div>
)
}
export { MembersDashboard }
One of the more common ways to load data on load of a component is to use a hook called useEffect
, but React Router’s loading
feature provides us with another way that isn’t tied so much to the component itself, but rather the request for the component. It’s not a perfect implementation, but my authorization structure is a bit more complex than just “logged in or not”.
That brings me to the second thing I need to apply authorization to: operations.
A user might have access to a “user management” component, but do they have access to add, modify or — gawd help us — deactivate or delete a user? It’s not uncommon to allow users to view but not touch data, and I want to allow for that level of distinction.
My original plan was to have a tiered system which assigned a “role” to all users, and each role would have a “weight” to it. Guests would have a role weight of 0, members a role weight of something like 600, and site admins would have a role weight of 800. The higher the weight, the more access a user would have. Every element that required authorization would have its own weight, so if the weight of the user’s role exceeded the weight needed by the specific component or operation, they could access that resource. If not, they would get punted back to the login if we’re talking about a component, would see a disabled control if we’re talking about a button or textbox, or might not see the component at all if we’re talking about an entire section of a page. I thought this would allow for an easy way of “authorization inheritance” by simply kicking a user up the weight chain, but then I ran into issues whereby some site features might only be available to an org admin, but the site admin — who might not have the same responsibilities in the organizational structure, but needs access to the site to troubleshoot — would need to see what the org admins do from time to time.
I got it in my head to try a system like the one used by Discord. That came with issues. First, I had actually gotten my original weighted role system working, so I had to rip it all out from the data tables to the stored procedures to the server and website code. The second was the fact that I couldn’t keep the parts of the new system straight in my own head. I needed roles still, but those roles now had to have actions assigned to them, and those actions needed switches such as “yes”, “no”, or maybe “inherit” from some kind of parent. That meant I needed a master table to define those actions. Some of those actions needed to take the place of my routing authorization system, and some would be used to allow or deny access to component content.

I eventually figured out a way to streamline the structure by not allowing really wacky actions; I don’t need the levels that differentiate between “can view pictures of explosions” and “can view any picture”.
I have yet to go back and implement this new system for authorization, because I need to start with the creation of the management UI. So far I have been doing everything at the database-API-and-Postman level, doing lists, adds, and updates via the endpoints, so I need to start actually writing website content to manage all of this mess.
[1] We keep using “Fort Knox” to mean “the pinnacle of security”, but is Fort Knox really still the most secure example we have? I mean, I don’t know if it is or not. What I do know is that it’s still more secure than any online site these days, even if their main entrance was a screen door. ZING!