Dynamically protecting url using node.js Keycloak adapter - node.js

The code from here works for the basic case when urls are known in advance.
How to handle dynamic protection of urls by Keycloak after middleware was added and express app started?
I was thinking about handling reading new urls from file by some node.js module which will emit the event and then the code below would handle the event. In the code of event handler, the call to app.all('/new url', keycloak.protect()) will be added.
I tried that but it doesn't work as expected because of the app.use('/lap', [some_midleware]) is before the new app.all('/new url', keycloak.protect()
The only way i think of is modifying app._router.stack by inserting the new middleware before the some_midleware
var Keycloak = require('keycloak-connect');
var hogan = require('hogan-express');
var express = require('express');
var session = require('express-session');
var fs = require()
var app = express();
var server = app.listen(3000, function () {
var host = server.address().address;
var port = server.address().port;
console.log('Example app listening at http://%s:%s', host, port);
});
var memoryStore = new session.MemoryStore();
app.use(session({
secret: 'mySecret',
resave: false,
saveUninitialized: true,
store: memoryStore
}));
var keycloak = new Keycloak({
store: memoryStore
});
app.use(keycloak.middleware({
logout: '/logout',
admin: '/',
protected: '/protected/resource'
}));
app.all('/url', keycloak.protect())
app.all('*', [some_midleware])

From what I understand, you want to dynamically add new routes that get handled before the last route in your example (app.use('*', ...)).
You could do that with a separate router:
app.all('/url', keycloak.protect())
const router = express.Router();
app.use(router);
app.use('*', [some_midleware])
Then, to add new route handlers, you'd add them to router, not app:
router.get('/new url', keycloak.protect());
Because router is added before app.use('*', ...), it will always get to handle requests first. Only if requests don't match any handlers will be pass the request on the handler on the last line.

Related

keycloak-connect : type=PERMISSION_TOKEN_ERROR, userId=null, ipAddress=, error=invalid_client_credentials

Context
Hi everyone, I am working on a 3 part application:
Keycloak server for auth
Angular app for the frontend (with dedicated client, public)
express router for the backend (with dedicated client, bearer-only)
The first two parts work like a charm, I can authenticate against my KC server with my Angular app, get the roles, and so on..
This week, I started to work with the backend, installed keycloak-connect following the official documentation, and others, to troubleshoot my current issue.
This is the condensed code I am trying:
var express = require('express');
var session = require('express-session');
var Keycloak = require('keycloak-connect');
var memoryStore = new session.MemoryStore();
var keycloak = new Keycloak({ store: memoryStore });
var app = express();
//session
app.use(session({
secret:'thisShouldBeLongAndSecret',
resave: false,
saveUninitialized: true,
store: memoryStore
}));
app.use( keycloak.middleware() );
app.get('/check', keycloak.checksso('user'), function(req, res){
console.log(req.headers);
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({ 'this': 'works!' }, null, 3));
});
app.get('/check', keycloak.enforce('user'), function(req, res){
console.log(req.headers);
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({ 'this': 'does not work' }, null, 3));
});
app.listen(8000, function () {
console.log('Listening at http://localhost:8000');
});
Beside I have a .json file provided by my KC server after I setup the bearer-only client, so this one should be just fine.
The issue
Note that, using .checkSSO() works as excepected, while using protect('my-role') or enforce('my-role') does not.
My express backend got the Angular token properly, and I checked for the role my-role using https://www.jstoolset.com/jwt service, and everything is fine.
I have enabled LOGLEVEL on my KC server, I got nothing at all using protect(), but I do got a message using enforce():
keycloak-connect : type=PERMISSION_TOKEN_ERROR, userId=null, ipAddress=, error=invalid_client_credentials
This is weird to me, as the dedicated client I am using for my backend, is configure as a bearer-only, and there is no place for such a secret within the configuration file my KC server is providing me...
I think I won't move further without your help, I was browsing all the week tried many leads, without luck so far...
Thank you for your help if any, kind strangers...

Enabling SSL certificate on Mongo using Mongoose and Express

I can't figure out how to add an SSL certificate to my server.js so I can access my API on the server through https.
var express = require('express'),
cors = require('cors'),
app = express(),
port = process.env.PORT || 3000,
mongoose = require('mongoose'),
Task = require('./api/models/todoListModel'), //created model loading here
bodyParser = require('body-parser'),
helmet = require('helmet');
// Test SSL connection
var MongoClient = require('mongodb').MongoClient;
// mongoose instance connection url connection
mongoose.Promise = global.Promise;
mongoose.connect('mongodb://localhost/Tododb'); // was tododb
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
// adding Helmet to enhance your API's security
app.use(helmet());
// enabling CORS for all requests
app.use(cors());
app.get('/', (req, res) => res.send('Hello World!'))
var routes = require('./api/routes/todoListRoutes'); //importing route
routes(app); //register the route
app.listen(port);
console.log('Supporter RESTful API server started on: ' + port);
I have tried mongoose.connect('mongodb://localhost/Tododb&ssl=true'); but I don't really know what to do after that? I understand I have to add a reference to the key and certificate files that I have generated but I can't figure how I add those to the connection string.
I have been attempting to follow some of this https://docs.mongodb.com/manual/reference/connection-string/
What's the next step?
The next step is to provide ConnectionOptions to the mongoose.connect call. The mongoose documentation (at https://mongoosejs.com/docs/connections.html#options) specifies that it will pass on ssl specific options to the underlying MongoClient. The options for the MongoClient can be found at: http://mongodb.github.io/node-mongodb-native/2.2/api/MongoClient.html#connect
The ones you are interrested in are likely sslCert, sslKey, and maybe sslPass (along with some more of the ssl* settings). Note that the ones I listed here might require you to load in a file in code yourself. You might be able to use something like:
var fs = require('fs');
var options = {
...
sslCert: fs.readFileSync(path.join(__dirname, <relative path to the cert>))
};
mongoose.connect('mongodb://localhost/Tododb', options);

How to fix: Cannot read property 'keycloak-token' of undefined in node js?

I have created realm and client. keycloak json is placed in root folder. still i'm getting the error like,
Cannot read property 'keycloak-token' of undefined
TypeError: Cannot read property 'keycloak-token' of undefined at SessionStore.get (C:\Users\...\node_modules\keycloak-connect\stores\session-store.js:24:58)
var session = require('express-session');
var Keycloak = require('keycloak-connect');
var memoryStore = new session.MemoryStore();
var keycloak = new Keycloak({ store: memoryStore });
You get this error when you set app.use(keycloak.middleware()) and don't configure the session store. The keycloak-connect library is trying to read a keycloak-token value from the session that hasn't been configured. You can circumvent the error by supplying an Authorization header for example Authorization: Bearer 123 but the solution when using a session store is to configure it.
For a complete example see node_modules/keycloak-connect/example/index.js in your project's dependencies. A minimal example with resource protection using multiple middlewares in the route handler below.
Be advised however, that:
MemoryStore, is purposely not designed for a production environment. It will leak memory under most conditions, does not scale past a single process, and is meant for debugging and developing.
const express = require('express')
const app = express()
const session = require('express-session');
const Keycloak = require('keycloak-connect');
var memoryStore = new session.MemoryStore();
var keycloak = new Keycloak({ store: memoryStore });
// Configure session
app.use(session({
secret: 'mySecret',
resave: false,
saveUninitialized: true,
store: memoryStore
}));
// Attach middleware
app.use(keycloak.middleware());
// Attach route handler for home page
app.get('/', keycloak.protect(), (req, res, next) => {
res.json({status: 'ok'})
})
// Start server
app.listen(3005)

Express and redis session keeps returning undefined

I've been having problems trying to access stored session values! Once I've set the values and try access them from a new route, I get undefined! So basically I've got a login (POST) and in that request I set the session data, and then I have a show user details (POST) where I try and access the session data I've just stored.
Setup
// Setup express and needed modules #############################################
var express = require('express'),
session = require('express-session'),
cookieParser = require('cookie-parser'),
redis = require("redis"),
redisStore = require('connect-redis')(session),
bodyParser = require('body-parser');
var client = redis.createClient(), //CREATE REDIS CLIENT
app = express();
// Setup app
app.use(cookieParser('yoursecretcode'));
app.use(session(
{
secret: 'x',
store: new redisStore({
port: 6379,
client: client
}),
saveUninitialized: true, // don't create session until something stored,
resave: false // don't save session if unmodified
}
));
app.use(bodyParser.urlencoded({ extended: true}));
app.use(bodyParser.json());
app.set('trust proxy', 1) // trust first proxy
So as you've seen my setup, you know I'm using express sessions and Redis. Below is where I'm setting the session values! If I print out the session values here it works, but then If I try and access the session data in another route it returns undefined.
Routes
I send a http post request and set the session data:
router.route('/login/').post(function(req, res) {
req.session.userId = req.body.uId;
req.session.name = req.body.uName;
// THIS PRINTS OUT IF I TRY AND ACCESS THE SESSION DATA HERE
console.log("THIS PRINTS OUT --> " + req.session.name);
});
So now that the session values have been set, I can go access them right, no, I get undefined each time I try and log them out.
router.route('/user/printoutuserdetails').post(function(req, res) {
// THESE RETURN UNDEFINED
console.log(req.session.userId);
console.log(req.session.uName);
console.log("THIS PRINTS OUT --> " + req.session.name);
});
Does anyone have any idea what's happening? I've tried everything and looked everywhere and can't seem to find a way to get it to work!
Solved:
The reason this wasn't was because you're not suppose to use sessions when using a RESTFUL api.

How to retrieve SessionID in NodeJS with multiple servers?

I'm new to NodeJS. I am developing a REST API and using express-session to deal with sessions. So, to get the session ID I'm using
var sessionID = req.sessionID
This sessionID is generated from the server side. So, when I scale up to two or more servers, this is a problem. For example, if one server shuts down and the request is redirected to another server (Assuming I have a load balancer), a new session ID is generated. So, is there a way to retrieve the session ID from the client side?
Good question! Session management can be challenging to get up and running with - especially since to get up and running with any sort of sophisticated session management in node you need a ton of different packages, each with their own set of docs. Here is an example of how you can set up session management with MongoDB:
'use strict';
var express = require('express'),
session = require('express-session'),
cookieParser = require('cookie-parser'),
mongoStore = require('connect-mongo')(session),
mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/someDB');
var app = express();
var secret = 'shhh';
app.use(session({
resave: true,
saveUninitialized: true,
secret: secret,
store: new mongoStore({
mongooseConnection: mongoose.connection,
collection: 'sessions' // default
})
}));
// ROUTES, ETC.
var port = 3000;
app.listen(port, function() {
console.log('listening on port ' + port + '.')
});
This configuration gives you access to req.sessionID but now it should persists across app servers if the user's session cookie has not expired.
I hope this works!

Resources