Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Keycloak: Access token validation end point

Running keycloak on standalone mode.and created a micro-service by using node.js adapter for authenticating api calls.

jwt token from the keyclaok is sending along with each api calls. it will only respond if the token sent is a valid one.

  • how can i validate the access token from the micro service?
  • is there any token validation availed by keycloak?
like image 291
basith Avatar asked Jan 16 '18 04:01

basith


3 Answers

To expand on troger19's answer:

Question 1: How can I validate the access token from the micro service?

Implement a function to inspect each request for a bearer token and send that token off for validation by your keycloak server at the userinfo endpoint before it is passed to your api's route handlers.

You can find your keycloak server's specific endpoints (like the userinfo route) by requesting its well-known configuration.

If you are using expressjs in your node api this might look like the following:

const express = require("express");
const request = require("request");

const app = express();

/*
 * additional express app config
 * app.use(bodyParser.json());
 * app.use(bodyParser.urlencoded({ extended: false }));
 */

const keycloakHost = 'your keycloak host';
const keycloakPort = 'your keycloak port';
const realmName = 'your keycloak realm';

// check each request for a valid bearer token
app.use((req, res, next) => {
  // assumes bearer token is passed as an authorization header
  if (req.headers.authorization) {
    // configure the request to your keycloak server
    const options = {
      method: 'GET',
      url: `https://${keycloakHost}:${keycloakPort}/auth/realms/${realmName}/protocol/openid-connect/userinfo`,
      headers: {
        // add the token you received to the userinfo request, sent to keycloak
        Authorization: req.headers.authorization,
      },
    };

    // send a request to the userinfo endpoint on keycloak
    request(options, (error, response, body) => {
      if (error) throw new Error(error);

      // if the request status isn't "OK", the token is invalid
      if (response.statusCode !== 200) {
        res.status(401).json({
          error: `unauthorized`,
        });
      }
      // the token is valid pass request onto your next function
      else {
        next();
      }
    });
  } else {
    // there is no token, don't process request further
    res.status(401).json({
    error: `unauthorized`,
  });
});

// configure your other routes
app.use('/some-route', (req, res) => {
  /*
  * api route logic
  */
});


// catch 404 and forward to error handler
app.use((req, res, next) => {
  const err = new Error('Not Found');
  err.status = 404;
  next(err);
});

Question 2: Is there any token validation availed by Keycloak?

Making a request to Keycloak's userinfo endpoint is an easy way to verify that your token is valid.

Userinfo response from valid token:

Status: 200 OK

{
    "sub": "xxx-xxx-xxx-xxx-xxx",
    "name": "John Smith",
    "preferred_username": "jsmith",
    "given_name": "John",
    "family_name": "Smith",
    "email": "[email protected]"
}

Userinfo response from invalid valid token:

Status: 401 Unauthorized

{
    "error": "invalid_token",
    "error_description": "Token invalid: Token is not active"
}

Additional Information:

Keycloak provides its own npm package called keycloak-connect. The documentation describes simple authentication on routes, requiring users to be logged in to access a resource:

app.get( '/complain', keycloak.protect(), complaintHandler );

I have not found this method to work using bearer-only authentication. In my experience, implementing this simple authentication method on a route results in an "access denied" response. This question also asks about how to authenticate a rest api using a Keycloak access token. The accepted answer recommends using the simple authentication method provided by keycloak-connect as well but as Alex states in the comments:

"The keyloak.protect() function (doesn't) get the bearer token from the header. I'm still searching for this solution to do bearer only authentication – alex Nov 2 '17 at 14:02

like image 128
kfrisbie Avatar answered Nov 19 '22 08:11

kfrisbie


There are two ways to validate a token:

  • Online
  • Offline

The variant described above is the Online validation. This is of course quite costly, as it introduces another http/round trip for every validation.

Much more efficient is offline validation: A JWT Token is a base64 encoded JSON object, that already contains all information (claims) to do the validation offline. You only need the public key and validate the signature (to make sure that the contents is "valid"):

There are several libraries (for example keycloak-backend) that do the validation offline, without any remote request. Offline validation can be as easy as that:

token = await keycloak.jwt.verifyOffline(someAccessToken, cert);
console.log(token); //prints the complete contents, with all the user/token/claim information...

Why not use the official keycloak-connect node.js library (and instead use keycloak-backend)? The official library is more focused on the express framework as a middleware and does (as far as I have seen) not directly expose any validation functions. Or you can use any arbitrary JWT/OICD library as validation is a standardized process.

like image 39
Markus Avatar answered Nov 19 '22 08:11

Markus


I would use this UserInfo endpoint for that, with which you can also check other attributes like email as well as what you defined in mappers. You must send access token in header attributes with Bearer Authorization : Bearer access_token

http://localhost:8081/auth/realms/demo/protocol/openid-connect/userinfo

like image 7
troger19 Avatar answered Nov 19 '22 08:11

troger19