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

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
});
});

Related

Show private messages list of multiple users logged in at the same time

I've created a private messaging app where users login and can view their private messages. The problem that I am having is that is only shows the message list of one logged in user at a time. So let's say User A is logged in, it will show the chat list of user A. But then User B logs in, then both User A and User B will see the chat list of User B.
This is my server side call to fetch chats by user id:
Im using express for the backend
io.on('connection', socket => {
socket.on('findAllChatsByUserId', (userId) => {
socket.userId = userId
socket.join(socket.userId)
ChatModel.aggregate([{$match: {$or:[{senderId: userId},{receiver: userId}]}}, {$group: {_id: '$chatId', 'data': {$last: '$$ROOT'}}}]).exec(function(error, data) {
if (error) {
return error
} else {
data.sort(function (a, b) {
return b.data.date - a.data.date;
});
io.to(socket.userId).emit('findAllChatsByUserId', data);
}
})
})
});
And on the client side I do:
I am using VueJs on the FE
mounted () {
this.loading = true
this.socket.emit('findAllChatsByUserId', this.getUserId) // this calls the socket to get the chats for the given user Id
this.loading = false
},
I tried creating rooms by userId to make sure that only the data for a given user ID is passed in but it seems like only one user can use the socket at a time. I thought the rooms would solve this issue for me. Do I have to create a separate socket for each user? If so, how do I do that? I've followed the socket.io private messaging tutorial but they use 2 users talking to each other to explain the problem.
So I ended up solving this by doing:
io.to(socket.id).emit('findAllChatsByUserId', data);
instead of:
io.to(socket.userId).emit('findAllChatsByUserId', data);
So you use the "to" attribute to make sure the data you're sending is going to a particular socket, and you can find your specific socket by just calling socket.id (you don't have to set this, it gets set on its own. And the data will get emitted to whomever is on that specific socket.

Getting SessionID During Socket.io Authorization?

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.

RxJs: How to conditionally chain observable of BehaviorSubject?

I've got an observable data service (UserService) that returns the currently logged in user. I followed this tutorial - https://coryrylan.com/blog/angular-observable-data-services, which describes using a BehaviorSubject to return a default currentUser immediately, then emit the real currentUser once it's loaded or altered. The service is basically like this...
private _currentUser: BehaviorSubject<User> = new BehaviorSubject(new User());
public currentUser: Observable<User> = this._currentUser.asObservable();
constructor(private http: Http) {}
loadUser() { // app.component onInit and login component call this
return this.http.get('someapi.com/getcurrentuser')
.map(response => <User>this.extractData(response))
.do(
(user) => {
this.dataStore.currentUser = user;
this._currentUser.next(Object.assign(new User(), this.dataStore).currentUser);
},
(error) => this.handleError(error)
)
.catch(error -> this.handleError(error));
}
I'm having problems whenever a user hits F5 to reload the entire spa. When a consuming component subscribes to the currentUser on the UserService, it immediately receives a default user while the UserService waits for an api call to receive the actual user. The moment that api call finishes, the real user is emitted by UserService and all the subscribers get the real user. The first value emitted by the BehaviorSubject, however, is the default value and it always has an id of "undefined", so we can't make our next api call yet. In fact, when the real user comes through and I CAN make a valid call using the user.id, the chained subscription never happens and I don't get the values out of the response.
I know I'm doing something stupid, but I haven't figured out exactly what yet. I just stumbled across concatMap, but I'm not sure how to use it. While I pursue that, I'd like to know why the below code doesn't work. I particularly want to know why the subscribe never fires, even after the real user comes in, just to help my newbie understanding of Observables.
this.userService.currentUser
.flatMap((user) => {
this.user = user;
// Need to NOT call this if the user does not have an id!!!
this.someOtherService.getSomethingElse(user.id); // user.id is always undefined the first time
})
.subscribe((somethingElse) => {
// This never gets called, even after the real user is emitted by the UserService
// and I see the getSomethingElse() call above get executed with a valid user.id
this.somethingElse = somethingElse;
});
If you want to ignore user instances that do not have an id, use the filter operator:
import 'rxjs/add/operator/filter';
this.userService.currentUser
.filter((user) => Boolean(user.id))
.flatMap((user) => {
this.user = user;
this.someOtherService.getSomethingElse(user.id);
})
.subscribe((somethingElse) => {
this.somethingElse = somethingElse;
});
Regarding "why the subscribe never fires", it's likely due to an error arising from the undefined id. You only pass a next function to subscribe, so any errors will be unhandled. And if an error occurs, the observable will terminate and will unsubscribe any subscribers - as that is how observables behave - so any subsequent users with defined id properties will not be received.

How do you configure a client to auto-answer within vLine?

What is the correct method for setting a client to auto answer with the vLine API for WebRTC calls?
Looking at your comment, it looks like you have figured this out. But for completeness and for future reference I will go ahead and answer.
To auto answer a call, all you have to do is call MediaSession.start() when an incoming call comes in, instead of throwing a prompt to the user.
Here is an example snippet:
client.on('add:mediaSession', onAddMediaSession, self);
// Handle new media sessions
onAddMediaSession(event){
var mediaSession = event.target;
mediaSession.on('enterState:incoming', onIncoming, self);
},
// Handle new incoming calls and autoAccept
onIncoming(event){
var mediaSession = event.target;
// Auto Accept call instead of a prompt
mediaSession.start();
}
Note that you can do this in your code even if you are using the UI Widgets.

How should I persist CredentialPickerResults.Credential for CredentialPickerOptions.PreviousCredential?

I am wanting to use a CredentialPicker to prompt for a username and password. When I configure an instance of this class, I can set CredentialPickerOptions.PreviousCredential to a value previously obtained by CredentialPickerResults.Credential. I believe this causes the dialog to prepopulate the credentials.
However, persisting this value appears to be non-trivial; it's an IBuffer, whose members don't appear to contain the relevant credentials. Programming Windows 8 Apps with HTML, CSS, and JavaScript, page 657, implies that this should be possible:
An IBuffer containing the credential as an opaque byte array. This is what you can
save in your own persistent state if needs be and passed back to the picker at a later time; we’ll
see how shortly.
Unfortunately, the we'll see how shortly appears to only refer to the fact that the value can be passed back from memory into PreviousCredential; I didn't find any mention of how it's persisted.
Also, I want to persist the credentials using the recommended approach, which I believe is to use PasswordVault, however, this appears to only allow me to save the credentials as username and password strings rather than an IBuffer.
Thanks for taking the time to ask, and I certainly agree that I could've been more clear in that part of the book. Admittedly, I spent less time on Chapter 14 than I would have liked, but I'll try to remedy that in the next edition. Feedback like yours is extremely valuable in knowing where I need to make improvements, so I appreciate it.
Anyway, writing a buffer to a file is something that was mentioned back in Chapter 8 (and could've been mentioned again here...page 325, though it doesn't mention IBuffer explicitly). It's a straightforward job using the Windows.Storage.FileIO class as shown below (promise!).
First, a clarification. You have two ways to save the entered credentials. If you want to save the plain-text credentials, then absolutely use the Credential Locker. The benefit here is that those credentials can roam automatically with the user if that roaming passwords is enabled in PC Settings (it is by default). Otherwise, you can save the opaque CredentialPickerResults.credential property directly to a file. It's already encrypted and scrambled, so you don't need to use the credential locker in that case.
Now for saving/loading the credential property, which is an IBuffer. For this you use FileIO.writeBufferAsync to save and FileIO.readBufferAsync to reload.
I modified the Credential Picker sample, scenario 3 to demonstrate this. To save the credential property, I added this code at the end of the completed handler for pickAsync:
//results.credential will be null if the user cancels
if (results.credential != null) {
//Having retrieved a credential, write the opaque buffer to a file
var option = Windows.Storage.CreationCollisionOption.replaceExisting;
Windows.Storage.ApplicationData.current.localFolder.createFileAsync("credbuffer.dat", option).then(function (file) {
return Windows.Storage.FileIO.writeBufferAsync(file, results.credential);
}).done(function () {
//No results for this operation
console.log("credbuffer.dat written.");
}, function (e) {
console.log("Could not create credbuffer.dat file.");
});
}
Then I created a new function to load that credential, if possible. This is called on the Launch button click instead of launchCredPicker:
//In the page ready method:
document.getElementById("button1").addEventListener("click", readPrevCredentialAndLaunch, false);
//Added
function readPrevCredentialAndLaunch() {
Windows.Storage.ApplicationData.current.localFolder.getFileAsync("credbuffer.dat").then(function (file) {
return Windows.Storage.FileIO.readBufferAsync(file);
}).done(function (buffer) {
console.log("Read from credbuffer.dat");
launchCredPicker(buffer);
}, function (e) {
console.log("Could not reopen credbuffer.dat; launching default");
launchCredPicker(null);
});
}
//Modified to take a buffer
function launchCredPicker(prevCredBuffer) {
try {
var options = new Windows.Security.Credentials.UI.CredentialPickerOptions();
//Other options omitted
if (prevCredBuffer != null) {
options.previousCredential = prevCredBuffer;
}
//...
That's it. I put the modified JS sample on http://www.kraigbrockschmidt.com/src/CredentialPickerJS_modified.zip.
.Kraig
Author, Programming Windows 8 Apps in HTML, CSS, and JavaScript (free ebook)