I'm having problems while trying to parse back signed cookies in express/connect application.
io.set('authorization', function (handshakeData, callback) {
if(handshakeData.headers.cookie) {
var signedCookies = cookie.parse(decodeURIComponent(handshakeData.headers.cookie));
handshakeData.cookie = connect.utils.parseSignedCookies(signedCookies, secret);
} else {
return accept('No cookie transmitted', false);
}
callback(null, true); // error first callback style
});
What happens is call to connect.utils.parseSignedCookies returns empty object. I looked into source for parse function and found out that it calls unsign method which gets a substring of encoded value and then tries to sign it again with the same secret and compare the results to verify that its the same value encoded and for some reasons it fails and values does not match. I don't know what I'm doing wrong, why those values differs and why I'm unable to get correct session ID.
My app initialization code looks like this:
app.use(express.cookieParser(secret));
app.use(express.session({
key: 'sessionID',
secret: secret,
maxAge: new Date(Date.now() + 3600000),
store: new RedisStore({
client: redisClient
})
}));
Please help and point what I'm doing wrong here. Thank you
The cookie parser is a middleware, so we have to use it like one. It will actually populate the object that you pass to it. This is how you would want to be using the parser:
// we need to use the same secret for Socket.IO and Express
var parseCookie = express.cookieParser(secret);
io.set('authorization', function(handshake, callback) {
if (handshake.headers.cookie) {
// pass a req, res, and next as if it were middleware
parseCookie(handshake, null, function(err) {
// use handshake.signedCookies, since the
// cookie parser has populated it
});
} else {
return accept('No session.', false);
}
callback(null, true);
});
The cookie parser API changed and this is what it looks like now:
module.exports = function cookieParser(secret) {
return function cookieParser(req, res, next) {
if (req.cookies) return next();
var cookies = req.headers.cookie;
req.secret = secret;
req.cookies = {};
req.signedCookies = {};
if (cookies) {
try {
req.cookies = cookie.parse(cookies);
if (secret) {
req.signedCookies = utils.parseSignedCookies(req.cookies, secret);
req.signedCookies = utils.parseJSONCookies(req.signedCookies);
}
req.cookies = utils.parseJSONCookies(req.cookies);
} catch (err) {
err.status = 400;
return next(err);
}
}
next();
};
};
So what we're doing is passing handshake as a request object, and the parser will read the headers.cookie property. Then, the cookies will be parsed, and put into req.signedCookies. Since we passed handshake as req, the cookies are now in handshake.signedCookies. Note that the cookies are only signed because you passed a secret to the parser.
I was having problems left and right with cookies/sessions/socket.io etc. It was finally #vytautas comment that helped me. In case anyone sees this, please make sure you're connecting to the correct host, whether you have it setup as localhost or an IP address or what have you. Otherwise you won't be able to parse your incoming cookies.
(Seems kind of obvious in hindsight.)
Related
My former server.js is like:
After running the server I could see my index.html
var connect = require('connect');
var serveStatic = require('serve-static');
connect().use(serveStatic(__dirname)).listen(5000, '192.168.xx.xx', function(){
console.log('Server running on 5000');
});
I want to create http login and password to secure the website, so I found online the information of http module: if I put right login and password, I could see congratulations message:
var http = require('http');
var server = http.createServer(function(req, res) {
// console.log(req); // debug dump the request
// If they pass in a basic auth credential it'll be in a header called "Authorization" (note NodeJS lowercases the names of headers in its request object)
var auth = req.headers['authorization']; // auth is in base64(username:password) so we need to decode the base64
console.log("Authorization Header is: ", auth);
if(!auth) { // No Authorization header was passed in so it's the first time the browser hit us
// Sending a 401 will require authentication, we need to send the 'WWW-Authenticate' to tell them the sort of authentication to use
// Basic auth is quite literally the easiest and least secure, it simply gives back base64( username + ":" + password ) from the browser
res.statusCode = 401;
res.setHeader('WWW-Authenticate', 'Basic realm="Secure Area"');
res.end('<html><body>Need authorization</body></html>');
}
else if(auth) { // The Authorization was passed in so now we validate it
var tmp = auth.split(' '); // Split on a space, the original auth looks like "Basic Y2hhcmxlczoxMjM0NQ==" and we need the 2nd part
var buf = new Buffer(tmp[1], 'base64'); // create a buffer and tell it the data coming in is base64
var plain_auth = buf.toString(); // read it back out as a string
console.log("Decoded Authorization ", plain_auth);
// At this point plain_auth = "username:password"
var creds = plain_auth.split(':'); // split on a ':'
var username = creds[0];
var password = creds[1];
if((username == 'admin') && (password == 'admin')) { // Is the username/password correct?
res.statusCode = 200; // OK
res.end('<html><body>Congratulations, feel free to explre!</body></html>');
}
else {
res.statusCode = 401; // Force them to retry authentication
res.setHeader('WWW-Authenticate', 'Basic realm="Secure Area"');
// res.statusCode = 403; // or alternatively just reject them altogether with a 403 Forbidden
res.end('<html><body>You shall not pass</body></html>');
}
}
});
server.listen(5000, function() { console.log("Server Listening on http://localhost:5000/"); });
I am new to nodejs, I want to know how to combine this 2 js? In order to realize my function of adding authorization to my web.
Could I do something to show my index instead of showing congratulation message after putting the login and password?
Thanks a lot.
In order to show HTML page instead of congratulation message, you can follow these steps:
Get request path by req.url, such as / or /introduction.html.
According to the above path, read the corresponding HTML file in server disk, using fs.readFile().
Return HTML file content to browser if the read is successful. Otherwise, return 404 error page.
Here is some example code for above steps:
if((username == 'admin') && (password == 'admin')) { // Is the username/password correct?
res.statusCode = 200; // OK
// res.end('<html><body>Congratulations, feel free to explre!</body></html>');
var requestURL = req.url; // e.g. / or /a or /a.html
var requestFilePath = getFilePathFromRequestURL(requestURL); // you need to implement this logic yourself, such as "/" mapping to "./index.html"
fs.readFile(requestFilePath, function(error, data) {
if (error) {
res.statusCode = 404;
res.write('File not found.');
} else {
res.statusCode = 200;
res.write(data);
}
res.end();
});
}
However, unless you want to write some low-level node.js code to better understand this language, I highly recommend using node.js web framework such as Express. Serve HTTP request using low-level node.js would be tedious, especially in production code.
Also, please note that using WWW-Authenticate Basic for authentication is neither secure nor user-friendly. You need some other way to implement authentication, such as JSON Web Tokens
I am new to Nodejs and using Visual Studio 2015 with Basic Nodejs Express 4 app template.
Setting some values through a post request from client. Then I open another tab and send another post request. The values I set on previous request persists. I would like to set variables for each connected clients.
Nodejs Code
var someValue = 0;
app.post('/set-values', function (req, res, next) {
var reqBody = req.body;
someValue += parseInt(reqBody.someValue);
res.send(JSON.stringify({ value:someValue }));
});
Client Code
$.post("http://localhost:1337" + "/set-values", { someValue: 5 }, function (d) {
var data = JSON.parse(d);
console.log(data.value);
});
When the client makes request, console value is 5 then on another request it is 10 then on another it is 15...
I would like to have 5 for all requests and keep someValue property 0 when each connection starts but i also need it outside of the post scope. Every search send me pages about socket.io client or user authentication.
You should generate a key for identity request.
If we assuming you will do this on client-side, your code should be like below:
if(typeof(Storage) !== "undefined") {
var val = Math.random().toString(36).substring(7);
localStorage.setItem("key", val);
} else {
alert("web storage not supported")
}
$.post("http://localhost:1337" + "/set-values", { someValue: 5, key:localStorage.getItem("key") }, function(d) {
var data = JSON.parse(d);
console.log(data.value);
});
And your server-side:
var dataStore = [];
app.post('/set-values', function (req, res,next) {
if(dataStore[req.body.key] == undefined) dataStore[req.body.key] = 0;
dataStore[req.body.key] += parseInt(req.body.someValue);
res.send(JSON.stringify({ value:dataStore[req.body.key] }));
})
In this case we used HTML5 LocalStorage, You can try jQuery Cookie or another something.
Recently I started learning a little bit about Node.js and it's capabilities and tried to use it for some web services.
I wanted to create a web service which will serve as a proxy for web requests.
I wanted my service to work that way:
User will access my service -> http://myproxyservice.com/api/getuserinfo/tom
My service will perform request to -> http://targetsite.com/user?name=tom
Responded data would get reflected to the user.
To implement it I used the following code:
app.js:
var express = require('express');
var bodyParser = require('body-parser');
var app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
var proxy = require('./proxy_query.js')
function makeProxyApiRequest(name) {
return proxy.getUserData(name, parseProxyApiRequest);
}
function parseProxyApiRequest(data) {
returned_data = JSON.parse(data);
if (returned_data.error) {
console.log('An eror has occoured. details: ' + JSON.stringify(returned_data));
returned_data = '';
}
return JSON.stringify(returned_data);
}
app.post('/api/getuserinfo/tom', function(request, response) {
makeProxyApiRequest('tom', response);
//response.end(result);
});
var port = 7331;
proxy_query.js:
var https = require('https');
var callback = undefined;
var options = {
host: 'targetsite.com',
port: 443,
method: 'GET',
};
function resultHandlerCallback(result) {
var buffer = '';
result.setEncoding('utf8');
result.on('data', function(chunk){
buffer += chunk;
});
result.on('end', function(){
if (callback) {
callback(buffer);
}
});
}
exports.getUserData = function(name, user_callback) {
callback = user_callback
options['path'] = user + '?name=' + name;
var request = https.get(options, resultHandlerCallback);
request.on('error', function(e){
console.log('error from proxy_query:getUserData: ' + e.message)
});
request.end();
}
app.listen(port);
I wish I didn't screwed this code because I replaced some stuff to fit my example.
Anyway, the problem is that I want to post the response to the user when the HTTP request is done and I cant find how to do so because I use express and express uses asynchronous calls and so do the http request.
I know that if I want to do so, I should pass the makeProxyApiRequest the response object so he would be able to pass it to the callback but it is not possible because of asyn problems.
any suggestions?
help will be appreciated.
As you're using your functions to process requests inside your route handling, it's better to write them as express middleware functions, taking the specific request/response pair, and making use of express's next cascade model:
function makeProxyApiRequest(req, res, next) {
var name = parseProxyApiRequest(req.name);
res.locals.userdata = proxy.getUserData(name);
next();
}
function parseProxyApiRequest(req, res, next) {
try {
// remember that JSON.parse will throw if it fails!
data = JSON.parse(res.locals.userdata);
if (data .error) {
next('An eror has occoured. details: ' + JSON.stringify(data));
}
res.locals.proxyData = data;
next();
}
catch (e) { next("could not parse user data JSON."); }
}
app.post('/api/getuserinfo/tom',
makeProxyApiRequest,
parseProxyApiRequest,
function(req, res) {
// res.write or res.json or res.render or
// something, with this specific request's
// data that we stored in res.locals.proxyData
}
);
Even better would be to move those middleware functions into their own file now, so you can simply do:
var middleware = require("./lib/proxy_middleware");
app.post('/api/getuserinfo/tom',
middleware.makeProxyApiRequest,
middleware.parseProxyApiRequest,
function(req, res) {
// res.write or res.json or res.render or
// something, with this specific request's
// data that we stored in res.locals.proxyData
}
);
And keep your app.js as small as possible. Note that the client's browser will simply wait for a response by express, which happens once res.write, res.json or res.render etc is used. Until then the connection is simply kept open between the browser and the server, so if your middleware calls take a long time, that's fine - the browser will happily wait a long time for a response to get sent back, and will be doing other things in the mean time.
Now, in order to get the name, we can use express's parameter construct:
app.param("name", function(req, res, next, value) {
req.params.name = value;
// do something if we need to here, like verify it's a legal name, etc.
// for instance:
var isvalidname = validator.checkValidName(name);
if(!isvalidname) { return next("Username not valid"); }
next();
});
...
app.post("/api/getuserinfo/:name", ..., ..., ...);
Using this system, the :name part of any route will be treated based on the name parameter we defined using app.param. Note that we don't need to define this more than once: we can do the following and it'll all just work:
app.post("/api/getuserinfo/:name", ..., ..., ...);
app.post("/register/:name", ..., ..., ... );
app.get("/api/account/:name", ..., ..., ... );
and for every route with :name, the code for the "name" parameter handler will kick in.
As for the proxy_query.js file, rewriting this to a proper module is probably safer than using individual exports:
// let's not do more work than we need: http://npmjs.org/package/request
// is way easier than rolling our own URL fetcher. In Node.js the idea is
// to write as little as possible, relying on npmjs.org to find you all
// the components that you need to glue together. If you're writing more
// than just the glue, you're *probably* doing more than you need to.
var request = require("request");
module.exports = {
getURL: function(name, url, callback) {
request.get(url, function(err, result) {
if(err) return callback(err);
// do whatever processing you need to do to result:
var processedResult = ....
callback(false, processedResult);
});
}
};
and then we can use that as proxy = require("./lib/proxy_query"); in the middleware we need to actually do the URL data fetching.
I'm attempting to create a simple web service using node.js, express, monk, and mongodb which returns results from mongodb based on the params in the URL. I want to add jsonp support to the calls. The service will be called as such:
localhost:3000/database/collection/get?param1=Steve¶m2=Frank&callback=foo
app.js
var mongo_address = 'localhost:27017/database';
var db = monk(mongo_address);
app.get('/:coll/get', routes.handle(db);
routes/index.js
exports.handle = function(db) {
return function(req, res) {
// Send request
db.get(req.params.coll).find(req.query, {fields:{_id:0}}, function(e,docs) {
if (e) throw e;
res.jsonp(docs)
});
};
}
When I use the built in JSONP support with res.jsonp, it sends the callback param to mongo and returns an empty list. I've tried stripping out the callback param during the query and then manually adding it back to the results without much luck. I feel like I'm missing something simple. Any help would be appreciated.
After some messing around with JS, I found a workable solution with minimal additional code. AFter stripping the callback from the query and storing the function value, I had to explicitly build the return string for JSONP requests.
exports.handle = function(db) {
return function(req, res) {
//Determine if URL implements JSONP
var foundCallback = req.query.callback;
var callbackVar;
//If asking for JSONP, determine the callback function name and delete it from the map
if (foundCallback){
callbackVar = req.query.callback;
delete req.query.callback
}
// Send request
db.get(req.params.coll).find(req.query, {fields:{_id:0}}, function(e,docs) {
if (e) throw e;
//If callback, send function name and query results back, else use express JSON built in
if (foundCallback)
res.send('typeof ' + callbackVar + ' === \'function\' && ' + callbackVar + '(' + JSON.stringify(docs) + ');');
else
res.json(docs);
});
};
}
Try
app.set("jsonp callback", true);
I have set up my express/connect app to use sessions with redis as the store:
app.use(express.session({
secret: "secret key here",
store: app.redisStore,
cookie: { maxAge: 600000 }
}));
I have created my own flash message system by doing the following:
module.exports = function (app) {
'use strict';
//set-up session messages
app.use(function (req, res, next) {
/* set up flash*/
if (req.session.flash === "undefined") {
req.session.flash = [];
}
res.locals.messages = function () {
var messages = req.session.flash;
//clear out messages
req.session.flash = [];
return messages;
};
...
Then essentially I just push flash message objects into the flash array whenever I need them. Every time the message function is used they get cleared out. This seems to work for the most part; however, while logging out I use the regenerate function and flash becomes undefined:
function logout(req, res) {
var currentUser = req.session.currentUser;
req.session.regenerate(function (err) {
req.session.flash.push({"type": "info", "message": "You have been logged out."});
console.log(currentUser + " Logged Out");
res.redirect("/login");
return;
});
}
Which seems to make sense. Regenerate obliterates the session, and since it happens after the initial request flash becomes undefined. To avoid any future problems like this I am wondering if there is there some sort of initialize function for sessions that I can override or hook into? I'd use this to set some default session values every time a session is started or regenerated.
Side-question: Is flash actually getting saved in redis?
To avoid any future problems like this I am wondering if there is
there some sort of initialize function for sessions that I can
override or hook into? I'd use this to set some default session values
every time a session is started or regenerated.
No, the way you are doing this is correct. If you want to attach multiple actions to regenerate method, then you can also try something like this:
var EventEmitter = require('events').EventEmitter;
var session_events = new EventEmitter( );
module.exports = function (app) {
'use strict';
//set-up session messages
app.use(function (req, res, next) {
var regenerate = req.session.regenerate;
req.session.regenerate = function() {
session_events.emit( "session_regenerated", req );
regenerate.apply( req.session, arguments );
};
/* set up flash*/
if (req.session.flash === "undefined") {
session_events.emit( "session_initializing", req );
}
// other code goes here
});
}
Then you can simply do:
session_events.on( "session_regenerated", function(req) {
req.session.flash.push({"type": "info", "message": "You have been logged out."});
});
session_events.on( "session_initializing", function(req) {
req.session.flash = [];
});
This is a monkey patch, though ( at least the .regenerate overriding ), so I advice writing your own session store, which is not difficult at all, since session is nothing else then an entry in Redis/any other storage.
Side-question: Is flash actually getting saved in redis?
Yes, everything attached to session goes to session store ( Redis in your case ).