Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to setup public key for verifying JWT tokens from Keycloak?

Tags:

rsa

jwt

keycloak

I'm writing backend microservice that receives requests from front-end which have Authorisation: Bearer ... header, with token obtained from keycloak (which is inside docker container).

I got the RSA public key to verify the signature of that token from Keys section of realm settings, but it seems that when container with keycloak restarts, it regenerates pair of keys, and my public key set in service config becomes invalid.

What is the proper way to work with RSA public key from keycloak? Is there some way to configure it to use a fixed pair of keys for realm? Are keys exported when realm exports? Or I have to get the public key from keycloak using url like http://keycloak:8080/auth/realms/:realm_name:, which I rather not to do because this adds a dependency between keycloak and backend.

like image 892
Bunyk Avatar asked Nov 29 '18 16:11

Bunyk


1 Answers

You should verify the JWT token's signature based on the issuer identity server's /.well-known/jwks endpoint.

1) Query the issuer identity server's /.well-known/jwks endpoint (JWKS stands for JSON Web Key Set)

2) From the JWKS, get the JWK (JSON Web Key) with the same kid (Key ID) as the Bearer token we are verifying. To get the kid from your JWT token, first decode it using jwt.io's Debugger tool.

3) As long as identity server-issued tokens are verified with an asymmetric cryptography algorithm (e.g.: RS256), we can verify the signature with the Public Key only (so you won't need the Private Key)

4) The Public Key can be retrieved from the JWK (it is the x5c entry in the JWK JSON )

5) Verify the JWT Bearer token's signature with this Public Key.

For example, in Java you can verify it like this:

// verify JWT signature based on Access Identity's JWKS RSA public key (RS256)
try {

  Jwk jwk = new UrlJwkProvider(new URL(issuer + Constants.JWKS_ENDPOINT)).get(decodedJWT.getKeyId());
  final PublicKey publicKey = jwk.getPublicKey();

  if (!(publicKey instanceof RSAPublicKey)) {
    throw new IllegalArgumentException("Key with ID " + decodedJWT.getKeyId() + " was found in JWKS but is not a RSA-key.");
  }

  Algorithm algorithm = Algorithm.RSA256((RSAPublicKey) publicKey, null);
  JWTVerifier verifier = JWT.require(algorithm)
      .withIssuer(issuer)
      .build(); //Reusable verifier instance

  verifier.verify(bearerToken);

  LOGGER.info("Token verified!");

} catch (Exception e) {
  LOGGER.error(e.getMessage());
  throw new InvalidAccessTokenException("JWTVerificationException - Invalid token signature.");
}
like image 60
martoncsukas Avatar answered Oct 03 '22 01:10

martoncsukas