Protect your frontend
Securing your frontent when you have logged in users is crucial. Here's some examples that points out why this is important.
- You only want logged in users to access the app, otherwise they should be prompted with a login.
- You have views or components that only some users should be able to access. E.g. an analytics view for only users with the privilege
ANALYTICS_READ
.
In this guide we'll be showing you how easy it is to secure your application frontend with Nblocks. You will apply protection in two ways by using the tokens obtained from the logged in user. These ways are:
- Protect part of a web page, like hide/show a button that is just for users with a certain role.
- Protect a route, that only some users can access and others are redirected back.
After you're done with this guide your application frontend will be able to show/hide content depending on what user login.
The Nblocks team provides, maintains, and adds code examples for popular languages continuously. However, if you have a specific need not covered by this quickstart yet, you'll find documentation on how to use Nblocks with any language in our API reference API reference.
- You have completed the Quickstart and have access to the tokens obtained from the logged in user.
Short info about the access token
When you completed the [Quickstart]((/docs/getting-started/quickstart) you got hold of a couple of tokens that was signed securely by Nblocks. One of them, the access token holds the users access information and this information can be used to protect your app. The token is encoded in a format called JWT for greater portability and security. If we decode the access token it looks like this:
{
"aid": "63d2ab029e23db0afb07a5a7",
"tid": "63d2b5c18892e10022e08399",
"scope": "AUTHENTICATED USER_READ USER_WRITE TENANT_READ TENANT_WRITE",
"role": "OWNER",
"plan": "FREE",
"iat": 1685648418,
"exp": 1685652018,
"aud": [
"63d2ab029e23db0afb07a5a7",
"https://app.nblocks.cloud"
],
"iss": "https://auth.nblocks.cloud",
"sub": "63d2b5c18892e10022e083a2"
}
JWTs is a well known concept in security. That means there are plenty of libraries for many software stacks to decode and verify them into readable JSON. Here's a extensive list of different libraries. We'll be using one of these in our code examples.
In the structure above we highlighted the parts that we'll be focusing on in this guide.
- The
role
property tells the role of the user. - The
scope
property tells what privileges the user obtained when logging in as a that role. This can be used for more fine grained protection and role based access control on individual features.
Step 1. Protect part of a web page
The first part we're going to take on is protecting a part of a web page.
Imaging you have a side menu that contains links to different features and routes of your frontend app. This could look something like this.
<sidebar>
<ul class="menu">
<li>
<a href="/home">Home</a>
</li>
<li>
<a href="/analytics">Analytics</a>
</li>
<li>
<a href="/profile">Profile</a>
</li>
</ul>
</sidebar>
But we don't want to show the link to Analytics feature if the user don't have the suffient rights to access it. Therefore we need to use the access token to start render this link conditionally. This can easily be done likt this:
Example code
- ReactJS
- Can't find your stack?
Create a new component that we call ProtectedContent. Since React is built upon the idea of component trees where a parent component renders children, this will be the component that conditionally render the content depending on what access the user should have.
import { useEffect, useState } from "react";
import { decodeJwt } from "jose";
// The component takes the props roles and privileges to verify against the current user.
export default function ProtectedContent({roles, privileges, children}) {
// This will be our variable telling if the user is granted access and we should render the component children
// Initially this variable is false
const [granted, setGranted] = useState(false);
useEffect(() => {
// Retrieve the access token JWT from localstorage
const accessToken = window.localStorage.getItem('access_token');
if (accessToken) {
const decoded = decodeJwt(accessToken);
setGranted(hasRoleOrPrivilege(decoded));
}
}, []);
// Helper method to see if the users token contains any of the required roles or privileges
const hasRoleOrPrivilege = (decoded) => {
return roles ? roles.includes(decoded.role) : false ||
privileges ? privileges.some(scope => decoded.scope.includes(scope)) : false;
}
// Only if granted should we render the component children
if (granted)
return (children);
else
return ("");
}
In the User Login Quickstart, we stored the access token into Localstorage as a fast and easy way later retrieve it again. Other ways could be to save in-memory in a global state or React context.
Now you can use this component in your side menu to make the link to /analytics
only render for users with the privilege USER_READ
.
<sidebar>
<ul class="menu">
<li>
<a href="/home">Home</a>
</li>
<li>
<ProtectedContent privileges={['ANALYTICS_READ']}>
<a href="/analytics">Analytics</a>
</ProtectedContent>
</li>
<li>
<a href="/profile">Profile</a>
</li>
</ul>
</sidebar>
More examples
Our ProtectedContent can take both roles and privileges as props. Here's how that would look.
<ProtectedContent privileges={['ANALYTICS_READ', 'ANALYTICS_WRITE']}>
<p>This is just shown for users with the ANALYTICS_READ or ANALYTICS_WRITE privileges</p>
</ProtectedContent>
<ProtectedContent roles={['ADMIN']}>
<p>This is just shown for users the role ADMIN</p>
</ProtectedContent>
If you cannot find your stack in the example code you need to integrate this part manually.
Nblocks can be integrated with any stack or framework.
Essentially it's all about REST API calls or redirecting the user browser agent which is documented in more details in the API reference https://nebulr-group.github.io/nblocks-api-docs.
Use the other code examples to understand what should to be done and in what order.
We'd be happy to get feedback so we can add more code examples so don't forget to make a request, by joining our Discord https://discord.gg/kjWYdZ6f6G and spell it out.
Step 2. Protect a route
Protecting a whole route is as simple as protecting part of the web page. But instead of hiding/showing content we can redirect the user to another page if the requirements are not met.
- ReactJS
- Can't find your stack?
Imaging you have a React router structure like this.
<BrowserRouter>
<Routes>
<Route path="/auth/login" element={<LoginComponent />} />
<Route path="/auth/oauth-callback" element={<CallbackComponent />} />
<Route path="/home" element={<HomeComponent />} />
<Route path="/analytics" element={<AnalyticsComponent />} />
</Routes>
</BrowserRouter>
We want to restrict the route /analytics
for only users with suffient rights to access it, otherwise they should be redirected back to /home
. Therefore we need to use the access token to protect this route conditionally.
This can easily be done likt this:
Create a new component that we call ProtectedRoute.
import { useEffect, useState } from "react";
import { decodeJwt } from "jose";
import { Navigate } from 'react-router-dom';
export default function ProtectedRoute({roles, privileges, redirectTo, children}) {
// This will be our variable telling if the user is granted access or if we should redirect to login
// Initially this variable is true since we don't want to redirect before resolving the
const [granted, setGranted] = useState(true);
// This is the path which users without access should be redirect to
const redirectPath = redirectTo ? redirectTo : "/auth/login";
useEffect(() => {
// Retrieve the access token JWT from localstorage
const accessToken = window.localStorage.getItem('access_token');
if (accessToken) {
const decoded = decodeJwt(accessToken);
setGranted(hasRoleOrPrivilege(decoded));
} else {
setGranted(false);
}
}, []);
// Helper method to see if the users token contains any of the required roles or privileges
const hasRoleOrPrivilege = (decoded) => {
return roles ? roles.includes(decoded.role) : false || privileges ? privileges.some(scope => decoded.scope.includes(scope)) : false;
}
// Only if granted should we render the component children
if (granted)
return (children);
else
return (<Navigate to={redirectPath} replace />);
}
Now you can use this component in your app routing like this:
<BrowserRouter>
<Routes>
<Route path="/auth/login" element={<LoginComponent />} />
<Route path="/auth/oauth-callback" element={<CallbackComponent />} />
<Route path="*" element={
<ProtectedRoute privileges={["AUTHENTICATED"]}>
<Routes>
<Route path="/home" element={<HomeComponent />} />
<Route path="/analytics" element={<AnalyticsComponent />} />
</Routes>
</ProtectedRoute>
}
/>
</Routes>
</BrowserRouter>
If you now try to navigate to either /home
or /analytics
, you will be redirected to login.
More examples
Our ProtectedRoute can do more like redirecting to a forbidden page if the user is logged in but are missing a required privilege. Here's how that would look:
<BrowserRouter>
<Routes>
<Route path="/auth/login" element={<LoginComponent />} />
<Route path="/auth/oauth-callback" element={<CallbackComponent />} />
<Route path="/forbidden" element={<ForbiddenComponent />} />
<Route path="*" element={
<ProtectedRoute privileges={["AUTHENTICATED"]}>
<Routes>
<Route path="/home" element={<HomeComponent />} />
<Route path="/analytics" element={
<ProtectedRoute
privileges={["ANALYTICS"]}
redirectTo="/forbidden"
>
<AnalyticsComponent />
</ProtectedRoute>
}
/>
</Routes>
</ProtectedRoute>
}
/>
</Routes>
</BrowserRouter>
If you cannot find your stack in the example code you need to integrate this part manually.
Nblocks can be integrated with any stack or framework.
Essentially it's all about REST API calls or redirecting the user browser agent which is documented in more details in the API reference https://nebulr-group.github.io/nblocks-api-docs.
Use the other code examples to understand what should to be done and in what order.
We'd be happy to get feedback so we can add more code examples so don't forget to make a request, by joining our Discord https://discord.gg/kjWYdZ6f6G and spell it out.
That's all! With this guide we've show the best practises how you can protect your frontend application using the tokens generated from Nblocks Login
Next steps
- Protect your backend with user tokens.
- Add Nblocks hosted user management to your app.
- Add SSO alternatives to your login experience.
If you haven't already, join our Discord