Secure Frontend (React.js) and Backend (Node.js/Express Rest API) with Keycloak

Chamith Madusanka
Aug 1, 2020 · 10 min read

Step-by-Step guide to secure application build with React front end and Node back end (Express Rest API) using Keycloak.

Steps

  1. User trying to login to the front-end application (React.js).
  2. User will be redirected to the Keycloak server for authentication.
  3. If authentication successful, Authenticated user will be redirected to the application. Meantime user will get a JWT (JSON Web Token).
  4. Unauthenticated user will be redirected to back to the keycloak login page.
  5. With the valid JWT, front-end can access back-end rest api (Node.js/Express.js) by sending the JWT along with the service request.
  6. Back-end will communicate with the Keycloak server to validate the token.
  7. If the token is valid, back-end will server the request accordingly.
  8. If the token is invalid, back-end will respond with error code 401 (Unauthorized).

Keycloak

Keycloak is an open source Identity and Access Management solution aimed at modern applications and services. It makes it easy to secure applications and services with little to no code. Keycloak uses open protocol standards like Open ID Connect or SAML 2.0, especially in Identity Federation and SSO scenarios.

Authentication with Keycloak brings to the table virtually every feature you might want regarding user authentication and authorization. Some of these include

  • Single sign-on and sign-out, with possible integration with Kerberos (LDAP or Active Directory),
  • Support for OpenID Connect and SAML 2.0,
  • Log in via social media,
  • User account management via both the web console and REST API,
  • Fine-grained authorization for different services.

How does Keycloak work?

Applications are configured to point to and be secured by this server. Browser applications redirect a user’s browser from the application to the Keycloak authentication server where they enter their credentials. This is important because users are completely isolated from applications and applications never see a user’s credentials. Applications instead are given an identity token or assertion that is cryptographically signed. These tokens can have identity information like username, address, email, and other profile data. They can also hold permission data so that applications can make authorization decisions. These tokens can also be used to make secure invocations on REST-based services.

Keycloak Configuration

1. Set up Keycloak server

There are multiple ways to setup Keycloak instance. Follow the instructions in the link. Once the setup completed you should login to the Keycloak server using provided admin account credentials.

2. Create a Realm

A realm secures and manages security meta data for a set of users, applications, and registered OAuth clients. Users can be created confined to a specific realm within the Administration console. Roles can be defined at the realm level. You can also set up user role mappings to assign these permissions to specific users.

Create a realm by clicking the add realm button on the Select realm drop down. Give a name with your preference and click the Create button.

Add realm

After that you will be redirected to the realm setting page.

Realm settings

Note : You can change access token and refresh token lifespan by moving to token tab.

Realm token settings

Make sure Demo-Realm is selected for the below configurations. Avoid using the master realm. You don’t have to create the realm every time. It’s a one time process.

3. Create Clients

Clients are entities that can request Keycloak to authenticate a user. Most often, clients are applications and services that want to use Keycloak to secure themselves and provide a single sign-on solution. Clients can also be entities that just want to request identity information or an access token so that they can securely invoke other services on the network that are secured by Keycloak.

Clients tab allows you to manage your application clients.

Here we have two applications. Front-end and back-end. We have to create two clients to manage those application.

Front-end : react-web-app
Back-end : node-microservice

For the front-end client (react-web-app) we set Access Type to public and Redirect URL to application url.

For the back-end client (node-microservice) we set Access Type to bearer-only.

Access Types explained,

  • Bearer-only — this is for services that rely solely on the bearer token included in the request and never initiate login on their own. It’s typically used for securing the back-end.
  • Confidential — clients of this type need to provide a secret in order to initiate the login process.
  • Public — since we have no real way of hiding the secret in a JS-based browser app, this is what we need to stick with.

Valid Redirect URIs — this is the URI pattern (one or more) which the browser can redirect to after completing the login process.

You can change access token lifespan of any client using the Advanced Settings drop down.

4. Create Roles

Roles identify a type or category of user. Admin, user, manager and employee are all typical roles that may exist in an organization. Applications often assign access and permissions to specific roles rather than individual users as dealing with users can be too fine grained and hard to manage. For example, the Admin Console has specific roles which give permission to users to access parts of the Admin Console UI and perform certain actions. There is a global namespace for roles and each client also has its own dedicated namespace where roles can be defined.

Realm Roles: Realm-level roles are a global namespace to define your roles. You can see the list of built-in and created roles by clicking the Roles left menu item.
Client Roles: Client roles are basically a namespace dedicated to a client. Each client gets its own namespace. Client roles are managed under the Roles tab under each individual client. You interact with this UI the same way you do for realm-level roles.

  1. Create client roles admin and user for react-web-app client
Client admin role
Client user role

2. Create realm roles app-admin and app-user for react-web-app client

Realm admin role
Realm user role

Composite Roles: Any realm or client level role can be turned into a composite role. A composite role is a role that has one or more additional roles associated with it. When a composite role is mapped to the user, the user also gains the roles associated with that composite. This inheritance is recursive so any composite of composites also gets inherited.

3. After saving the realm roles enable Composite Roles and search for client react-web-app in Client Roles field. Select admin role and click Add selected. This configuration will assign react-web-app, admin client role to the app-admin realm role. If you have multiple clients with multiple roles, pick and choose the required roles from each client to create realm roles based on the need.

4. Similarly add user client role to app-user realm role.

5. Follow above steps to create client roles, realm roles and composite roles for the node-microservice client.

5. Create Users

Users are entities that are able to log into your system. They can have attributes associated with themselves like email, username, address and phone number. They can be assigned to groups and have specific roles assigned to them.

  1. Create three users and assign them following realm roles,
  • User1 with app-user
  • User2 with app-admin
  • User3 with app-user, app-admin

Go to Users in left side menu to create users

2. Go to Credentials tab to set user credentials.

3. From the Role Mappings tab assign required roles to the Users.

User 1
User 2
User 3

6. Create Client Scope

If you have many applications you need to secure and register within your organization, it can become tedious to configure role scope mappings for each of these clients. Keycloak allows you to define a shared client configuration in an entity called a client scope.

If we want to get client roles in a custom key in the JWT token, we have to add client scope to put client roles in access token.

  1. Go to Client Scopes and click create button to create new scope.

2. Go to Mappers tab and click Create to create a new mapper for the react-web-app client. Add below configurations.

Here Token Claim Name is the field we have to mention the name of the key in JWT token.

3. Now move to client react-web-app to add the custom scope we created. Select the client_roles_react_web_app scope and click Add selected.

Now we can get the client roles form the JWT token with roles key which was mentioned above. We can use these roles to build UI rendering logic in our front-end application. We can enable/disable view to particular user roles by using the client roles we received form the token.

Front-end Application Configuration (React.js)

Keycloak Client Adapers : Keycloak client adapters are libraries that make it very easy to secure applications and services with Keycloak. We call these ‘adapters’ rather than libraries as they provide a tight integration to the underlying platform and framework. This makes adapters easy to use and require less boilerplate code than what is typically required by a library.

  1. Install keycloak-js dependency to the react app.
    npm install keycloak-js
  2. We can implement our authentication logic using the standard Keycloak APIs init method call which returns a promise containing authentication details.

login-required is one of two possible values to be passed as an onLoad parameter. This will authenticate the client if the user has already logged into Keycloak, or redirect the browser to the login page if he hasn’t. The other option is check-sso : this will only authenticate the client if the user has already logged in, otherwise the client will remain unauthenticated without automatic redirection.

Here we need to store the access token received from authenticated Keycolak object to call back-end Rest APIs. We can put it into either global variable or local storage. We are proceeding with the global variable scenario.

4. After user authenticated to the react-web-app, For each Back-end API call we have to send the access token in the header. Let’s add it to an interceptor.

add the token stored in the global variable

3. Get the client related info JSON from the Keycloak server and put it into the public/keycloak.json

That’s it for the front-end application configuration.

Back-end Application Configuration (Node.js/Express.js)

  1. Install keycloak-connect and express-session dependencies to your project.
    npm install keycloak-connect
    npm install express-session
  2. Create new file keycloak-config.js to put node.js keycloak configurations.
keycloak-config.js

3. Add the role-based access for APIs in index.js

index.js

/user
This APIs should be accessible only to users with the role user in both react-web-app & node-microservice clients.

/admin
This APIs should be accessible only to users with the role admin in both react-web-app & node-microservice clients.

/all
This APIs should be accessible to users with the role admin or user in both react-web-app & node-microservice clients.

That’s it for the back-end configuration.

Now you can try to login to the react-web-app using one of the users we created. After successful authentication you will be provided with a JWT token. It contains all the information (username, client roles, realm roles etc.) to communicate with back-end. When you are requesting resources from node-microservice, back-end will communicate with Keycloak server and verify that the access token is valid. It also checks whether the user requesting the resource has the correct permission to access the resource.

Thank you for reading this post. If you do have any questions please add as a comment. I will definitely answer your questions. If you like this post, give a Cheer!!!

Happy Coding ❤

References:
Keycloak official website: https://www.keycloak.org/

DevOps Dudes

Stories for anything and everything DevOps

DevOps Dudes

A collection of stories that have anything and everything to do with DevOps from horror stories to success stories. If it's about Gitlab, Jenkins, Chef, Ansible, AWS, Azure, Kubernetes, Software Engineer then it belongs here.

Chamith Madusanka

Written by

Senior Full-Stack Engineer | Java | Spring boot | ReactJs | NodeJs | NestJs | Microservices | https://www.linkedin.com/in/chamith24/

DevOps Dudes

A collection of stories that have anything and everything to do with DevOps from horror stories to success stories. If it's about Gitlab, Jenkins, Chef, Ansible, AWS, Azure, Kubernetes, Software Engineer then it belongs here.