6
var loadData = function () {

var url = 'http://localhost:3000/users';

var req = new XMLHttpRequest();
req.open('GET', url, true);
req.setRequestHeader('Accept', 'application/json');
req.setRequestHeader('Authorization', 'Bearer ' + keycloak.token);

req.onreadystatechange = function () {
    if (req.readyState == 4) {
        if (req.status == 200) {
            console.log('Success');
        } else if (req.status == 403) {
            console.log('Forbidden');
        }
    }
}

req.send();  
};

Above is my front end code requesting the REST API and passing the keycloak token in the authorization header which will be needed for authentication at the node js server side.

Now I wanted to know how to secure my Rest Api using Keycloak and authenticate it on the basis of token received from the front end and tell whether the authentic user is requesting the rest api resource or not.

I have created a rest api in node js and used keycloak-connect npm packge. I have mapped the nodejs middleware with keycloak middleware.

var express = require('express');
var router = express.Router();
var app = express();
var Keycloak = require('keycloak-connect');
var keycloak =new Keycloak();

app.use( keycloak.middleware( {
logout: '/logout',
admin: '/',
} ));

router.get('/users',function(req, res, next) {
var token=req.headers['authorization']; //Access token received from front end

//Now how to authenticate this token with keycloak???

});

I have also included the keycloak.json file in the root folder of my project.

Saransh Kumar
  • 65
  • 1
  • 1
  • 6

3 Answers3

1

Take a look at my answer here which outlines how to verify that a token (provided by a client request) is valid in your node REST API by sending it to Keycloak's userinfo route.

This solution suggests:

Implementing 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.

Code example using Node.js/Express:

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);
});
kfrisbie
  • 826
  • 10
  • 18
  • A link to a potential solution is always welcome, but please add context around the link so your fellow users will have some idea what it is and why it’s there. Always quote the most relevant part of an important link, in case the target site is unreachable or goes permanently offline. – awh112 Jun 26 '18 at 17:01
  • Could also [validate JWT](https://www.keycloak.org/docs/latest/securing_apps/index.html#validating-access-tokens) using realm's public key after receiveing bearer token in Authorization header. This could prevent lots of additional HTTP requests. – danialk Jan 02 '20 at 20:58
0

Look at the keycloak.protect() function. Use it to authenticate your route.

router.get('/users',keycloak.protect(),function(req, res, next) {

});
rckrd
  • 3,124
  • 1
  • 13
  • 23
  • By just adding keycloak.protect() , will it automatically authenticate my bearer access token ? – Saransh Kumar Feb 24 '17 at 17:45
  • I have not used bearer auth only, but do think so. – rckrd Feb 24 '17 at 17:47
  • Try the user mailing list: https://lists.jboss.org/mailman/listinfo/keycloak-user – rckrd Feb 24 '17 at 18:15
  • 3
    No, the keyloak.protect() function do not get the bearer token from the header. I'm still searching for this solution to do bearer only authentification – alex Nov 02 '17 at 14:02
0

It would seem that the nodejs 4.0.0.1 beta middlware expects a full object called request.kauth which contains the full payload.

http://lists.jboss.org/pipermail/keycloak-user/2017-February/009719.html

return function protect (request, response, next) {
    if (request.kauth && request.kauth.grant) {*   // Line 2*
      if (!guard || guard(request.kauth.grant.access_token, request,
response)) {
        return next();
      }

      return keycloak.accessDenied(request, response, next);
    }

I'm not sure where or what the encoding decoding occurs. Seems like its missing in the docs.

https://issues.jboss.org/browse/KEYCLOAK-4687

Jack Murphy
  • 2,952
  • 1
  • 30
  • 49
  • weird - its looking into the memoryStore on the nodejs layer for session info - see Keycloak.prototype.getGrant – Jack Murphy Apr 04 '18 at 22:25