Skip to main content

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:

  1. Protect part of a web page, like hide/show a button that is just for users with a certain role.
  2. 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.

Prerequisites
  1. 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"
}

Decoding and verifying JWTs

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

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 ("");
}

How to get the access token?

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>

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.

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>

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