DEV Community

loading...

Express-jwt and Keycloak: how I didn't use Keycloak's official library

nurgasemetey profile image nurgasemetey ・2 min read

Problem

We have many microservices that run on multiple deployments. I wanted to add security by using Keycloak with the help of JWT.

Solution

One of the earliest solution was to use Keycloak Js Adapter. Yet, Keycloak JS adapter requires following:

var keycloakConfig = {
    clientId: 'nodejs-microservice',
    bearerOnly: true,
    serverUrl: 'http://localhost:8080/auth',
    realm: 'Demo-Realm',
    credentials: {
        secret: '62c99f7c-da55-48fb-ae4e-a27f132546b7'
    }
};
Enter fullscreen mode Exit fullscreen mode

which seems cumbersome way of doing this.


I thought there must be more simple way, I just wanted to validate requests.

That's why I liked Spring Boot approach which is:

  • include package
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
Enter fullscreen mode Exit fullscreen mode
  • add one line config
spring.security.oauth2.resourceserver.jwt.issuer-uri = http://localhost:8080/auth/realms/appsdeveloperblog
Enter fullscreen mode Exit fullscreen mode

At start, it fetches makes request to issuer-uri which has response like this

{
  "realm": "appsdeveloperblog",
  "public_key": "...",
  "token-service": "http://localhost:8080/auth/appsdeveloperblog/master/protocol/openid-connect",
  "account-service": "http://localhost:8080/realms/appsdeveloperblog/account",
  "tokens-not-before": 0
}
Enter fullscreen mode Exit fullscreen mode

and stores public_key which is used to validate JWT tokens. It doesn't make request each time to verify JWT.
As result, any request is validated and working out of box.


So I wanted to replicate this on NodeJS.

I started with express-jwt and simple example was like this

var jwt = require('express-jwt');

app.get('/protected',
  jwt({ secret: 'shhhhhhared-secret', algorithms: ['HS256'] }),
  function(req, res) {
    if (!req.user.admin) return res.sendStatus(401);
    res.sendStatus(200);
  });

//Or with public key, shortened

var publicKey = fs.readFileSync('/path/to/public.pub');
jwt({ secret: publicKey, algorithms: ['RS256'] });

Enter fullscreen mode Exit fullscreen mode

However it was problem for us to provide public key because

  • we have multiple deployments
  • each deployment has its own Keycloak.

We couldn't maintain this so I decided to implement like in Spring Boot.

With the help sync-request package:

const res = request('GET', 'http://localhost:8080/auth/realms/appsdeveloperblog');
const response = JSON.parse(res.getBody().toString());
const publicKey = `-----BEGIN PUBLIC KEY-----\r\n${response.public_key}\r\n-----END PUBLIC KEY-----`;

app.use(jwt({ secret: publicKey, algorithms: ['RS256'] }));
Enter fullscreen mode Exit fullscreen mode

I achieved on-start fetch of public key without cumbersome settings on NodeJS.

Discussion (0)

pic
Editor guide