Authorization focused web application using Auth0 and Azure’s API management

Muhammad Salman
Houston I/O
Published in
12 min readJun 21, 2020

Introduction

In this article, we will be creating a web application that uses Auth0, Azure API management, and React.js based frontend to create an authorization focused end to end application. While production apps have lots of other moving parts as well, this simple application might give you some ideas on how to think about authorization while developing an application.

Setting Stage

Authentication and authorization are the most critical components of any application. Without properly setting up authentication and authorization, our application is vulnerable and is open to exploits from bad actors.

APIs are another critical component of our application. Most, if not every production application, has a backend and an API that provides an interface to a frontend to interact with the database. Mostly APIs are programmed in languages such as Node.js, Java, or C#. We might also have an additional layer available to us for managing APIs based on the cloud provider. The API management service streamlines the task of managing APIs, provides possibilities to avoid unnecessary backend releases and makes it easy for developers to manage APIs in the long run.

Why Auth0?

Authentication and authorization is something non-trivial and hard to do correctly yourself. OAuth2 has been an industry standard and solves this tricky problem efficiently. Auth0 provides the authentication and authorization service based on OAuth2. One of the most significant advantages of using Auth0 is that it rids the team of managing the user credentials themselves.

Why Azure’s API Management?

We would be using Azure’s API management to manage our APIs, and it sits in between the backend API and the frontend. We direct our API calls to the API Management, which forwards the request to the backend. This way, the API Management provides us with a security layer by which we can hide our real backend from the internet and only allow calls from the API Management. API management also shines when you need to work across different environments and need a single place to handle all your APIs. In our application, we will be using API management to validate that the header contains a valid JWT and has the required permission.

Configuring Auth0

Without further ado, let us start with configuring Auth0. If this is the first time you are using Auth0, you could go to Auth0 and follow the instructions to create your tenant.

Auth0 is free to use as long as the number of active users is less than 7000.

Once you log in to your tenant, let’s create an Auth0 Application. From the sidebar, you can find applications, click on it. The view should now display the ‘Create application’ button. Clicking on it opens a ‘Create application’ modal. Give your application a suitable name, for example, react-app. The application type will be `Single page web application.` Click ‘Create.’ Now we have an Auth0 application.

Next, go to the newly created application’s setting and make a note of ‘Domain’ and ‘Client id.’ We are going to use that later once we set up our front end application. On the settings page, if you scroll down, you will find text boxes for

  • Allowed Callback URLs
    Application Callback URLs are the set of your application URLs that Auth0 deems Ok to redirect to once the authorization happens.
    For example,
    http://localhost:3000, or https://your-app.example.com.
  • Allowed Logout URLs
    Allowed Logout URLs are similar to the Application Callback URL in a sense that it provides the list of URLs, but the redirect to these URLs happens after the user logs out. These could be the same set of URLs as in the Application Callback URLs.
  • Allowed Web Origins
    Allowed Web Origins is the list of URLs from which the call to Auth0 endpoints are made. The incoming request to the Auth0 endpoints from URL that is not specified in the list is blocked. These could as-well be the same as the list of URLs specified in the above two options.

We plan to access our frontend application on our local machine on http://localhost:3000. Let’s put http://localhost:3000 in all the three text boxes, and once you are aware of your production frontend endpoint, put that root URL in all three of the text boxes.

Next, let’s create an API from Auth0. You can find the API from the sidebar. Click on it and click on ‘Create API.’ In the ‘New API’ modal, give a suitable name, for example, backend-api. Give an identifier and take a note of it as it will be used later when we configure our frontend. Click ‘Create.’ Next, go to the newly created API and click on the Permissions tab. Add permission with scope ‘admin’. The users having ‘admin’ permission will have been granted access to our backend resource.

In the settings tab, scroll down to RBAC settings and toggle the ‘Enable RBAC’ and ‘Add permissions in the access token.’ button.

Next, let’s go to the ‘User management’ and add a new user with the ‘admin’ permission that we created in the last step. Clicking on the ‘Create User’ button opens up the create user modal. Provide your email address, password, and click ‘Create.’ Once the user is created, go to the permissions tab and assign the ‘admin’ permission to the user.

At this point, our Auth0 has been configured. Let’s get started with setting up our frontend application next.

React Frontend

Let’s shift our focus on creating a frontend application. We will be using the Create React App package as it comes with all the bells and whistles and does not need any additional build configuration.

To get going, run npx create-react-app auth0-app. Go to the newly created application directory and install the @auth0/auth0-react package by running npm install --save @auth0/auth0-react.

At the time of publishing this article, the @auth0/auth0-react library is in beta.

We need to provide our Auth0 tenant configuration to the Auth0 library. Let’s define them as environment variables. While using the react script, the name of the environment variable should be prefixed with REACT_APP_ for it to be accessible anywhere in the application. Let’s add the following env variables to our .env file.

  • REACT_APP_AUTH0_DOMAIN
  • REACT_APP_AUTH0_CLIENT_ID
  • REACT_APP_AUTH0_AUDIENCE

The value for these env variables are available to us from Auth0. Write down the values you noted previously in the .env file. Next, we need to wrap our application with Auth0Provider. Auth0Provider is a named import from ‘@auth0/auth0-react’ library.

In index.js

...
ReactDOM.render(
<Auth0Provider
domain={process.env.REACT_APP_AUTH0_DOMAIN}
clientId={process.env.REACT_APP_AUTH0_CLIENT_ID}
audience={process.env.REACT_APP_AUTH0_AUDIENCE}
redirectUri={window.location.origin}
>
<App />
</Auth0Provider>,
document.getElementById(‘root’)
);
...

In App.js

import { useAuth0 } from ‘@auth0/auth0-react’;const App = () => {
const {
isAuthenticated,
user,
loginWithRedirect,
logout,
} = useAuth0();
return (
<div className=”app”>
{isAuthenticated ? (
<div className=”auth-app-section”>
<div className=”user-section”>
Hello {user.name}
<button onClick={logout}>Logout</button>
</div>
</div>
) : (
<button className=”login-btn” onClick={loginWithRedirect}>
Log in
</button>
)}
</div>
);
...
}

The above code renders the login or a logout button based on if the user is authenticated or not. Let’s run this app once before modifying it further.
Go to the app directory in the terminal and give the following command. npm run start.

In the browser, you could see our app with a login button, clicking on it redirects you to the Auth0 universal login page, provide the user name and password of the user you created in Auth0. In the next view, you need to authorize the app to access your user. Once you gave the access, you should be redirected to your app and see a text welcoming you with your user name and the logout button. Next, we would be getting the JWT and making a fetch call to our API endpoint. For getting the Auth0 token
of a logged-in user, we would be using the getTokenSilently, which is available to us through the @auth0/auth0-react library. It needs a tenant audience, which could be found from the environment variables.

const App = () => {
...
const {
getAccessTokenSilently
} = useAuth0();
const token = await getAccessTokenSilently({
audience: process.env.REACT_APP_AUTH0_AUDIENCE,
});
...
}

Let’s move this code in the onClickHandler of a button which would, besides getting the token would add this token in the Authorization header of the API request and make the fetch call to the API management endpoint.

const App = () => {
...
const getResource = async () => {
const token = await getAccessTokenSilently({
audience: process.env.REACT_APP_AUTH0_AUDIENCE,
});
const response = await fetch(‘{API_ENDPOINT_COMES_HERE}’, {
headers: {
Authorization: `Bearer ${token}`,
},
});
};
...
}

Next, let’s look into how we create and define the policies in the API management service.

API Management with Azure

In the Azure portal, click on ‘Create a resource.’ Search for ‘Api Management Service’ and press Enter. It will show you the API management service resource. Click ‘Create.’ Fill in the name and the Organization name on the creation page. The name has to be unique Azure-wide. Other required fields must be already filled in. Click ‘Create’.

In the API management resource, you will find an already created Echo API. While you can create your API, but for simplicity, we will be using the Echo API. Click on the Echo API. Click on the settings of the Echo API, scroll down and uncheck the ‘Subscription required.’ With ‘Subscription required’ as unchecked, we do not need to send it as a header in our request. Click ‘Save.’

Click on the Design tab beside the Settings and click on ‘All operations.’ It will show you the flow of the incoming request and outgoing response and an easy way to edit the policy code for the request and response.

We are interested in Inbound processing. Its the place where you can modify and validate the request before it is sent to the backend service. Click on the policy code editor in the Inbound Processing section. It will open an editor where we will define our policies.

You need to specify which origins the calls can come from; otherwise, the request will get blocked by Cross-origin resource sharing(CORS) policy.

<cors allow-credentials=”true”>
<allowed-origins>
<origin>http://localhost:3000</origin>
<origin>https://your-app.example.com</origin>
</allowed-origins>
<allowed-methods>
<method>*</method>
</allowed-methods>
<allowed-headers>
<header>*</header>
</allowed-headers>
<expose-headers>
<header>*</header>
</expose-headers>
</cors>

Because Echo API uses Azure’s API by default, we don’t need to change the backend URL. Once you are ready to start using your backend you set the backend URL with:

<set-backend-service base-url=”https://backend.example.com" />

Use validate-jwt tag to validate if the user is allowed to make the call to the backend.

We need to validate the incoming JWT, and there is a validate-jwt tag available to us, and we will use it to authorize the user if he is allowed to make the call to the backend. If the user doesn’t have required permissions, the API management blocks the request, and the backend never receives the request. In the validate-jwt you can specify the header name, require-scheme, and failure http-code and message. You can easily configure the validate-jwt with Auth0’s open-id configuration from the .well-known directory.

your-tenant.auth0.com/.well-known/openid-configuration.

Within the validate-jwt you can specify the required claims and whether to match all of them or just one of them. If needed, you can write C# code inside @{ } tags, see API management docs to learn more.

You can use jwt.io to manually check the contents of the token; this will be especially useful if you want to debug the call to API endpoint.

validate-jwt policy looks like

<validate-jwt 
header-name=”Authorization”
failed-validation-httpcode=”401"
failed-validation-error-message=”@((string)context.LastError.Message)”
require-scheme=”Bearer”
require-signed-tokens=”true”
>
<openid-config
url=”https://YOUR-TENANT.auth0.com/.well-known/openid-configuration"
/>
<required-claims>
<claim name=”permissions” match=”all”>
<value>admin</value>
</claim>
</required-claims>
</validate-jwt>

Complete policy

<policies>
<inbound>
<base />
<cors>
<allowed-origins>
<origin>
http://localhost:3000
</origin>
<origin>
https://your-app.example.com
</origin>
</allowed-origins>
<allowed-methods>
<method>*</method>
</allowed-methods>
<allowed-headers>
<header>*</header>
</allowed-headers>
</cors>
<set-backend-service base-url=”https://backend.example.com” /
<validate-jwt
header-name=”Authorization”
failed-validation-httpcode=”401"
failed-validation-error-message=”@((string)context.LastError.Message)”
require-scheme=”Bearer”
require-signed-tokens=”true”
>
<openid-config
url=”https://YOUR-TENANT.auth0.com/.well-known/openid-configuration"
/>
<required-claims>
<claim name=”permissions” match=”all”>
<value>admin</value>
</claim>
</required-claims>
</validate-jwt>
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>

Next, let’s formulate the URL to which we will be making the call from our frontend. We can see the base URL from the Settings tab.

For us it is

https://acme-blog.azure-api.net/echo

We will make a ‘GET’ request from our frontend. Find the GET request from the Design Tab. Click on it, and there you should see suffix for the GET request for the URL. In the example we created, we can see it as /resource. Append that to the base URL, and we get the endpoint to which we will make an API request from our frontend.

https://acme-blog.azure-api.net/echo/resource

Working Application

Let’s shift our focus to the frontend application. The last thing we did in the frontend was putting the code to fetch our API, but we were missing the URL from it. Now, once we have configured the API management and have a URL, let’s insert that into the fetch statement. After our recent changes, our getResource method, which makes the fetch call would look something like.

const getResource = async () => {
const token = await getAccessTokenSilently({
audience: process.env.REACT_APP_AUTH0_AUDIENCE,
});
fetch({API_ENDPOINT_FROM_API_MANAGEMENT}, {
headers: {
Authorization: `Bearer ${token}`,
},
}).then((response) => {
if (response.ok) {
setResponse(‘Backend call successful’);
} else {
setResponse(‘Backend failed: ‘ + resp.statusText);
}
}).catch((err) => {
console.log(err);
});
};

The call to getResource happens on the onClick event of the button. At this point, our App react component looks like

Let’s run our application. When the application starts, it should show the ‘Login’ button. Once you log in, you can see the Logout and the Get Resource button. Clicking on Get resource makes the API call to the endpoint specified in the API management, API management checks if the user has been granted the admin permission in Auth0 and then forwards the call to the backend. Once a response comes back from the backend (and through API management), we receive it in the frontend and display the `Backend call successful` text.

Authorized API call. Found permission.

Otherwise, if the permission has not been granted to the user, then we will see a message, ‘Backend call failed: unauthorized’.

Unauthorized API call. Missing Permission.

Complete application code could be found from https://github.com/Houston-Inc/auth0-azureapi-houstonio.

Conclusion

The application we created demonstrates how we can leverage Auth0’s capabilities and parse the permission information into and out of the JWT. We also saw how we can use Azure API management to allow/deny a user from accessing a resource.

While we went through one example of how Azure’s API management and Auth0 combine to provide a secure authorization mechanism, for our Cerulean Data Hub project, we at Houston Inc are also using Auth0 and Azure API management for other interesting use cases. Stay tuned as we share our insights and experiences regarding that and a range of different technical topics and the challenges we might face during the development of our Data Hub.

This article is a part of Cerulean Data Hub article series.

I would like to thank my colleague and friend Miikka Alatalo for helping me with the creation of the example application and the writing of the text.

--

--

Muhammad Salman
Houston I/O

A software developer with a passion for breaking muddle like problems in smaller, easy-to-tackle cookie-like insights.