I think passport.js is a amazing framework. Unfortunately, however, it seems to be that doesn't support socket. Why I said this is that Sails framework provides http and socket. When user connected sails's service through passport.js, it doesn't matter. Accessing by socket makes error. Because socket may not support middleware?
Anyway, the critical problem, I don't know how apply passport.js on socket.
Indeed, the websocket requests do not pass threw the passport middleware, but it is possible to use a workaround. Are you using this sails passport generator ?
I added this code to the passport policy to add passport methods to the socket requests.
/** Content not generated BEGIN */
var http = require('http')
, methods = ['login', 'logIn', 'logout', 'logOut', 'isAuthenticated', 'isUnauthenticated'];
/** Content not generated END */
module.exports = function (req, res, next) {
// Initialize Passport
passport.initialize()(req, res, function () {
// Use the built-in sessions
passport.session()(req, res, function () {
// Make the user available throughout the frontend
res.locals.user = req.user;
/** Content not generated BEGIN */
// Make the passport methods available for websocket requests
if (req.isSocket) {
for (var i = 0; i < methods.length; i++) {
req[methods[i]] = http.IncomingMessage.prototype[methods[i]].bind(req);
}
}
/** Content not generated END */
next();
});
});
};
Alexis gave the right answer... I think it's the way recommanded by Mike, regarding this message : https://stackoverflow.com/a/17793954/6793876
Just delete passport's mentions in config/http.js , make new policy passportMiddleware.js with the following content :
//passportMiddleware.js
var passport = require('passport');
var http = require('http');
module.exports = function (req, res, next) {
// Initialize Passport
passport.initialize()(req, res, function () {
// Use the built-in sessions
passport.session()(req, res, function () {
res.locals.user = req.user;
var methods = ['login', 'logIn', 'logout', 'logOut', 'isAuthenticated', 'isUnauthenticated'];
if (req.isSocket) {
for (var i = 0; i < methods.length; i++) {
req[methods[i]] = http.IncomingMessage.prototype[methods[i]].bind(req);
}
}
next();
});
});
};
And finally add this policy to all controllers, in policies.js :
module.exports.policies = {
RabbitController: {
nurture : ['passportMiddleware','isRabbitMother'],
feed : ['passportMiddleware','isNiceToAnimals', 'hasRabbitFood']
}
};
Related
I know there already are a lot of questions here like this, but none really helped me really since I'm a Junior Developer.
I am currently trying to completely just reproduce the following article: https://medium.com/devops-dudes/secure-front-end-react-js-and-back-end-node-js-express-rest-api-with-keycloak-daf159f0a94e
In there One tries to secure a Frontend with React JS and a Node.js Backend (Express Rest API) with Keylcoak.
But somehow if I try to start the node-microservices app my console keeps showing me "Initializing Keycloak" and if I try to access an endpoint in my browser it says:
TypeError: Cannot read property 'keycloak-token' of undefined
What did I do wrong?
Node-Microservices Code:
index.js
const express = require('express');
var router = express.Router();
var app = express();
const keycloak = require('./keycloak-config.js').initKeycloak();
app.use(keycloak.middleware());
router.get('/user', keycloak.protect('user'), function (req, res) {
res.send("Hello User");
});
router.get('/admin', keycloak.protect('admin'), function (req, res) {
res.send("Hello Admin");
});
router.get('/all', keycloak.protect(['user', 'admin']), function (req, res) {
res.send("Hello All");
});
app.get('/', function (req, res) {
res.send("Server is up!");
});
app.listen(8081);
keycloak-config.js
var session = require('express-session');
var Keycloak = require('keycloak-connect');
const chalk = require('chalk');
let keycloak;
var keycloakConfig = {
"realm": "Demo-Realm",
"bearer-only": true,
"auth-server-url": "http://localhost:8080/auth/",
"ssl-required": "external",
"resource": "node-microservice",
"verify-token-audience": true,
"use-resource-role-mappings": true,
"confidential-port": 0,
"realmPublicKey": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1SrR985UGLhBlCReW1p9ypcKbQhjHXDqS3DK78ihqGxLiwNWWsG/ilwD54HbxMjcVIl6hLXZbpd85oAr6HCSjRm8D6HGP6AVfva7s6pQcXmNWIeFGhwOSM/k2rjXkVGpCu3Yhg+Fnx03zA/nSlybhyYJrt/EftbjhCVO58WnAhEY8ytBHZQws+I85BzstClm3dSXj9EiWea6pqB7vpYkMy/nGUBgfOFn30Hqa2Pp7Dxkgd7+G/xRN2JDbg09etgZzt9kXVs1X6LbwAWG60qeNF2ZMCHTZeiHi0FRM7d7irRrc68orrFiEg2Gp7u3urr2ss4JOwSWe9QK/l3eZ3XS6QIDAQAB"
};
function initKeycloak() {
if(keycloak) {
console.log("Returning existing Keycloak instance!");
return keycloak;
}
else {
console.log("Initializing Keycloak...");
var memoryStore = new session.MemoryStore();
keycloak = new Keycloak({
store: memoryStore,
secret: 'any_key',
resave: false,
saveUnitialized: true
}, keycloakConfig);
return keycloak;
}
}
module.exports = {
initKeycloak
};
EDIT:
You need to do it like the solution the documentation proposes, as
otherwise the middleware will not find the keycloak connection.
--
I had a brief look on the medium article you linked and it seems like the article uses a complicated approach to securing your express routes.
Have a look at the Official documentation of the NodeJS adapter for Keycloak (keycloak-connect), it's much, much simpler than described in the article to configure and use that way. What the initKeycloak() is doing is unneccessarily mimicking the behaviour of the frontend keycloak.js, which indeed ships a initKeycloak method to initialize Keycloak authorization services at the frontend. For Node applications, just initialize keycloak once in your index.js and you're good to go.
I have my service running with injected Kubernetes secrets, that's why all the environment variables and am setting up keycloak at the top of my index.ts like that:
Example
import express from 'express';
import pgSession from 'connect-pg-simple';
import Keycloak from 'keycloak-connect';
const app = express();
const pgSessionInit = pgSession(session);
const pgSessionStore = new pgSessionInit({
pool: pool,
tableName: 'session',
schemaName: 'foo',
createTableIfMissing: true,
});
app.use(session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
store: pgSessionStore
}))
const keycloak = new Keycloak({
store: pgSessionStore
});
// RequestHandler and Route definitions after this point
On a side note: It helps us a lot to include stack traces, so that we can better understand your problem and point you in the right direction.
Node has an additional property on its Error object called "stack".
try{
// some code producing an Error
}catch(e: unknown){
if(e instanceof Error){
console.error(e, e.stack); // Retrieve Error Message and stack
}
}
I'm trying to setup my server with websockets so that when I update something via my routes I can also emit a websocket message when something on that route is updated.
The idea is to save something to my Mongo db when someone hits the route /add-team-member for example then emit a message to everyone who is connected via websocket and is a part of whatever websocket room that corresponds with that team.
I've followed the documentation for socket.io to setup my app in the following way:
App.js
// there's a lot of code in here which sets what to use on my app but here's the important lines
const app = express();
const routes = require('./routes/index');
const sessionObj = {
secret: process.env.SECRET,
key: process.env.KEY,
resave: false,
saveUninitialized: false,
store: new MongoStore({ mongooseConnection: mongoose.connection }),
secret : 'test',
cookie:{_expires : Number(process.env.COOKIETIME)}, // time im ms
}
app.use(session(sessionObj));
app.use(passport.initialize());
app.use(passport.session());
module.exports = {app,sessionObj};
start.js
const mongoose = require('mongoose');
const passportSocketIo = require("passport.socketio");
const cookieParser = require('cookie-parser');
// import environmental variables from our variables.env file
require('dotenv').config({ path: 'variables.env' });
// Connect to our Database and handle an bad connections
mongoose.connect(process.env.DATABASE);
// import mongo db models
require('./models/user');
require('./models/team');
// Start our app!
const app = require('./app');
app.app.set('port', process.env.PORT || 7777);
const server = app.app.listen(app.app.get('port'), () => {
console.log(`Express running → PORT ${server.address().port}`);
});
const io = require('socket.io')(server);
io.set('authorization', passportSocketIo.authorize({
cookieParser: cookieParser,
key: app.sessionObj.key, // the name of the cookie where express/connect stores its session_id
secret: app.sessionObj.secret, // the session_secret to parse the cookie
store: app.sessionObj.store, // we NEED to use a sessionstore. no memorystore please
success: onAuthorizeSuccess, // *optional* callback on success - read more below
fail: onAuthorizeFail, // *optional* callback on fail/error - read more below
}));
function onAuthorizeSuccess(data, accept){}
function onAuthorizeFail(data, message, error, accept){}
io.on('connection', function(client) {
client.on('join', function(data) {
client.emit('messages',"server socket response!!");
});
client.on('getmessage', function(data) {
client.emit('messages',data);
});
});
My problem is that I have a lot of mongo DB save actions that are going on in my ./routes/index file and I would like to be able to emit message from my routes rather than from the end of start.js where socket.io is connected.
Is there any way that I could emit a websocket message from my ./routes/index file even though IO is setup further down the line in start.js?
for example something like this:
router.get('/add-team-member', (req, res) => {
// some io.emit action here
});
Maybe I need to move where i'm initializing the socket.io stuff but haven't been able to find any documentation on this or perhaps I can access socket.io from routes already somehow?
Thanks and appreciate the help, let me know if anything is unclear!
As mentioned above, io is in your global scope. If you do
router.get('/add-team-member', (req, res) => {
io.sockets.emit('AddTeamMember');
});
Then every client connected, if listening to that event AddTeamMember, will run it's associated .on function on their respective clients. This is probably the easiest solution, and unless you're expecting a huge wave of users without any plans of load balancing, this should be suitable for the time being.
Another alternative you can go:
socket.io lib has a rooms functionality where you can join and emit using the io object itself https://socket.io/docs/rooms-and-namespaces/ if you have a knack for this, it'd look something like this:
io.sockets.in('yourroom').broadcast('AddTeamMember');
This would essentially do the same thing as the top, only instead of broadcasting to every client, it'd only broadcast to those that are exclusive to that room. You'd have to basically figure out a way to get that users socket into the room //before// they made the get request, or in other words, make them exclusive. That way you can reduce the amount of load your server has to push out whenever that route request is made.
Lastly, if neither of the above options work for you, and you just absolutely have to send to that singular client when they initiate it, then it's going to get messy, because you have to have some sort of id to that person, and since you have no reference, you'd have to store all your sockets upon connection, and then make a comparison. I do not fully recommend something like this, because well, I haven't ever tested it, and don't know what type of repercussions could happen, but here is a jist of an idea I had:
app.set('trust proxy', true)
var SOCKETS = []
io.on('connection', function(client) {
SOCKETS.push(client);
client.on('join', function(data) {
client.emit('messages',"server socket response!!");
});
client.on('getmessage', function(data) {
client.emit('messages',data);
});
});
router.get('/add-team-member', (req, res) => {
for (let i=0; i< SOCKETS.length; i++){
if(SOCKETS[i].request.connection.remoteAddress == req.ip)
SOCKETS[i].emit('AddTeamMember');
}
});
Keep in mind, if you do go down this route, you're gonna need to maintain that array when users disconnect, and if you're doing session management, that's gonna get hairy really really quick.
Good luck, let us know your results.
Yes, it is possible, you just have to attach the instance of socket.io as long as you get a request on your server.
Looking to your file start.js you just have to replace your functions as:
// Start our app!
const app = require('./app');
app.app.set('port', process.env.PORT || 7777);
const io = require('socket.io')(app.app);
const server = app.app.listen(app.app.get('port'), () => {
server.on('request', function(request, response){
request.io = io;
}
console.log(`Express running → PORT ${server.address().port}`);
});
now when you receive an event that you want to emit some message to the clients you can use your io instance from the request object.
router.get('/add-team-member', (req, res) => {
req.io.sockets.emit('addteammember', {member: 6});
//as you are doing a broadcast you just need broadcast msg
....
res.status(200)
res.end()
});
Doing that i also were able to integrate with test framework like mocha, and test the events emited too...
I did some integrations like that, and in my experience the last thing to do was emit the msg to instances in the socket.
As a good practice the very begining of middleware functions i had were doing data validation, data sanitization and cleaning data.
Here is my working example:
var app = require('../app');
var server = require('http').Server(app);
var io = require('socket.io')(server);
io.on('connection', function(client) {
client.emit('connected');
client.on('disconnect', function() {
console.log('disconnected', client.id);
});
});
server.on('request', function(request, response) {
request.io = io;
});
pg.initialize(app.config.DATABASEURL, function(err){
if(err){
throw err;
}
app.set('port', process.env.PORT || 3000);
var server1 = server.listen(app.get('port'), function(){
var host = 'localhost';
var port = server1.address().port;
console.log('Example app listening at http://%s:%s', host, port);
});
});
Your io is actually the socket object, you can emit events from this object to any specific user by -
io.to(userSocketId).emit('eventName', data);
Or you can broadcast by -
io.emit('eventName', data);
Just create require socket.io before using it :)
You can use emiter-adapter to emit data to client in other process/server. It use redis DB as backend for emitting messages.
I did something similar in the past, using namespaces.
Let's say your client connect to your server using "Frontend" as the namespace.
My solution was to create the instance of socket.io as a class in a separate file:
websockets/index.js
const socket = require('socket.io');
class websockets {
constructor(server) {
this.io = socket(server);
this.frontend = new Frontend(this.io);
this.io.use((socket, next) => {
// put here the logic to authorize your users..
// even better in a separate file :-)
next();
});
}
}
class Frontend {
constructor(io) {
this.nsp = io.of('/Frontend');
[ ... ]
}
}
module.exports = websockets;
Then in App.js
const app = require('express')();
const server = require('http').createServer(app);
const websockets = require('./websockets/index');
const WS = new websockets(server);
app.use('/', (req, res, next) => {
req.websocket = WS;
next();
}, require('./routes/index'));
[ ... ]
Finally, your routes can do:
routes/index.js
router.get('/add-team-member', (req, res) => {
req.websocket.frontend.nsp.emit('whatever', { ... });
[ ... ]
});
I have two servers
http://127.0.0.1:7777/
http://127.0.0.1:7778/
I'm trying to link the actions of these two servers.
For example, when I click on server 7778 I create a folder and put an action that detects the file create
var fs = require('fs');
if (fs.existsSync('/home/diegonode/Desktop/ExpressCart-master/New')) {
console.log("hello");
}
but I use an app like this and this command only works when it is included in router get
How can I link the actions of these two servers?
var express = require('express');
var router = express.Router();
var common = require('./common');
// The homepage of the site
router.get('/', function(req, res, next) {
var number_products = req.config.get('application').number_products_index ? req.config.get('application').number_products_index : 8;
req.db.products.find({ product_published: { $in: [ 'true' , 'true2'] }, }).limit(3).exec(function (err, results) {
res.render('index', {
title: 'Shop',
results: results,
session: req.session,
message: clear_session_value(req.session, "message"),
message_type: clear_session_value(req.session, "message_type"),
config: req.config.get('application'),
helpers: req.handlebars.helpers,
page_url: req.config.get('application').base_url,
show_footer: "show_footer"
});
});
});
please help me
I REFER how I can click action (or another type of action) of one server and detect this click in another server
I refer use router get from another server, not from the actual server and get I do a some action
router.get( ('http://127.0.0.1:7778/data '),
Consider the following code from an express app:
var express = require('express');
var router = express.Router();
var standupCtrl = require('../controllers/standup.server.controller');
/* GET home page. */
router.get('/', function(req, res) {
return standupCtrl.list(req, res);
});
/* POST filter by member name - home page. */
router.post('/', function(req, res) {
return standupCtrl.filterByMember(req, res);
});
// ............ more code here
module.exports = router;
exports.list = function(req, res) {
var query = Standup.find();
query.sort({ createdOn: 'desc'}).limit(12).exec(function(err, results){
res.render('index', {title: 'Standup - List', notes: results});
});
};
exports.filterByMember = function(req, res) {
var query = Standup.find();
var filter = req.body.memberName;
query.sort({ createdOn: 'desc' });
if (filter.length > 0)
{
query.where({ memberName: filter})
}
query.exec(function(err, results) {
res.render('index', { title: 'Standup - List', notes: results });
});
};
I know that when submitting a form you can specify a
method = get/post
For this scenario (where nothing such has been specified) , how does the server know which to trigger (post or get) when user navigates to '/' (e.g. Homepage)?
More generally, My question is:
What events trigger a Post/Get action (if not explicitly specified) ?
(PS: I know typing anything in the address bar of a browser triggers a GET request)
Thanks a lot in advance!
HTTP GET is pretty much the default everywhere if you don't specify otherwise.
Most POSTs are the result of either a form submit or an explicit call via AJAX or as a web service call (like RPC from another server). You CAN handcraft POST requests using programs like curl, but again that would be rare. In every toolkit I've seen, it's GET by default.
And rarely (VERY), you might find some RPC provider (such as an IoT or other embedded device) that only speaks POSTs (to save on code space).
I am not really sure what to title this, but I'm new to Node.js. I just found a neat REST API project on GitHub to implement but I'm not sure how I can split all GET and POST etc. to separate files.
I have one singular api.js file where I have
function API_ROUTER(router, connection, md5) {
var self = this;
self.handleRoutes(router, connection, md5);
}
API_ROUTER.prototype.handleRoutes = function(router, connection, md5) {
router.get("/", function(req, res) {
res.json({"Message" : "Hello World !"});
});
};
module.exports = API_ROUTER;
Now how can I create a sibling other.js and use:
var api = require('./api.js');
// Create router.get, router.post etc. here?
but I'm not sure how I can split all GET and POST etc. to separate files.
One way you can organize your routes would be to have a separate object for each route that has the handlers (separated by HTTP methods) and other needed info such as the path:
api/home.js
module.exports = {
path: '/',
handlers: {
'get': function(req, res) {
res.json({"Message" : "Hello World !"});
},
'post': {
// ...
}
// ...
}
}
api/other.js
module.exports = {
path: '/other',
handlers: {
'get': function(req, res) {
res.json({"Message" : "Other !"});
},
// ...
Then you can load all of these inside the handleRoutes method:
API_ROUTER.prototype.handleRoutes = function(router, connection, md5) {
var routes = ['home', 'other'];
routes.forEach(function(name) {
// load the current route object (NOTE: you should use the path module for determining file path in a cross-platform manner)
var routeObject = require('./' + name + '.js');
var apiPath = routeObject.path;
var handlers = routeObject.handlers;
var methods = Object.keys(handlers);
// assign handlers for each method
methods.forEach(function(method) {
router[method](apiPath, handlers[method]);
});
});
};
This will install all your routes with the appropriate information and handlers.
Now you can call this code by instantiating your API_ROUTER with the necessary data:
// initialize the api (and handle the routes internally)
var Api = new require('./api.js')(router, connection, md5);
If you implement a RESTful API, then you should keep in mind that this is just one way how you can provide data, and you might want to change it in future, as of that the API will most of the time only be a translation layer.
Normally you will split your code based on the resources, and the code that is handling the request won't have so much logic, it will just take the request and pass it to you internal API. For that purpose you not really need an additional layer if you already use express.js or a similar library.
In express the app.use([path,] function [, function...]), already provides the functionality you would need to modularize your code. For each resource your will create an own express.Router that itself also might mount another sub module. So for this part you do not really need a library.
When might a library be useful:
if it automatically translates thrown errors to the correct response codes
if it includes a tool to automatically create a documentation to your API
if it fully abstracts the underlaying routing system so that you can hook into express, hapi, ... without the need to change the code.
Here how a setup with express.js could look like
./lib/rest/customer.js
var customerSystem = require('../customer-system');
var express = require('express');
var router = new express.Router();
router.get('/:id', function(req, res, next) {
customerSystem.find({
id: req.params.id
}, function(err, customer) {
if (err) {
res.status( /*correct status code*/ ).send( /*depending on the api return json, xml, ....*/ )
} else {
res.send( /*depending on the api return json, xml, ....*/ )
}
})
});
router.delete('/:id', function(req, res, next) {
customerSystem.delete({
id: req.params.id
}, function(err) {
//...
});
});
router.post('/', function(req, res, next) {
//...
});
//save the customer id for the pass to the sub routers
router.use('/:id', function(req, res, next) {
req.customerId = req.params.id;
next();
});
router.use('/:id/addresses', require('./customer-address') )
module.exports = router;
./lib/rest/customer-address.js
var customerSystem = require('../customer-system');
var express = require('express');
var router = new express.Router();
router.get('/:id', function(req, res, next) {
customerSystem.find({
id: req.customerId
}, function(err, customer) {
// ...
})
});
/* ..... */
//save the address id for the pass to the sub routers
router.use('/:id', function(req, res, next) {
req.addressId = req.params.id;
next();
});
router.use('/:id/addresses', require('./customer-address') )
module.exports = router;