Getting SessionID During Socket.io Authorization? - express

I'm trying to get the sessionID from express-session when a new WebSocket connection comes in from a user. I'm able to find the sessionID, I just have a question about its format.
When I make a HTTP request to my messenger page say I get 'X' as a sessionID, if I then made a WebSocket connection I can find the session ID 'AXB', the session ID X is in there, but also surrounded with other information.
var express = require('express');
var app = express();
var server = require('http').createServer(app);
var session = require('express-session');
var io = require('socket.io')(server);
var store = new MemoryStore({
checkPeriod: 86400000
});
app.use(session({
store: store,
secret: 'jpcs-0001080900DRXPXL',
saveUninitialized: false,
resave: true
}));
// ...
app.get('/messenger/:uid', authorizationRedirect, (req, res) => {
console.log(req.sessionID);
// prints "EIVUudPTckmojrkv6FN9Cdb5NAQq5oQU"
// ...
});
io.set('authorization', (data, accept) => {
if (data && data.headers && data.headers.cookie) {
console.log(data.headers.cookie);
cookies_str = data.headers.cookie;
cookies_arr = cookies_str.split(';');
cookies = {};
for (index in cookies_arr) {
cookie = cookies_arr[index].split('=');
key = cookie[0].replace(/ /g,'');
val = cookie[1];
cookies[key] = val;
}
sessionId = cookies['connect.sid'].split('.')[0];
console.log(sessionId);
// prints "s%3AEIVUudPTckmojrkv6FN9Cdb5NAQq5oQU.AQkvP..."
// ...
});
So basically, in io.set('authorization', ...) I get:
s%3AEIVUudPTckmojrkv6FN9Cdb5NAQq5oQU.AQkvPsfoxieH3EAs8laFWN28dr1C%2B9zIT%2BMXtKTRPBg
But in app.get('/...', ...) I get:
EIVUudPTckmojrkv6FN9Cdb5NAQq5oQU
You can notice that the string from socket.io does contain the session id in this format: "s%3A" + sessionID + ".xxxxxxxxxxx..."
So obviously I can get the sessionID from here, but I'm curious why the sessionID is shown like this when I get socket connections? Will it ALWAYS be shown like this regardless of browser, WebSocket implementations, etc? What does the other information contained mean? I mostly want to make sure that this is a reliable way to get the sessionID. Thanks!

I would first like to clarify that io.set('authorization',...) has been deprecated. Here's the updated version Documentation
So obviously I can get the sessionID from here, but I'm curious why the sessionID is shown like this when I get socket connections? Will it ALWAYS be shown like this regardless of browser, WebSocket implementations, etc?
It's not reserved for socket connections at all. That is simply how it is fixed on the browser. So yes, it will always be shown like that.
What does the other information contained mean? I mostly want to make sure that this is a reliable way to get the sessionID. (s%3AEIVUudPTckmojrkv6FN9Cdb5NAQq5oQU.AQkvPsfoxieH3EAs8laFWN28dr1C%2B9zIT%2BMXtKTRPBg)
The first three characters are just encoded, and I believe every sessionID containts that. DecodedURIComponent("s%3A") = "s:"
After that is the sessionID itself (EIVUudPTckmojrkv6FN9Cdb5NAQq5oQU)
Now, after the dot(AQkvPsfoxieH3EAs8laFWN28dr1C%2B9zIT%2BMXtKTRPBg) is the signature portion. That verifies the authenticity of the cookie and is actually given when you sign the cookie. AND yes, I would say it is a trusted and reliable way.

Related

Page stored using Cloudflare Cache API expires earlier than expected

I am developing a backend API using Cloudflare Workers to cache the tokens into respective individual pages, something like http://cache.api/[tokenId] with token value itself as body content, using Cache API.
const tokenId = 'fakeJWT';
const internalUrl = ''.concat(
'http://cache.api/',
tokenId // the query string
);
const cacheUrl = new URL(internalUrl);
const cacheKey = new Request(cacheUrl.toString());
const cache = caches.default;
let response = new Response(tokenId);
response.headers.append('Cache-Control', 's-maxage=86400'); // 24 hours
await cache.put(cacheKey, response.clone());
I've configured Cache-Control header with 24 hours expiry. Then, I wrote another API in the same Cloudflare Workers to check the existence of the cache, it exists after 10 minutes but does not exist after 15 minutes.
const internalUrl = ''.concat(
'http://cache.api/',
tokenId // the query string
);
const cacheUrl = new URL(internalUrl);
const cacheKey = new Request(cacheUrl.toString());
const cache = caches.default;
let response = await cache.match(cacheKey);
if (!response) {
console.log(
`Cache url for ${cacheUrl} is not present`
);
return unauthorised(`${pathName}: This user session has expired! cache url is ${cacheUrl}`);
}
else
{
let response = new Response(tokenId);
response.headers.append('Cache-Control', 's-maxage=86400'); // in seconds
await cache.put(cacheKey, response.clone());
}
Did I miss anything?
What may be happening is a cache miss rather than an expiration. When using Cloudflare for caching, you shouldn't expect caching to be guaranteed, in particular using the cache API the docs mention it being data-center specific (not propagated globally) -
https://developers.cloudflare.com/workers/runtime-apis/cache/#background
Caching should be mechanism for improving performance, but not relied upon for guaranteed storage in other words. If your use-case requires this, it would be better to use the Workers Key Value -
https://developers.cloudflare.com/workers/learning/how-kv-works/

Generate and validate session in node.js

What is a correct way to generate session and than validate it on every request?
Registration and initial authentication (user identity check for generating session) is handled by external service, so it's out of the question.
To simplify a question, what is the native secure way to generate and encrypt session with secret.
Requirements (alternatives is welcomed):
Session should be of two parts, one stored in cookies, second in database.
User check handled by server using database session part, cookies part and validate function.
Session generating and validating functions stored on server side and not accessible to user.
If database session part or functions is compromised, hacker couldn't make request pretending to be user. For this he will need to steal user cookies or session generate function and database session part.
Multiple device support with the same database session part.
JWT is not usable as logout is needed on server side (database session part will be deleted and all devices wouldn't be able to login with old cookies session part). User had some trust level that can change and it's will require JWT invalidation, so sessions is better choice.
I was thinking of using Crypto AES for this, but after asking "is it ok?" - answer was no, i'm not an expert in crypto, so i didn't fully understood a reason.
Here is my initial idea of implementation:
/**
* #param {string} data dummy
* #param {string} userKey from database or create new
* #return {object} {iv, key, encryptedData}
*/
function encrypt(data, userKey) {
let key = userKey ? Buffer.from(userKey, 'hex') : crypto.randomBytes(32)
let iv = crypto.randomBytes(16)
let cipher = crypto.createCipheriv('aes-256-cbc', Buffer.from(key), iv)
let encrypted = cipher.update(data)
encrypted = Buffer.concat([encrypted, cipher.final()])
return { iv: iv.toString('hex'), key: key.toString('hex'), encryptedData: encrypted.toString('hex') }
}
/**
* #param {string} iv
* #param {string} key
* #param {string} encryptedData
* #return {string} decrupted dummy data
*/
function decrypt(iv, key, encryptedData) {
try {
iv = Buffer.from(iv, 'hex')
key = Buffer.from(key, 'hex')
encryptedData = Buffer.from(encryptedData, 'hex')
let decipher = crypto.createDecipheriv('aes-256-cbc', key, iv)
let decrypted = decipher.update(encryptedData)
decrypted = Buffer.concat([decrypted, decipher.final()])
return decrypted.toString()
} catch (err) {
return false
}
}
A way to make it, is that user generate RSA and use crypto with public and private keys, but the public key must always be sent by the user('device')
'use strict';
const express = require('express');
const fs = require('fs');
const nodersa = require('node-rsa');
const bodyParser = require('body-parser');
let app = express();
app.use(bodyParser.urlencoded({ extended : false }));
app.use(bodyParser.json());
// req.body.value is public key, this never write in database or file
app.post('/', (req, res)=>{
let value = req.body.value;
const privateKey = fs.readFileSync('./store/privateKey.pem', 'utf8');
const original = new nodersa(privateKey).decrypt(value, 'utf8');
res.end(original);
});
app.listen(8000, ()=>{
console.log('on 8000');
});
if you use a public certificate authority with node, use aditional file called "certification file", node example here, this file is issue by C.A. you could work as C.A. and generarte this file, but It is recommended for closed systems, if you not need a gubernamental allow

Socket.io - Is there a way to save socketid to prevent a new one being generated

After a connection to the socket.io server a socket.id is given for the connection. When the socket connection has not been used after some time a new socket id is generated.
I have read a lot of tutorials that do a "hello world" connection that just gets you connected, but, there is not much literature on messaging peer-to-peer/group. The docs give a 3 line paragraph on rooms/namespaces and every question related to this is just given a link to the same 3 line paragraph.
I understand that you can create and object/array of chats(in this example). For this example, let's say it is an object. That Object looks something like this:
const connections = {
"randomSocketID1": {
recipient: "Mom",
messages: [Array of Objects]
//more information
}
}
I then send a message to randomSocketID1 --> 'Hello'. Then next day I want to send another message to "Mom". Is that socketID going to be the same OR AT LEAST will "randomSocketID1" be updated under the hood, to its updated ID(which sounds improbable)? Is the regeneration of the socketID a product of garbage collection or a socket/engine/websocket protocol?
thanks for any clarification
So I was still unable to find an actual answer to this and by the 0 responses i see that no one knows. So what I have done in order to make sure that user and socket id are maintained is whenever a user enters the component that connects to the socketio server an automatic 'update-user' is emitted and the back end then just finds the user and assigns it the value.
So I have something like this:
chat.component.ts:
ngOnInit(){
this.socket.emit('update-user', 'Ctfrancia');
}
then in the back end:
const users = {};
io.on('connection', socket => {
socket.on('update-user', user => {
if (user in users) users[user] = socket.id;
else users[user] = socket.id
});
});

Issues with dat project's hyperdb in browser with webrtc and signalhub

I'm trying to use hyperdb in browser with swarming via webrtc and signalhub. The code is pretty strait forward, but there is some issue with hyperdb replicate where the connecting is killed because of a sameKey check in hypercore. So, I'm thinking ... I'm not properly juggling my discovery keys and id keys so the peers know they should be sync'd. Here is some sample code, it is a bit of a mess but the relevant bits are the hyperdb initialization and the webrtc/signalhub stuff (I think) ... the key at the top is the discovery key of the other peer:
const crypto = require('crypto'),
sha = crypto.createHash('sha1'),
hyperdb = require('hyperdb'),
hyperdiscovery = require('hyperdiscovery'),
cms = require('random-access-idb')('cms'),
webrtc = require('webrtc-swarm'),
signalhub = require('signalhub'),
hyperdrive = require('hyperdrive'),
pump = require('pump');
// Discovery key of other peer/signalhub channel
var key = "cbffda913dabfe73cbd45f64466ffda845383965e66b2aef5f3b716ee6c06528";
const db = hyperdb(filename => {
return cms(filename);
}, { valueEncoding: 'utf-8' });
var DEFAULT_SIGNALHUBS = 'https://signalhub-jccqtwhdwc.now.sh';
db.on('ready', function () {
const swarm = webrtc(signalhub(key, DEFAULT_SIGNALHUBS));
swarm.on('peer', function (conn) {
console.log("PEER!!!!!!!");
const peer = db.replicate({
upload: true,
download: true
});
pump(conn, peer, conn)
});
});
I put up a working example here: https://github.com/joehand/hyperdb-web-example/blob/master/index.js
I think you are getting that error because you are not initializing the db with the key:
var db = hyperdb(storage, key)
Once you do that, you can get the discovery key. Generally, you don't need to be copying the discovery key around because that is always generated from the public key.
If that does not work, please include only the relevant code or a minimum example, so it is easier to debug =). Thanks!

How to determine timestamp of last API request by user in parse server?

Is there a way to determine the timestamp of the last app launch or log the timestamp the last API request from a user on parse server, on the server side without adding code to the client?
To do this without modifying the client, you could think of a request your clients always perform, like a find query and then set a hook on your server like this one from the docs.
Parse.Cloud.beforeFind('MyObject', function(req) {
let query = req.query; // the Parse.Query
let user = req.user; // the user
let triggerName = req.triggerName; // beforeFind
let isMaster = req.master; // if the query is run with masterKey
let isCount = req.count; // if the query is a count operation (available on parse-server 2.4.0 or up)
let logger = req.log; // the logger
let installationId = req.installationId; // The installationId
});
Then just persist the data you want somewhere like the user model and save it.