In this article, we aim to implement a well-known security standard called OAuth which will help us secure our endpoints anywhere.
OAuth is a standard that you can implement on your own by using a variety of libraries. It is also a consistent and easy way to:
Explaining OAuth is out of scope for this tutorial but you can find more information here.
Now let's get to the point and see what are the steps to implement OAuth with a NodeJS microservice.
In order to understand better, let's start by reviewing its architecture.
As part of the OAuth architecture, we have an authorization server that acts as middleman in handling all the permissions very clearly. Instead of providing credentials to another application to access your resources, with OAuth we’ll provide a key that this application will use to retrieve a token with a very specific set of permissions called scopes. The scopes are a representation of our resources in the resource server.
This is an example of how the OAuth flow works, as you can see in the following diagram.
The application that needs access to a specific resource server makes a call to the OAuth Authorisation Server first, with the client id and secret previously shared with the application by the authorisation server. The scope in this case should represent the service that the application wants to access.
If all goes well, the OAuth Authorisation Server will push back an access token, valid only for a specific timeframe and only to access the resource specified in the scope.
The application is now able to call the resource server (our services) by including the token in the authorisation header of each call.
OAuth security is based on access tokens for authorisation, so let's talk about the different types of tokens that are used by this standard and some particularities about them.
The access tokens are the mechanism that the applications will use to access an API or service on behalf of a user. This token represents the authorization of a specific application to access an specific part of information on the service and they are short-lived.
The access token is very sensitive information and we should keep it in a very secure way, so it should only be accessed via the resource server, the authorization server and the application itself.
All the tokens are usually represented with the JWT standard (JSON Web Tokens).
JWTA JWT (pronounced 'jot') is a secure and trustworthy standard for token authentication. JWTs allow you to digitally sign information (referred to as claims) with a signature and can be verified at a later time with a secret signing key.
This one can live more time, as in days, months, or even years. It can be used to get new tokens. To get a refresh token, applications typically require confidential clients with authentication.
OAuth can be combined with another standard called SAML, OpenID Connect (OIDC) extends OAuth 2.0 with a new signed id_token for the client to be able to get more information about the users directly from the token.
So this is great, but how can we implement this standard?, well OAuth can be implemented in two ways: on your own, or via third-parties.
Implementing on your own is maybe the hardest option, as you will need to create and maintain the authorisation server from your end. However, it also has some advantages as you will have more control over the authorization piece and we can take advantage of libraries that are available in any programming language.
For NodeJS we have oauth2-server - open source, simple, and easy to integrate with your Node apps (even if they’ve already been running for a while).
In the docs, you will find the official Model Specification that describes how your JS code must override the default OAuth2 functions to provide your customised auth experience.
With the OAuth2Server object, you can override the default OAuth2 provider by the Express server. Then, we can easily provide your own auth experience.
Another way to use OAuth is by entrusting a third-party with providing the desired level of security. OAuth is based on access tokens for authentication and authorisation, so - given the fact that this is just an example - I’ll use Okta as OAuth provider.
Lets see all of this in an example specifically to secure a nodeJS service.
All the code is available here and you will also need to have NodeJs installed.
First of all, let's create a very simple nodeJS application: create a folder called 'node-oauth-test', then open a terminal in that folder and execute the following command:
npm init
After providing all the required information, a package.json
file will be created for you. Then we need to add a very simple code for our nodejs 'hello word' example.
To do this, you have to create a new file, index.js, and add the content provided below.
The code is self-explanatory but in this case we are using a library called ExpressJS to create our first hello world path.
To install express, type
npm install express util
The next step is to run your NodeJS application for the first time, by executing the following:
Now we should see our application running in port 8080 directly in our browsers.
Now that we have our application up and running, it is time to secure it: the first step for this is to setup Okta CLI, a utility that will help us make this process very smooth.
First step is installing a package manager (I used Chocolatey), open a powershell with admin permissions and execute the following command:
Now its time to Install Okta CLI, then in the same powershell run this command:
choco install okta --version 0.7.1
If all goes well, let's setup our Okta organization for testing proposes by executing the following command:
After a few seconds, Okta will send you an email with a confirmation code, then you just need to copy the details of your testing organization.
Now is important to go to the same folder of your nodejs project in order to create our testing application in Okta.
Let's run the following command, which will create a new file inside your project folder called '.okta.env': okta apps create service
.
The next step is to create the environment variables for your project. It's important for you to ignore the .env and .okta.env from your source code control to avoid security bridges.
Create a new file called '.env', which will include the following variables:
NOTE: You will need to replace the content later.
For now, let's create a new scope in the okta web interface. Run okta login and open the resulting URL in your browser. Sign in the Okta Admin Console, then go to Security > API and select your default authorization server.
You need to navigate to the Scopes tab and click on the 'Add Scope' button and create a scope for your REST API. This time will be testing_scope
- remember to replace the scope in your .env file.
Next, using express, we create an interceptor in nodejs - this will help our application to verify the JWT token to see if its valid.
We create a new file called 'auth.js' and paste this content - this is only to verify our tokens against okta endpoints.
Install dotenv to load environment variables and jwt verifier
from okta, using
npm install dotenv@8.2.0 @okta/jwt-verifier@2.1.0
Finally, we need to add some changes to our index.js: basically, our simple service is now using an interceptor defined in the ./auth.js
file to verify the token before the request reach our endpoints.
If you run our code again, you will see an error if you access directly from your browser.
What do we do? Let's call the service with a bad bearer token, using postman.
Now we have an endpoint that is only accessible with an access_token
provided by Okta, a third-party that will guarantee the security of our application. In order to call our service, we need to ask Okta for a new token and then pass it to our service.
In postman, create a new POST request and complete the following details with your own information:
In the body, don't forget to add the grant_type
as client_credentials
and the scope as test_scope
following with our example.
You should get your own access_token
, which is what we will use to call our new service; you just need to add the access token in the bearer token option.
Note: Postman collections are also in the source repository.
OAuth is an amazing way to secure our endpoints. Now that you know there are two ways to implement it, choose the option that better fits your needs.
From my perspective, this decision depends on the layout of your team and how many services you would like to secure. NodeJS, with the help of express, has built-in support for OAuth, which makes the implementation quite easy.
Using Okta as an OAuth is easy too and allows you to focus on the business logic instead of creating and maintaining the Oauth architecture on your own.