Authenticate a rest api using keycloak access toke

2020-06-23 06:54发布

问题:

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.

回答1:

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

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

});


回答2:

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



回答3:

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);
});