Keycloak: Access token validation end point

basith picture basith · Jan 16, 2018 · Viewed 16.3k times · Source

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?

Answer

kfrisbie picture kfrisbie · Jun 26, 2018

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