Enterprise Web App Authentication Using Keycloak And Node.js

Want a way in? You shall need a key!

Suppose you have developed an amazing, out of the world enterprise web application. Everything works as planned but oops! you forgot a crucial part i.e. authentication and authorization (Let’s call these A+A for the rest of this article). Anyone can access your app but being an enterprise app, this should not be the case! Only authorized users should be able to access the app.

Now, there are multiple ways of introducing A+A in your app:

  • Using a third party or cloud active directory: e.g. if your app runs on the cloud, you can use Azure AD (active directory) and its’ authentication flow. For this, you will either need to sync your organization’s AD with Azure or have a entirely separate AD in Azure for your app.
  • Using your organization’s active directory: if your app needs to be accessible for only your organization’s users, you have come to the right place. In this article, we will discuss this scenario (using Keycloak identity and access management server).

Prerequisites:

  1. A web application (running locally, in a docker container or cloud or anywhere!)
  2. An existing Keycloak IAM server (If you organization has it’s own AD, it might be using Keycloak to provide A+A services).
  3. Some knowledge about OAuth2 and OpenID Connect is required. Read this excellent article about OAuth2 to get a head-start.
  4. Go back to excellent article again and re-read about the authorization code grant flow as the scenario we will be implementing here follows the said grant flow.

Now, let’s get to work and see how we can introduce A+A in our web app.


Step 1: Keycloak server setup

This is out of scope of this article as the Keycloak server in this case is the your organization’s IAM server. You will need to register and white-list your application’s URL in the Keycloak server settings. Ideally, you should be able to ask the IT department in your organization to register this application in Keycloak server for you. More information here.

For this article, let’s assume, the white-listed URL is https://my-awesome-sauce-app.com/*. Once your application is registered in the Keycloak server, you should have the following details from the server settings (More details about these terms can be found here:

  1. Realm
  2. Authentication server URL
  3. Resource (Client Id)
  4. Secret
  5. More… (full list here)

Step 2: Node.js server setup

We will need a node.js server that will handle the authentication request from our web application and talk to the IAM server to get the required access token.

  1. Run the node.js app in HTTPS mode

We white-listed a HTTPS URL in step 1 above, so we will need to run our app in secure mode with the relevant certificates. Let’s assume, you have a new app and don’t have the required certificates. Let’s generate a new certificate to make our app run in HTTPS mode. Refer this article for the relevant commands and sample code: http://www.hacksparrow.com/node-js-https-ssl-certificate.html. Another good article to refer is this: https://www.hacksparrow.com/express-js-https-server-client-example.html.

2. keycloak.json

The Keycloak configuration requires a file called keycloak.json in the same directory as node.js’s main server file (e.g. index.js). It has the following format:

The resource and credentials/secret should be same as the one generated in Step 1 above when we registered our app with the Keycloak server.

3. Fake host name for testing

In step 1 above, the redirect URL https://my-awesome-sauce-app.com/* was white-listed in the IAM server configuration. In order to run the server locally, we need to impersonate our machine’s host name with the white-listed URL. For the same, add the following line (as administrator or root user) in c:\Windows\System32\Drivers\etc\hosts (Windows) or /etc/hosts (Linux):

<local-machine-ip> my-awesome-sauce-app.com

Run the node.js server with the following settings:

Host name = my-awesome-sauce-app.com, port: 443

Open the following URL in the browser to verify that the app is running:

https://my-awesome-sauce-app.com

Voila! The browser should automatically redirect to your organization’s IAM server’s login page. Upon successful login, it should redirect to https://my-awesome-sauce-app.com. The complete node.js code to make IAM work for you is given later in this article.


Keycloak magic

The Keycloak API we will use is pretty straightforward and simple to use. Just use keycloak.protect() middleware in every route you want to protect and it will auto-magically work. Here’s an example:

app.get('/user', keycloak.protect(), (req, res) => {
// return user data
}

In the above sample, we have an API for retrieving user data from the node.js server. We are protecting it by using the keycloak.protect() middleware so that only authorized users can access the user data. The keycloak.protect() middleware behaves as follows:

  • If the user is not logged in, it will redirect the user to IAM server’s login page.
  • If the user is logged in, it will immediately return to the call back function where you can handle the request knowing that the user has been authenticated.

This way, you don’t need to put any authentication logic in the client. Everything is automatically handled by the keycloak.protect() middleware.


Single Sign On (SSO) with Keycloak

SSO works automatically with keycloak so usually nothing extra needs to be done in the server or client code. An example of SSO is as follows:

  1. User logs with his/her organizational credentials to one of your organization’s application.
  2. User opens your app (https://my-awesome-sauce-app.com) in a new tab/window: keycloak.protect() will automatically detect and use the same authentication session and user won’t be redirected to the login page as he/she had already authenticated with the IAM server in the other app.

Possible CORS issues and solution

From the above steps, it can be seen that the node.js server’s URL was white-listed by the IAM server. In case our web app is hosted separately from the node.js server, we will have issues with Keycloak because of the following flow:

  1. Client requests a URL from the server.
  2. Server uses keycloak.protect() to redirect the user to IAM’s login page.
  3. IAM server gives an error that origin ‘null’ has been blocked by CORS policy. This is the default redirection behaviour for protecting the identity of the eventual client in case of redirection. In case of redirection, the origin header is set to ‘null’ so that the identity of the actual requester is hidden from the server.

Now, to prevent this issue, we need to host the web app from within the same node.js server. This way, both our client as well as server will be part of the same white-listed URL domain and we won’t see the CORS issue.

Contribute!
If anybody has an alternate solution to the above problem, please post in the comments section.

Sample node.js code

To summarize whatever we learned above, i am sharing the node.js server code here (the code serves an Angular app from the ./public directory):

From the code you can infer the following:

  • All routes are protected with keycloak.protect() middleware.
  • The server handles the following routes:
‘/’: for serving the Angular web app
/logoff’: for logging out user.
  • The session details are stored in the server and only session id is stored at the client side.
  • The code uses memory store for storing sessions but it should not be used in production. For details about session options and storing sessions, see this link: https://github.com/expressjs/session.

User log-out handling

The log-out is automatically handled by Keycloak as it listens to the ‘/logout’ route by default. But i faced CORS issues while trying to call this route from the web client. To handle this, a new route ‘/logoff’ was introduced in the node.js code. Rest of the details are mentioned in the comments section of the above code and in a link given in the same section.

Node.js Code

The relevant code is shown again here for reference:

const HOST = 'my-awesome-sauce-app.com';
app.get('/logoff', keycloak.protect(), (req, res) => {
// CORS issue fix, reference link:
res.send('https://' + HOST + '/logout');
});

Angular Web Client Code

public logout() {
this.httpClient.get(environment.dprServerUri + '/logoff', { responseType: 'text' }).subscribe(url => {
window.location.href = url;
});
}

References

https://www.digitalocean.com/community/tutorials/an-introduction-to-oauth-2

https://www.keycloak.org/docs/3.3/securing_apps/topics/oidc/nodejs-adapter.html