Back to listing

Building a React and Node app with Aserto authorization

Omri Gazitt avatar

Omri Gazitt

CEO, Aserto

Roie Schwaber-Cohen avatar

Roie Schwaber-Cohen

Developer Advocate

Apr 12th, 2023

Integration  |  


react node and aserto


Aserto is a cloud-native authorization platform that saves you from having to build your own access control solution, freeing you up to focus on your core user experience. In this tutorial you will learn how to integrate the Aserto SDK in the context of a Node.js service (using Express.js) that will interact with a React application.

Before we get started, let’s review two major components of Aserto: the Authorizer and the Control Plane.

The Authorizer is where authorization decisions are made. It is an open-source authorization engine which uses Open Policy Agent (OPA) to compute decisions based on policy, user context, and resource data. In this tutorial we’re going to use the hosted version of the authorizer.

The Control Plane manages the lifecycle of policies, user context, and resource data that are used by the authorizer. The control plane makes it easy to manage these artifacts centrally, and takes care of the details of synchronizing them to the Authorizer instance(s) deployed at the edge of your application. More specifically, it manages:

  • Connections to external systems, such as identity providers and source control systems
  • References to registered authorization policies
  • A user directory built from the identity providers it is connected to
  • A centralized log of aggregated decisions made by the Authorizer

The policy

At the core of Aserto’s authorization model is an authorization policy, which we refer to simply as a Policy. Policies are authored in a textual language called Rego, defined as part of the Open Policy Agent (OPA) project in the Cloud Native Computing Foundation.

We define the access control rules we want to enforce in our policy - as opposed to our application code. This is what's known as the "Policy-as-Code" approach, where authorization logic is decoupled from application logic.

Policies are treated just like application code or infrastructure-as-code - they are stored and versioned in a git repository. We’ll define and see the policy in action later in this tutorial.

What to expect

This post assumes you have a working knowledge of Javascript and that you are familiar with React.js and Node.js.

When you’ve completed this tutorial you'll have learned how to:

  1. Create a React application with authentication using oidc-react
  2. Set up a simple Express.js application with authentication middleware and define a protected route
  3. Create and modify a very simple authorization policy
  4. Integrate the Aserto Authorization Express.js SDK to enable fine grained access control
  5. Conditionally render UI elements based on user access

It should take about 30-45 minutes to complete this tutorial.


To get started, you’re going to need:

  1. Node.JS installed on your machine
  2. Aserto account and credentials (if you don't have one, sign up here!)
  3. Your favorite code editor

To begin, let's add users to your Aserto directory. We'll need these users to test our application and authorization policy.

Add users to the Aserto directory

Since our application deals with user access, we're going to need users in our directory. Aserto lets us add identity providers, and automatically syncs the users registered with those identity providers to the Aserto directory and authorizer. In this tutorial we’re going to use the Acmecorp identity provider, which simulates an identity provider with hundreds of users, each with their own set of roles and attributes.

Log in to your Aserto account. To add the Acmecorp identity provider, go to the Connections tab, and click “Add connection”.

add connection

From the dropdown, select “acmecorp”:

select acmecorp as identity provider

Name the provider (you can choose whatever name you want) and give it a description. Then, click “Add connection” to complete the process.

Review your users

Click on the “Users” panel. The users you’ll see have been imported from the identity provider Acmecorp into your directory. Let’s review a couple of users: Search the directory for Euan Garden and click on his user card.

Euan user card

You’ll see the following JSON object (shortened here for brevity):

  "id": "cirkzmrhzgmzos03mzm1ltqwngqtywy2ni1jnzdjzjezyte1zjgsbwxvy2fs",
  "enabled": true,
  "display_name": "Euan Garden",
  "email": "",
  "properties": {
    "department": "Sales Engagement Management",
    "manager": "2bfaa552-d9a5-41e9-a6c3-5be62b4433c8",
    "phone": "+1-804-555-3383",
    "title": "Salesperson",
    "roles": [

Users in this identity provider have properties and roles associated with them. In this case, among other roles, Euan has the role of a viewer. If you search the Aserto directory for the user Kris Johnson and inspect her associated JSON object, you’ll see she has the role of admin. Later in this tutorial we will leverage these roles to allow the authorizer to make a decision as to which user will have access to a piece of sensitive information.

But first, we'll set up our React application. Let's get started!

React application setup

Note: If you'd like to skip to the end, rather than follow the steps, simply clone the demo repo:

git clone
cd aserto-react-and-node-with-conditional-rendering
yarn install:all

From here, make sure you consult the README. Specifically, you also need to instantiate an Aserto policy before you can run yarn start:all to run the app. To do that, skip to the step called "Create an Aserto policy".

Create a new React app

We’re going to build a very bare bones application for this tutorial. We’ll start by creating an application using the yarn react-app generator: In your terminal, execute the following command:

yarn create react-app aserto-react-demo

You can now cd into the newly created folder and start the app:

cd aserto-react-demo
yarn start

The familiar React logo should appear, indicating that the app is ready to go.

react logo

Downgrade to React 17

The OIDC dependencies we will shortly install aren't yet working with React 18, so we'll need to downgrade.

yarn add react@17 react-dom@17 @testing-library/react@12 @types/react@17 @types/react-dom@17

You'll also need to change index.js as follows:

Change the line import { createRoot } from 'react-dom/client'; to the following:

import ReactDOM from 'react-dom';

Next, change this block:

const container = document.getElementById('root')
const root = createRoot(container)
    <App />

To the following:

    <App />

Make sure the app still loads and shows the spinning React logo.

We are now back to React v17.0.2 and can proceed to add authentication and authorization.

Adding OIDC dependencies

Now that we have a running React application, we'll continue by installing and then importing the required dependency - oidc-react

In your terminal, execute the following command:

yarn add oidc-react@1.5.1

The following environment variables are used to point your application to Aserto’s demo IDP, so that you don’t have to set one yourself. Create a file called .env and add the following:

Note: Make sure the .env file is added to the .gitignore file so that it is not checked in.

Open the file src/index.js and add the dependency:

import { AuthProvider } from "oidc-react";

Add the following configuration object:

const configuration = {
  authority: `https://${process.env.REACT_APP_OIDC_DOMAIN}/dex`,
  clientId: process.env.REACT_APP_OIDC_CLIENT_ID,
  autoSignIn: true,
  responseType: "id_token",
  scope: "openid profile email",
  redirectUri: window.location.origin,
  audience: process.env.REACT_APP_OIDC_AUDIENCE,
  onSignIn: () => {

Next, we'll wrap the top level React application component with the AuthProvider, and pass it the required configuration we created.

    <AuthProvider {...configuration}>
      <App />

Note: When developing locally, make sure your application is running on port 3000 - other ports are not registered with the identify provider and will not work.

If your application is still running, you should see this login window:

login window

Use the following user credentials to log in:

  • Email address:
  • Password: V@erySecre#t123!

After logging in, you should see the React logo again.

react logo

Add a stylesheet

We've created a stylesheet for this app. To reference it in your index.html file in the public folder. in the <head> section, add the following:

<link rel="stylesheet" href=""/>

Next, we’ll build the app itself. Open the App.js file, and replace it’s contents with:

import React, { useEffect } from "react";
import { useAuth } from "oidc-react";

function App() {
  const auth = useAuth();
  const isAuthenticated = auth.userData?.id_token ? true : false;

  //If the user logs out, redirect them to the login page
  useEffect(() => {
    if (!auth.isLoading && !isAuthenticated) {

  return (
    <div className="container">
      <div className="header">
        <div className="logo-container">
          <div className="logo"></div>
          <div className="brand-name"></div>

      <div className="user-controls">
        {isAuthenticated && (
            <div className="user-info">{auth.userData?.profile?.email}</div>
            <div className="seperator"></div>
            <div className="auth-button">
              <div onClick={() => auth.signOut("/")}>Log Out</div>
        {!isAuthenticated && (
          <div className="auth-button">
            <div onClick={() => auth.signIn("/")}>Login</div>

      <div className="main">
        {isAuthenticated && (
            <div className="top-main">
              <div className="welcome-message">
                Welcome {auth.userData?.profile?.email}!

export default App;

Test the application

Let's test our application by logging in. If it's not already running, start your application by executing:

yarn start

If you haven't already, log in, using the following credentials:

  • Email address:
  • Password: V@erySecre#t123!
log in as euan

If everything works as expected, you should see this:

logged in as euan

Make sure that the application's authentication flow works by logging out and then logging back in.

Great! Our application authenticates with the Acmecorp IDP, and so we have our user's identity in hand. Next, we'll create the Express.js service which will host our protected resource and will communicate with the Aserto hosted authorizer to determine whether or not a logged in user has the permissions to access the protected resource, based on the user's identity.

Service setup

To get started, let's create a new folder called service under the React application folder. cd into the folder and run:

yarn init -y
yarn add express express-jwt jwks-rsa cors @aserto/aserto-node dotenv

To the .env file we created previously, we'll add the following:


In the service folder, Create a file called api.js - that will be our server. To this file, we'll add the following dependencies:

const express = require("express");
const { expressjwt: jwt } = require("express-jwt");
const jwksRsa = require("jwks-rsa");
const cors = require("cors");
const app = express();

In the next section we define the middleware function which will call our identity provider to verify the validity of the JWT (and also enable CORS): Express.js will pass the call to the checkJwt middleware which will determine whether the JWT sent to it is valid or not. If it is not valid, Express.js will return a 403 (Forbidden) response.

//Paste after the dependencies

const checkJwt = jwt({
  // Dynamically provide a signing key based on the kid in the header and the signing keys provided by the JWKS endpoint
  secret: jwksRsa.expressJwtSecret({
    cache: true,
    rateLimit: true,
    jwksRequestsPerMinute: 5,
    jwksUri: process.env.JWKS_URI,

  // Validate the audience and the issuer
  audience: process.env.AUDIENCE,
  issuer: process.env.ISSUER,
  algorithms: ["RS256"],

Lastly, we set up a protected route which will use the checkJwt middleware:

// Enable CORS

// Protected API endpoint
app.get("/api/protected", checkJwt, function (req, res) {
  //send the response
    secretMessage: "Here you go, very sensitive information for ya!",

// Launch the API Server at localhost:8080

Awesome! our service will be listening on port 8080 and we set up a protected endpoint. In the next section we'll test this endpoint by updating our application to send a JWT token.

Update the application

To test this endpoint we're going to have to make sure the React app actually sends the authentication token to the server and requests the protected resources. To do that, we'll have to make some changes to the App.js file in our React app.

At the top of the file, modify this line:

import React, { useEffect } from "react";


import React, { useEffect, useCallback, useState } from "react";

Then, find the following code block:

function App() {
  const auth = useAuth();
  const isAuthenticated = auth.userData?.id_token ? true : false;

And add the this code right after the definition for the isAuthenticated variable:

const [message, setMessage] = useState(false);
const accessSensitiveInformation = useCallback(async () => {
  try {
    if (!auth.isLoading) {
      const accessToken = auth.userData?.id_token;
      const sensitiveInformationURL = `${process.env.REACT_APP_API_ORIGIN}/api/protected`;
      const sensitiveDataResponse = await fetch(sensitiveInformationURL, {
        headers: {
          Authorization: `Bearer ${accessToken}`,

      try {
        const res = await sensitiveDataResponse.json();
      } catch (e) {
        //In case no access is given, the response will return 403 and not return a JSON response
  } catch (e) {
}, [auth.isLoading, auth.userData?.id_token]);

In this portion of the code we create a callback (which will be triggered by a button). The callback will first get our JWT token from the identity provider, using the auth object that is obtained from the useAuth hook. Then, we perform the call to our service sending the authorization token as part of our request's headers (fetch).

Finally, we parse the JSON response from the server and set the state of the message variable: if the service returns 403 Forbidden or 401 Unauthorized errors, the message “No access to sensitive information” will be shown. If no error is returned from the service, the user will get access to the protected resource.

Next we’ll update the main section of the app (in the div with the className main) to include the button that will trigger accessSensitiveInformation and an area to show the message. Replace the existing div with the class main section with the following:

<div className="main">
  {isAuthenticated && (
      <div className="top-main">
        <div className="welcome-message">
          Welcome {auth.userData?.profile?.email}!
          {!message && (
              onClick={() => accessSensitiveInformation()}
              Get Sensitive Resource
          <div className="message-container">
            {message && message !== 403 && message !== 401 && (
                <div className="lottie"></div>
                <div className="message">{message}</div>
            {message && message === 401 && (
                <div className="sad-lottie"></div>
                <div className="message">
                  No access to sensitive information
            {message && message === 403 && (
                <div className="sad-lottie"></div>
                <div className="message">
                  No access to sensitive information

Test the application

To run both your application and the server in parallel, add the npm-run-all dependency: cd into the project's root folder and run:

yarn add npm-run-all

Then, update the package.json in the root folder, and add the following to the scripts section:

"scripts": {
 "start:server": "node service/api.js",
 "start:all": "npm-run-all --parallel start start:server"

First, stop the application by hitting ctrl+c in the terminal where you previously started the application. To start both the application and the server, you can now run:

yarn start:all

Let's test our application by first logging out, then logging in again with the email and the password V@erySecre#t123!.

If everything works as expected, we should see the following:

euan sees sensitive data

We can further test this by intentionally sending a malformed header and making sure the sensitive information isn't shown. One way to do this is to append some rogue characters to the access token like so:

const sensitiveDataResponse = await fetch(sensitiveInformationURL, {
   headers: {
       Authorization: `Bearer ${accessToken}SOME_ROGUE_CHARACTERS`,

In this case we'd expect the "No access to sensitive information" message to be displayed.

 no access


At this point, we have successfully implemented an authentication flow in our React application. We can now move on to the next step: creating an authorization policy that will govern how users access our protected endpoint.

Creating a role-based access control (RBAC) authorization model

Until now, we dealt only with authentication of users in our application. We focused on ensuring that our users are who they claim to be. Now, we can move on to setting-up an authorization model which will enforce limitations on user access to protected resources. In this example, we want to limit access to the protected resource in our application only to users who have particular roles. This authorization model is called role-based access control (RBAC) and it is the simplest model to set up.

Earlier we saw that the user Euan Garden( has the viewer role, and the user Kris Johnson ( has the admin role. Right now, if you log in as Euan you’ll see the following:

euan has access

We want to ensure that if we're logged in as Euan (a viewer), the application won't allow access to our protected resource. Since we didn't add any way to authorize users based on their role - all users will have access to the protected resource. Let's fix that by first creating a simple Aserto policy to allow access only to users with the admin role. We'll then use this policy in our application.

Create an Aserto policy

Initially, the policy we’ll create for this tutorial will only allow a user with the role of admin to access our protected resource. Users without the admin role will not be able to access that resource.

In, go to the Policies tab and click the "Create an instance" button:

add policy

Choose "...from a sample image", and select policy-aserto-react from the dropdown. Choose the latest tag, and use the name aserto-react.

Finally, click the "Create policy" button.

That was easy! We now have a policy that will only allow users with the admin role access our protected resource.

You can examine the policy by clicking on the Modules tab in the left-nav.

package asertodemo.GET.api.protected

default allowed = false

allowed {
  some index[index] == "admin"

In this policy, the only allowed user is one with an admin role.


Great! We now have a policy that will only allow users with the admin role access our protected resource. In the next section we'll see how to reference the policy in our Express.js service.

Update the Express service to use the Aserto Express.js middleware

In order to have our policy govern authorization in our service, we need to configure and apply the Aserto Express.js middleware. In order to avoid saving any secret credentials in our source code, we'll add the following credentials to our .env file. To find these credentials, click on your policy in the Policies tab. Then click the "Download config" button in the top right, and add the values in the downloaded file to your .env file:

policy details

Copy the values from the downloaded file to your .env file:


Add the following dependency reference in service/api.js (after the const jwt = require("express-jwt"); line):

const { jwtAuthz } = require("@aserto/aserto-node");

Continue by creating the configuration object for the Aserto middleware. Add the following section after the const app = express(); line:

const authzOptions = {
  authorizerServiceUrl: process.env.ASERTO_AUTHORIZER_SERVICE_URL,
  instanceName: process.env.ASERTO_POLICY_INSTANCE_NAME,
  instanceLabel: process.env.ASERTO_POLICY_INSTANCE_LABEL,
  policyRoot: 'asertodemo',
  authorizerApiKey: process.env.ASERTO_AUTHORIZER_API_KEY,
  tenantId: process.env.ASERTO_TENANT_ID,

We'll define a function for the Aserto middleware, and pass it the configuration object.

//Aserto authorizer middleware function
const checkAuthz = jwtAuthz(authzOptions);

Lastly, add the checkAuthz middleware to our protected route: Add the reference to the checkAuthz middleware right after the checkJwt middleware reference. Your endpoint definition should look like this:

//Protected API endpoint
app.get("/api/protected", checkJwt, checkAuthz, function (req, res) {
  //send the response
  res.json({ secret: "Very sensitive information presented here" });

The checkAuthz middleware is going to pass the request context - which consists of the policy reference (based on the request route), the identity context (based on the JWT token passed), and resource context (based on the request parameters) - to the authorizer, which given the policy will determine what the allowed decision would be.

Test the application

Before testing the application, stop both the application and the server by hitting ctrl+c and run yarn start:all again.

When we log in with the user who has the role of an admin we will still be able to see the following:

krisj sees sensitive information

If we log out and log in again as we will see the following:

euan has no access

Euan doesn’t have the role of admin, so the route /api/protected will be disallowed.


This concludes our tutorial! We covered the basics of connecting a React application to an identity provider, using OIDC for authentication and setting up a Node.js service that verified the validity of the JWT token passed from the application. We then learned how to create and update a role-based access control authorization policy and how to use that policy with the Aserto Express.js middleware to create a protected endpoint that allows access only to users with an authorized role.

But you're journey doesn't have to end here! You can learn how to expand your policy to include more roles, as well as how to use Aserto's React SDK for conditional UI rendering.

The finished application code is available in here.

As always, we'd love to hear your feedback. Drop us a line here, or join our community Slack.