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.
Look at the keycloak.protect() function. Use it to authenticate your route.
router.get('/users',keycloak.protect(),function(req, res, next) {
});
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
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);
});