Many modern applications separate the backend services from the frontend user interface. In this type of architecture, the backend will expose a web based API that the frontend client consumes. Typically, the backend will handle incoming requests and return a JSON or XML encoded response. The frontend will then be in charge of formatting, styling, and displaying this response to the user. We’ve all heard the term “separation of concerns” and applying it at this scale has many benefits.

The backend services can grow and evolve independent of the frontend client. New APIs, if properly versioned, can provide new features and functionality without breaking existing integrations. A single backend can interface with multiple clients and the frontend clients will not be limited to any specific framework or programming language. This means that your single backend can work with your app for both a web based implementation, your mobile app, and even with IoT devices.

In today’s post, we are going to build and expose a RESTful API built with NodeJS. We are also going to build two front-end clients, also with NodeJS, that are going to consume our API. Our API is going to be protected with Auth0. In addition to protecting our API endpoints, we’ll also add specific roles to each of our clients and show how we can give granular access to our API.

Let’s get started.

The application we are building today will be an app called Movie Analyst. Movie Analyst aggregates and displays a list of the latest movie reviews by famous critics across the web. Users can view the latest movie reviews, learn about the critics that review them, and also see the publications that Movie Analyst has partnered with. Website administrators, who will have a separate website, can see and edit these pages, but also have access to reviews-in-progress so that they can prepare and approve reviews ahead of time.

Rather than building two separate backends for the user facing side and the admin application, we are going to build and expose an API with four endpoints. The user facing app will have access to the movies, reviewers and publications endpoints, while the admin app will additionally have access to the pending endpoint which will return movie reviews that are not ready to be publically accessible. We’ll start by building our API.

We’ll build our backend API with NodeJS and the Express framework. To get started let’s create a directory called movie-analyst-api , navigate into it and run npm init to setup our package.json file. You can leave all the default settings, or change them as you see fit. Once you’ve gone through the setup, the package.json file will be created in your directory.

Before we get to writing code, let’s take care of our dependencies. Run npm install express express-jwt auth0-api-jwt-rsa-validation --save to install the dependencies we are going to need. The express dependency will pull down the express framework, express-jwt library will give us functions to work with JSON Web Tokens, and finally auth0-api-jwt-rsa-validation will provide a helper function for getting our secret key.

Now that we have our dependencies in place, let’s create a new file called server.js . Since our backend API is only going to expose a few routes, we’ll write all of our code in this single file. Check out our implementation below.

var express = require ( 'express' ) ; var app = express ( ) ; var jwt = require ( 'express-jwt' ) ; var rsaValidation = require ( 'auth0-api-jwt-rsa-validation' ) ; app . get ( '/movies' , function ( req , res ) { var movies = [ { title : 'Suicide Squad' , release : '2016' , score : 8 , reviewer : 'Robert Smith' , publication : 'The Daily Reviewer' } , { title : 'Batman vs. Superman' , release : '2016' , score : 6 , reviewer : 'Chris Harris' , publication : 'International Movie Critic' } , { title : 'Captain America: Civil War' , release : '2016' , score : 9 , reviewer : 'Janet Garcia' , publication : 'MoviesNow' } , { title : 'Deadpool' , release : '2016' , score : 9 , reviewer : 'Andrew West' , publication : 'MyNextReview' } , { title : 'Avengers: Age of Ultron' , release : '2015' , score : 7 , reviewer : 'Mindy Lee' , publication : 'Movies n\' Games' } , { title : 'Ant-Man' , release : '2015' , score : 8 , reviewer : 'Martin Thomas' , publication : 'TheOne' } , { title : 'Guardians of the Galaxy' , release : '2014' , score : 10 , reviewer : 'Anthony Miller' , publication : 'ComicBookHero.com' } , ] res . json ( movies ) ; } ) app . get ( '/reviewers' , function ( req , res ) { var authors = [ { name : 'Robert Smith' , publication : 'The Daily Reviewer' , avatar : 'https://s3.amazonaws.com/uifaces/faces/twitter/angelcolberg/128.jpg' } , { name : 'Chris Harris' , publication : 'International Movie Critic' , avatar : 'https://s3.amazonaws.com/uifaces/faces/twitter/bungiwan/128.jpg' } , { name : 'Janet Garcia' , publication : 'MoviesNow' , avatar : 'https://s3.amazonaws.com/uifaces/faces/twitter/grrr_nl/128.jpg' } , { name : 'Andrew West' , publication : 'MyNextReview' , avatar : 'https://s3.amazonaws.com/uifaces/faces/twitter/d00maz/128.jpg' } , { name : 'Mindy Lee' , publication : 'Movies n\' Games' , avatar : 'https://s3.amazonaws.com/uifaces/faces/twitter/laurengray/128.jpg' } , { name : 'Martin Thomas' , publication : 'TheOne' , avatar : 'https://s3.amazonaws.com/uifaces/faces/twitter/karsh/128.jpg' } , { name : 'Anthony Miller' , publication : 'ComicBookHero.com' , avatar : 'https://s3.amazonaws.com/uifaces/faces/twitter/9lessons/128.jpg' } ] ; res . json ( authors ) ; } ) app . get ( '/publications' , function ( req , res ) { var publications = [ { name : 'The Daily Reviewer' , avatar : 'glyphicon-eye-open' } , { name : 'International Movie Critic' , avatar : 'glyphicon-fire' } , { name : 'MoviesNow' , avatar : 'glyphicon-time' } , { name : 'MyNextReview' , avatar : 'glyphicon-record' } , { name : 'Movies n\' Games' , avatar : 'glyphicon-heart-empty' } , { name : 'TheOne' , avatar : 'glyphicon-globe' } , { name : 'ComicBookHero.com' , avatar : 'glyphicon-flash' } ] ; res . json ( publications ) ; } ) app . get ( '/pending' , function ( req , res ) { var pending = [ { title : 'Superman: Homecoming' , release : '2017' , score : 10 , reviewer : 'Chris Harris' , publication : 'International Movie Critic' } , { title : 'Wonder Woman' , release : '2017' , score : 8 , reviewer : 'Martin Thomas' , publication : 'TheOne' } , { title : 'Doctor Strange' , release : '2016' , score : 7 , reviewer : 'Anthony Miller' , publication : 'ComicBookHero.com' } ] res . send ( pending ) ; } ) app . listen ( 8080 ) ;

If we launch our API server and navigate to any of the four routes we’ve created, we should get the expected response. Let’s test this by running node server and navigating to localhost:8080/movies . You should see a JSON response with the list of movie reviews and their associated data.

We have our API but now anyone can call it. Let’s fix this by securing our endpoints with Auth0. Auth0 makes user identity simple and recently they released a new feature to make securing API endpoints a breeze as well. If you don’t already have an Auth0 account, signup for a free account here. Once you’ve signed up, navigate to your Auth0 dashboard and click on the New Client button to create a new Auth0 application.

For the client type, select Non-Interactive Client and name it MovieAnalyst Website. Click on the Create button to create the client.

As this is a new feature that is still in beta, you will be prompted to enable the API functionality. You can do so by simply flipping the switch and a new menu option titled “API’ will be available. Click on this new menu option to continue.

The API tab will already have one API created automatically, this is the Auth0 Management API. Utilizing this API, you can create your own Auth0 dashboard or manage any of the Auth0 features programmatically. To learn more about the features of the Management API, click here. We won’t be using this API, so let’s instead click on the Create API button to create our own API.

You can name your API whatever you want, we’ve chosen MovieAnalystAPI. The identifier field will be a unique identifier for your API. This field does not have to be a URL, so feel free to put an identifier that makes the most sense to you - just know that this field cannot be changed. We’ll leave the signing algorithm as RS256 and click the Create API button.

We are also going to create one more client for our Admin front end. This will follow the same process as when we created the first client. Navigate to the Clients section and click on Create New Client. This time name the client MovieAnalyst Admin and make it a non-interactive client as well. Click on the Create button to create this client.

With our two clients and API created in the Auth0 dashboard, we can now go ahead and secure our API endpoints. Open up the server.js file again and make the following updates.

var jwtCheck = jwt ( { secret : rsaValidation ( ) , algorithms : [ 'RS256' ] , issuer : "https://YOUR-AUTH0-DOMAIN.auth0.com/" , audience : 'https://movieanalyst.com' } ) ; app . use ( jwtCheck ) ; app . use ( function ( err , req , res , next ) { if ( err . name === 'UnauthorizedError' ) { res . status ( 401 ) . json ( { message : 'Missing or invalid token' } ) ; } } ) ;

If we launch our server now and navigate to any of the routes, we’ll get an error message as we are not providing the correct information on our requests to the server.

This is intended as we don’t want just anyone to have access to our API endpoints. In addition to protecting our API routes, we are also going to fine-tune access to specific endpoints. Luckily, we can do this with ease in the Auth0 management dashboard.

Head over the management dashboard, navigate to the API section and click into the MovieAnalyst API we created earlier. From here, click on the Scopes tab.

Scopes allow us to grant specific permissions to clients that are authorized to use our API. For our demo, we are going to create two scopes, general and admin. In an actual application, you could further narrow down the scopes by giving read or write permissions and even go as far to protect each individual route with a separate scope. We’ll keep it fairly general here though. Go ahead and create the two scopes now.

Now that we have our scopes in place, the last thing we’ll need to do is authorize our two clients to work with the API we created. Within the MovieAnalystAPI section, navigate to the Non-Interactive Clients tab. Here you will see a list of all the clients that can interface with our API. By default, when we created our MovieAnalystAPI a test client was created for us. We’ll also see the two clients we created but they will be displayed as Unauthorized.

To authorize our clients, flip the switch to Authorized and you will see additional information displayed. In addition to enabling access to the client, we can easily manage which scopes the client will have. For the admin client, let’s enable both the general and admin scopes, and for the website client, we’ll just enable general access. Be sure to hit the Update button once you’ve made the scope changes.

Now that we have our scopes in place, let’s go back to the server.js file for our API to make some final edits. What we are adding now is the ability to check if the client has permissions to view the endpoint requested. To do this, we’ll create another middleware that will look at the decoded JWT and see if it the token has the correct scope. If it doesn’t we’ll send an appropriate forbidden message, otherwise we’ll send the data. Take a look at our implementation of this functionality below.

var guard = function ( req , res , next ) { switch ( req . path ) { case '/movies' : { var permissions = [ 'general' ] ; for ( var i = 0 ; i < permissions . length ; i ++ ) { if ( req . user . scope . includes ( permissions [ i ] ) ) { next ( ) ; } else { res . send ( 403 , { message : 'Forbidden' } ) ; } } break ; } case '/reviewers' : { var permissions = [ 'general' ] ; for ( var i = 0 ; i < permissions . length ; i ++ ) { if ( req . user . scope . includes ( permissions [ i ] ) ) { next ( ) ; } else { res . send ( 403 , { message : 'Forbidden' } ) ; } } break ; } case '/publications' : { var permissions = [ 'general' ] ; for ( var i = 0 ; i < permissions . length ; i ++ ) { if ( req . user . scope . includes ( permissions [ i ] ) ) { next ( ) ; } else { res . send ( 403 , { message : 'Forbidden' } ) ; } } break ; } case '/pending' : { var permissions = [ 'admin' ] ; console . log ( req . user . scope ) ; for ( var i = 0 ; i < permissions . length ; i ++ ) { if ( req . user . scope . includes ( permissions [ i ] ) ) { next ( ) ; } else { res . send ( 403 , { message : 'Forbidden' } ) ; } } break ; } } app . use ( guard ) ;

Our guard middleware will be called on each request and will ensure that the token has the correct scope. If it does, we’ll send the data, otherwise we’ll return a 403 Forbidden status and appropriate message.

This wraps up our API implementation. If we launch our server now and try to navigate to any of the routes, we’ll still see the 401 Unauthenticated response. That’s still ok, as our API is meant to interface with other clients and not end-user browsers. Next, we’ll implement our first client, the MovieAnalyst Website.

We have our API ready, now let’s build a UI to consume it. Create a new directory titled movieanalyst-website and navigate into it. Run npm init for this directory to create a package.json file just like for the API. For our website dependencies run npm install express ejs superagent --save . The express dependency is self-explanatory, we will use ejs as our templating engine and finally the superagent library will handle making HTTP requests to our API.

In addition to creating a server.js file, you’ll also want to create a public directory and in this directory, create a subdirectory titled views . We are going to have four views for our user facing website so let’s create them now. The four views we are going to create are:

index.ejs - this will be our homepage

- this will be our homepage movies.ejs - this will be the view that display movie reviews

- this will be the view that display movie reviews authors.ejs - this will display the list of critics

- this will display the list of critics publications.ejs - this will will display our publication partners

With the scaffolding done, open up the server.js file to get started with the implementation. Let’s take a look at the implementation and we’ll explain what’s going on line by line.

var express = require ( 'express' ) ; var request = require ( 'superagent' ) ; var app = express ( ) ; app . set ( 'view engine' , 'ejs' ) ; app . set ( 'views' , __dirname + '/public/views/' ) ; app . use ( express . static ( __dirname + '/public' ) ) ; var NON_INTERACTIVE_CLIENT_ID = 'YOUR-AUTH0-CLIENT-ID' ; var NON_INTERACTIVE_CLIENT_SECRET = 'YOUR-AUTH0-CLIENT-SECRET' ; var authData = { client_id : NON_INTERACTIVE_CLIENT_ID , client_secret : NON_INTERACTIVE_CLIENT_SECRET , grant_type : 'client_credentials' , audience : 'https://movieanalyst.com' } function getAccessToken ( req , res , next ) { request . post ( 'https://YOUR-AUTH0-DOMAIN.auth0.com/oauth/token' ) . send ( authData ) . end ( function ( err , res ) { if ( req . body . access_token ) { req . access_token = res . body . access_token ; next ( ) ; } else { res . send ( 401 , ‘Unauthorized’ ) ; } } ) } app . get ( '/' , function ( req , res ) { res . render ( 'index' ) ; } ) app . get ( '/movies' , getAccessToken , function ( req , res ) { request . get ( 'http://localhost:8080/movies' ) . set ( 'Authorization' , 'Bearer ' + req . access_token ) . end ( function ( err , data ) { if ( data . status == 403 ) { res . send ( 403 , '403 Forbidden' ) ; } else { var movies = data . body ; res . render ( 'movies' , { movies : movies } ) ; } } ) } ) app . get ( '/authors' , getAccessToken , function ( req , res ) { request . get ( 'http://localhost:8080/reviewers' ) . set ( 'Authorization' , 'Bearer ' + req . access_token ) . end ( function ( err , data ) { if ( data . status == 403 ) { res . send ( 403 , '403 Forbidden' ) ; } else { var authors = data . body ; res . render ( 'authors' , { authors : authors } ) ; } } ) } ) app . get ( '/publications' , getAccessToken , function ( req , res ) { request . get ( 'http://localhost:8080/publications' ) . set ( 'Authorization' , 'Bearer ' + req . access_token ) . end ( function ( err , data ) { if ( data . status == 403 ) { res . send ( 403 , '403 Forbidden' ) ; } else { var publications = data . body ; res . render ( 'publications' , { publications : publications } ) ; } } ) } ) app . get ( '/pending' , getAccessToken , function ( req , res ) { request . get ( 'http://localhost:8080/pending' ) . set ( 'Authorization' , 'Bearer ' + req . access_token ) . end ( function ( err , data ) { if ( data . status == 403 ) { res . send ( 403 , '403 Forbidden' ) ; } } ) } ) app . listen ( 3000 ) ;

We have our server.js implementation complete. Let’s build out our UI pages. We’ll start with the index.ejs page. We will make use of the Bootstrap and Font Awesome libraries to create a good looking UI fast. The homepage will just display links to the other sections of the website. Take a look at the implementation and screenshot of what the completed product will look like.

<!doctype html> < html > < head > < link rel = " stylesheet " href = " //maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css " > < link rel = " stylesheet " href = " //maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css " > < script src = " http://code.jquery.com/jquery-2.2.4.min.js " > </ script > < style > body { background-color: #ecedec; } </ style > </ head > < body > < div class = " container " > < div class = " jumbotron text-center " > < h1 > < span class = " glyphicon glyphicon-bullhorn " > </ span > </ h1 > < h2 > Movie Analyst </ h2 > </ div > < div class = " row " > < div class = " col-sm-4 " > < div class = " panel " > < div class = " panel-heading " > < h3 class = " panel-title " > Latest Reviews </ h3 > </ div > < div class = " panel-body text-center " > < h1 > < span class = " glyphicon glyphicon-bullhorn " > </ span > </ h1 > </ div > < div class = " panel-footer small " > < a href = " /movies " class = " btn btn-block btn-primary " > View Latest Reviews </ a > </ div > </ div > </ div > < div class = " col-sm-4 " > < div class = " panel " > < div class = " panel-heading " > < h3 class = " panel-title " > View Latest Reviews </ h3 > </ div > < div class = " panel-body text-center " > < h1 > < span class = " glyphicon glyphicon-user " > </ span > </ h1 > </ div > < div class = " panel-footer small " > < a href = " /authors " class = " btn btn-block btn-primary " > View Authors </ a > </ div > </ div > </ div > < div class = " col-sm-4 " > < div class = " panel " > < div class = " panel-heading " > < h3 class = " panel-title " > Our Partners </ h3 > </ div > < div class = " panel-body text-center " > < h1 > < span class = " glyphicon glyphicon-tower " > </ span > </ h1 > </ div > < div class = " panel-footer small " > < a href = " /publications " class = " btn btn-block btn-primary " > View Publications </ a > </ div > </ div > </ div > </ div > </ div > </ body > </ html >

Next, we’ll build out the movies.ejs view. In the interest of time, we’ll omit some the head section. Feel free to copy and paste the contents from the index.ejs file to save some time.

< div class = " row " > <% movies.forEach(function(movie, index) { %> < div class = " col-sm-4 " > < div class = " panel " > < div class = " panel-heading " > < h3 class = " panel-title " > <%= movie.title %> (<%= movie.release %>) </ h3 > </ div > < div class = " panel-body text-center " > < h1 > <%= movie.score %> < small > / 10 </ small > </ h1 > </ div > < div class = " panel-footer small " > < p > By <%= movie.reviewer %> for <%= movie.publication %> </ div > </ div > </ div > <% }) %> </ div >

The authors page will look similar as well. The implementation for authors.ejs is below.

< div class = " container " > < div class = " jumbotron text-center " > < h1 > < span class = " glyphicon glyphicon-user " > </ span > </ h1 > < h2 > Our Movie Critics </ h2 > </ div > < div class = " row " > <% authors.forEach(function(author, index) { %> < div class = " col-sm-4 " > < div class = " panel " > < div class = " panel-heading " > < h3 class = " panel-title " > <%= author.name %> </ h3 > </ div > < div class = " panel-body text-center " > < img class = " img-circle " src = " <%= author.avatar %> " /> </ div > < div class = " panel-footer small " > < p > Writes for <%= author.publication %> </ div > </ div > </ div > <% }) %> </ div > </ div >

Finally, the publications.ejs implementation is below.

< div class = " container " > < div class = " jumbotron text-center " > < h1 > < span class = " glyphicon glyphicon-tower " > </ span > </ h1 > < h2 > Our Publication Partners </ h2 > </ div > < div class = " row " > <% publications.forEach(function(publication, index) { %> < div class = " col-sm-4 " > < div class = " panel " > < div class = " panel-heading " > < h3 class = " panel-title " > <%= publication.name %> </ h3 > </ div > < div class = " panel-body text-center " > < h1 > < span class = " glyphicon <%= publication.avatar %> " > </ span > </ h1 > </ div > </ div > </ div > <% }) %> </ div > </ div >

Let’s test our app and make sure that it works. Launch the API server by running the node server command from the movieanalyst-api directory. Launch the Website server by running the node server command from the movieanalyst-website directory. Now you will have two node applications running, the API at localhost:8080 and the client facing website at localhost:3000 .

Navigate to localhost:3000 and you should see the homepage we’ve designed. Click on any of the links, and you should see the appropriate page displayed with the data we’ve defined in our API backend project. Try navigating to localhost:3000/pending . Remember, we created this route, but the response will be forbidden as our client does not have the admin scope applied to it. Let’s build our Admin frontend that will have this scope.

Our admin frontend will be very similar to the user centric frontend. In fact, to get started quickly, let’s just copy and paste the entire directory and rename it movieanalyst-admin .

Let’s open up the server.js file next as we’ll need to make some updates. We are going to need to update the NON_INTERACTIVE_CLIENT_ID and NON_INTERACTIVE_CLIENT_SECRET variables with the values from our MovieAnalyst Admin client. Head over to the management dashboard and get those values and replace them here. The other change we will need to make is to change the port the app will listen on. Since our user website listens on 3000 and our API is on 8080 , we’ll set the app.listen() port for our admin interface to 4000 .

For our UI, we are going to change it up a bit. Since this is an admin based website, rather than just displaying the content, we’ll make it all editable. We won’t actually save changes. Another new feature with the admin website is that we are going to write a partial view for the main navigational menu. Let’s create this partial view first. Create a new subdirectory in your public/views directory and name it includes . We’ll call our partial view navbar.ejs . Create the file and open it. Our navbar implementation will be as follows:

< nav class = " navbar navbar-inverse " > < div class = " container-fluid " > < div class = " navbar-header " > < a class = " navbar-brand " href = " # " > Movie Analyst Admin </ a > </ div > < div class = " collapse navbar-collapse " > < ul class = " nav navbar-nav " > < li > < a href = " /movies " > Reviews </ a > </ li > < li > < a href = " /authors " > Authors </ a > </ li > < li > < a href = " /publications " > Publications </ a > </ li > < li > < a href = " /pending " > Pending Reviews </ a > </ li > </ ul > </ div > </ div > </ nav >

Next, we’ll update each of the views to display a form rather than just the content. We’ll also add a save button, so if an admin makes any updates they can save them. Finally, we’ll be sure to include our navbar partial in each of the views. We’ll only show the movies.ejs file change here. You can follow the process shown here, or get all the code from our demo GitHub repo.

<!doctype html> < html > < head > < link rel = " stylesheet " href = " //maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css " > < link rel = " stylesheet " href = " //maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css " > < script src = " http://code.jquery.com/jquery-2.2.4.min.js " > </ script > < style > body { background-color: #ecedec; } </ style > </ head > < body > <% include ./includes/navbar %> < div class = " container " > < h2 > Editing: < strong > Movies </ strong > </ h2 > < div class = " row clearfix " > <% movies.forEach(function(movie, index) { %> < div class = " col-sm-4 " > < div class = " panel " > < div class = " panel-heading " > < h3 class = " panel-title " > Edit: <%= movie.title %> </ h3 > </ div > < div class = " panel-body text-center " > < label > Name </ label > < input type = " text " class = " form-control " value = " <%= movie.title %> " /> < label > Release </ label > < input type = " text " class = " form-control " value = " <%= movie.release %> " /> < label > Score </ label > < input type = " text " class = " form-control " value = " <%= movie.score %> " /> < label > Reviewer </ label > < input type = " text " class = " form-control " value = " <%= movie.reviewer %> " /> < label > Publication </ label > < input type = " text " class = " form-control " value = " <%= movie.publication %> " /> </ div > </ div > </ div > <% }) %> </ div > < a class = " btn btn-primary btn-block " > Save </ a > </ div > </ body > </ html >

With the server.js and our views updated, let’s launch the application and navigate to localhost:4000 . If all went well, you should see the homepage showing the new navbar as well as the links to separate pages. Let’s click on the movies link to see how the design is different. We can now also visit the /pending link by either typing it in the browser bar or clicking on Pending Reviews in the navbar. This time, instead of seeing an error, we’ll get the data successfully returned and will be able to to make edits to it.

Our API works! We have successfully built an API with two disparate clients that have different levels of access to our API. We can add as many additional clients as we want now, and they don’t have to be Node based either. We can create a mobile application or even another web application with say Laravel to consume our API. We’ll just need to create a client and get the Client ID and Secret pair and write some code to exchange this pair of credentials for an access token.

The services API and clients we built today were fairly simple. Real applications will have many more routes, features, and likely need for granularity. Auth0 luckily supports many of these features out-of-the-box.

Other considerations that will be up to you are handling how your backend API interacts with clients. It is recommended that each endpoint be rate-limited so clients don’t overload your API. This is especially important if you have many different clients interacting with your backend. You can use the Auth0 Management API to handle the generation of clients if you don’t like using the Auth0 dashboard. The Management API can handle everything the dashboard can and more which may be useful in the event that you need to revoke access from a particular client, or need to adjust scopes. For more best practices, check out the Auth0 docs concerning API Authentication and Authorization.

Today, we built a backend API that exposed RESTful data to be consumed by independent UI clients. We created two clients, each with a different set of scopes and permissions, to show how we can granularly control access to our API. The Auth0 API Authorization and Authentication feature allowed us to easily protect our API, as well as create clients to interact with our newly secured API. Sign up for a free Auth0 account today and start securing your APIs with ease.

Like this article? Follow @kukicado on Twitter

This content is sponsored via Syndicate Ads.