Secure NestJs Rest API with Keycloak

Image for post
Image for post

Step-by-step guide to secure Rest API build with NestJs using Keycloak.

I am assuming you already have a JS frontend app or at least a HTTP client that perform the authentication against Keycloak and is in possession of a JWT and can pass in the header to your NestJS backend.

If you want to get an idea on how to secure ReactJS front-end using Keycloak and send the authenticated JWT from front-end to back-end, you can checkout my previous article Secure Front end (React.js) and Back end (Node.js/Express Rest API) with Keycloak

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.

Image for post
Image for post

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.

Image for post
Image for post
Add realm

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

Image for post
Image for post

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

Image for post
Image for post

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.

Image for post
Image for post

Here we have to register out NestJs application as a Keycloak client in Keycoak server.

Client : nest-app

Image for post
Image for post

For the created client set the Access Type as bearer-only

Image for post
Image for post

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. Mostly used in OAuth client credential flow.
  • 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.

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 nest-app client
Image for post
Image for post
Client admin role
Image for post
Image for post
Client user role
Image for post
Image for post

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

Image for post
Image for post
Realm admin role
Image for post
Image for post
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 nest-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.

Image for post
Image for post

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

Image for post
Image for post

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

Image for post
Image for post

2. Go to Credentials tab to set user credentials.

Image for post
Image for post

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

Image for post
Image for post

Like above assign app-user role to User2 and app-user, app-admin roles to User3

Application Configuration (NestJs)

I am assuming that you have basic knowledge on how to create a NestJs application with basic setup. We are using nest-keycloak-connect adapter to communicate with Keycloak server.

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.

Implementation

  • Add nest Keycloak library to our code base
npm install nest-keycloak-connect --save
  • Now we need to add Keycloak configuration to NestJs. Here we are adding configuration to the app.module.ts. But if you want you can create a separate module for Keycloak configuration and import that module in app.module.ts.
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import {
KeycloakConnectModule,
ResourceGuard,
RoleGuard,
AuthGuard,
} from 'nest-keycloak-connect';
import { APP_GUARD } from '@nestjs/core';
@Module({
imports: [
KeycloakConnectModule.register({
authServerUrl: 'http://localhost:8080/auth',
realm: 'Demo-Realm',
clientId: 'nest-app',
secret: '83790b4f-48cd-4b6c-ac60-451a918be4b9',
// Secret key of the client taken from keycloak server
}),
],
controllers: [AppController],
providers: [
AppService,
// This adds a global level authentication guard,
// you can also have it scoped
// if you like.
//
// Will return a 401 unauthorized when it is unable to
// verify the JWT token or Bearer header is missing.
{
provide: APP_GUARD,
useClass: AuthGuard,
},
// This adds a global level resource guard, which is permissive.
// Only controllers annotated with @Resource and
// methods with @Scopes
// are handled by this guard.
{
provide: APP_GUARD,
useClass: ResourceGuard,
},
// New in 1.1.0
// This adds a global level role guard, which is permissive.
// Used by `@Roles` decorator with the
// optional `@AllowAnyRole` decorator for allowing any
// specified role passed.
{
provide: APP_GUARD,
useClass: RoleGuard,
},
],
})
export class AppModule {}
  • Here we are using nest-keycloak-connect’s AuthGuard, ResourceGuard, RoleGuard to protect our endpoints.
  • Now we add following endpoints to our controller and start the application using npm start.
@Controller()
export class UserController {
constructor(private readonly userService: UserService) {}
@Get()
getpublic(): string {
return `${this.userService.getHello()} from public`;
}
@Get('/user')
getUser(): string {
return `${this.userService.getHello()} from user`;
}
@Get('/admin')
getAdmin(): string {
return `${this.userService.getHello()} from admin`;
}
@Get('/all')
getAll(): string {
return `${this.userService.getHello()} from all`;
}
}
  • You can try to access those endpoints from postman or a browser. You can see you are getting {“statusCode”:401,”message”:”Unauthorized”} from those endpoints.
  • To give access those endpoints we can use following annotations.
@Controller()
export class UserController {
constructor(private readonly userService: UserService) {}
@Get('/public')
@Unprotected()
getpublic(): string {
return `${this.userService.getHello()} from public`;
}
@Get('/user')
@Roles('user')
getUser(): string {
return `${this.userService.getHello()} from user`;
}
@Get('/admin')
@Roles('admin')
getAdmin(): string {
return `${this.userService.getHello()} from admin`;
}
@Get('/all')
@AllowAnyRole()
getAll(): string {
return `${this.userService.getHello()} from all`;
}
}
  • This is the extracted JWT received from frontend application from User1 we created when configuring Keycloak. In the resource access section you can see this that user has user client role and in the realm access section user has app-user realm role. We can also use realm role in the @Roles annotation. e.g : @Roles(‘realm:app-user’). In above example we used client role.
Image for post
Image for post
  • Now we can access the /user endpoint with postman with JWT attached in the Authorization header.
Image for post
Image for post
  • But if we try to access /admin endpoint we are getting 403 because User1 doesn’t have admin role.
Image for post
Image for post
  • We can access access the /all endpoint since it’s allowed for any roles.
Image for post
Image for post

Volla, we have successfully secured our NestJs REST API using Keycloak.

You can find the code from my Github repository.

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 Secure Coding ❤

Written by

Senior Full-Stack Engineer | Java | Spring boot | ReactJs | NodeJs | NestJs | Microservices

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store