How to distinguish connected users in WebSocket? - express

I'm trying to create an event in express.js, where connected users will be sent to the client.
My goal is that in client side user can see who belongs that particular message sent from.
As it describes in docs and as I could also run in localhost, broadcasting messages works fine.
wss.on("connection", function connection(ws) {
ws.on("message", function message(data) {
wss.clients.forEach(function each(client) {
if (client !== ws && client.readyState === WebSocket.OPEN) {
client.send(data);
}
});
});
});
So before sending messages to client, there should be another event where connected username should be sent, so that in client side can be defined who that particular message belongs.
My idea is to create users object which supposed to keep track of all connected users like here.
const users = {};
wss.on("connection", function connection(ws) {
ws.on("userName", function message(userName) {//this doesn't work
const user = {
name: userName,
id: uuid(), // some unique id, no idea if websocket has or not
};
users[id]= user
ws.send("user", user) // current user who belongs the message to
ws.send("users", users) // users to show connected users list
});
ws.on("message", function message(data) {
wss.clients.forEach(function each(client) {
if (client !== ws && client.readyState === WebSocket.OPEN) {
client.send(data);
}
});
});
});
user event doesn't work, it is just my idea but not sure if how to do it correctly.
Any help will be appreciated.

Related

Vue + MSAL2.x + Azure B2C Profile Editing

First, I am not finding Vue specific examples using MSAL 2.x and we'd like to use the PKCE flow. I am having issues with the way the router guards are run before the AuthService handleResponse so I must be doing something wrong.
In my main.js I am doing this...
// Use the Auth services to secure the site
import AuthService from '#/services/AuthServices';
Vue.prototype.$auth = new AuthService()
And then in my AuthConfig.js I use this request to login:
loginRequest : {
scopes: [
"openid",
"profile",
process.env.VUE_APP_B2C_APISCOPE_READ,
process.env.VUE_APP_B2C_APISCOPE_WRITE
]
},
The docs say it should redirect to the requesting page but that is not happening. If user goes to the protected home page they are redirected to login. They login, everything is stored properly so they are actually logged in, but then they are sent back to the root redirect URL for the site, not the Home page.
When a user wants to login we just send them to the protected home page and there is a login method called in the router guard which looks like this:
router.beforeEach(async (to, from, next) => {
const requiresAuth = to.matched.some(record => record.meta.requiresAuth)
const IsAuthenticated = await Vue.prototype.$auth.isAuthenticated()
console.log(`Page changing from ${from.name} to ${to.name}, requiresAuth = ${requiresAuth}, IsAuthenticated = ${IsAuthenticated}`)
if (requiresAuth && !IsAuthenticated)
{
next(false)
console.log('STARTING LOGIN')
Vue.prototype.$auth.login()
// Tried this
// Vue.prototype.$auth.login(to.path)
} else {
next()
}
})
In AuthServices.js I have this...
// The user wants to log in
async login(nextPg) {
// Tell B2C what app they want access to and their invitation ID if they are new
if (store.getters.userEmail != null) {
aCfg.loginRequest.loginHint = store.getters.userEmail
}
aCfg.loginRequest.state = "APP=" + store.getters.appCode
if (store.getters.appointmentLink != null && store.getters.appointmentLink != '') {
aCfg.loginRequest.state += ",ID=" + store.getters.appointmentLink
}
// Tried this
// if (nextPg && nextPg != '') {
// aCfg.loginRequest.redirectUrl = process.env.VUE_APP_B2C_REDIRECT_URL + nextPg
// }
return await this.msalInst.loginRedirect(aCfg.loginRequest)
}
I tried puting a nextPg parameter in the login method and adding a redirectUrl property to the login request but that gives me an error saying it is not one of the configured redirect URLs.
Also, I'm trying to make the user experience better when using the above technologies. When you look at the MSAL2.x SPA samples I see that when returning from a Profile Edit, a user is logged out and they are required to log in again. That sounds like a poor user experience to me. Sample here: https://github.com/Azure-Samples/ms-identity-b2c-javascript-spa/blob/main/App/authRedirect.js
Do I need to just create my own profile editing page and save data using MSGraph to prevent that? Sorry for the noob questions. Ideas?
Update - My workaround which seems cheesy is to add these two methods to my AuthService.js:
storeCurrentRoute(nextPath) {
if (!nextPath) {
localStorage[STOR_NEXT_PAGE] = router.history.current.path
} else {
localStorage[STOR_NEXT_PAGE] = nextPath
}
console.log('Storing Route:', localStorage[STOR_NEXT_PAGE])
}
reEstablishRoute() {
let pth = localStorage[STOR_NEXT_PAGE]
if (!!pth && router.history.current.path != pth) {
localStorage[STOR_NEXT_PAGE] = ''
console.log(`Current path is ${router.history.current.path} and reEstablishing route to ${pth}`)
router.push({ path: pth })
}
}
I call storeCurrentRoute() first thing in the login method and then in the handleResponse() I call reEstablishRoute() when its not returning from a profileEdit or password change. Seems like I should be able to make things work without this.
Update Number Two - When returning from B2C's ProfileEdit User Flow the MSAL component is not logging me out properly. Here is my code from my handlePolicyChange() method in my AuthService:
} else if (response.idTokenClaims[clmPolicy] === aCfg.b2cPolicies.names.editProfile) {
Vue.nextTick(() => {
console.log('BACK FROM Profile Change')
Vue.prototype.$swal(
"Success!",
"Your profile has been updated.<br />Please log in again.",
"success"
).then(async () => {
this.logout()
})
})
}
:
// The user wants to log out (all accounts)
async logout() {
// Removes all sessions, need to call AAD endpoint to do full logout
store.commit('updateUserClaims', null)
store.commit('updateUserEmail', null)
let accts = await this.msalInst.getAllAccounts()
for(let i=0; i<accts.length; i++) {
const logoutRequest = {
account: accts[i],
postLogoutRedirectUri: process.env.VUE_APP_B2C_REDIRECT_URL
};
await this.msalInst.logout(logoutRequest);
}
return
}
It is working fine until the call to logout() which runs without errors but I looked in my site storage (in Chrome's debug window > Application) and it looks like MSAL did not clear out its entries like it does on my normal logouts (which always succeed). Ideas?
As part of the MSAL auth request, send a state Parameter. Base64 encode where the user left off inside this parameter. MSAL exposes extraQueryParameters which you can put a dictionary object inside and send in the auth request, put your state Key value pair into extraQueryParameters.
The state param will be returned in the callback response, use it to send the user where you need to.

Avoiding Wix members confirmation emails or changing the sending address

I am working with a client who runs a BtoB. We have set up Member Accounts and some member roles to secure sensible information to which they would have access once approved. However, we are running with an issue that is driving me completely crazy: as Wix’s default registration process rules, once someone signs up and we approve Member Access, an email is sent to their account with a token to confirm their email.
Even though we have an email integrated with Wix with our domain, both the confirmation emails and password reset (sent after a user clicks on “forgot my password”), are not being sent from that address, but from some stupid #site-members.com address.
sender address
My client’s clients all use business emails, and most of their servers are completely blocking these emails. Therefore, they are not being able to complete the registration process and never get to login.
After trying to talk to Wix’s support - which was a complete waste of time - I started scouting the internet and found a tutorial for sending Triggered Emails as confirmation emails. I tried it and it seems to work, however, even though the Triggered Email is set to be sent from my account, it adds a via something:
via ascendbywix
Aaaand.. Guess what! After doing some testing, these get blocked as well in many servers.
So, I don’t know what else to do! Can somebody please help me to either avoid sending confirmation emails at all, or to see if there is any other way to set the email address sending those using Corvid or the Wix dashboard?
Just in case you want to see the code, there are three parts to it:
REGISTRATION LIGHTBOX
import wixWindow from 'wix-window';
import wixData from 'wix-data';
import { doRegistration } from 'backend/register';
let registration;
$w.onReady(function () {
$w("#register").onClick((event) => {
console.log("Button was clicked");
$w("#errorMessage").collapse();
$w("#emailExists").collapse();
if ($w("#email").valid && $w("#password").valid && $w("#company").valid && $w("#name").valid) {
registerPerson();
console.log("Trying to register");
} else {
$w("#errorMessage").expand();
console.log("Missing Information");
}
})
});
function registerPerson () {
let email = $w("#email").value;
let password = $w("#password").value;
let name = $w("#name").value;
let company = $w("#company").value;
let toInsert = {
"name": name,
"company": company,
"email": email
};
wixData.insert("Members", toInsert)
.then( (results) => {
let item = results;
} )
.catch( (err) => {
let errorMsg = err;
} );
doRegistration(email, password, name)
.then((result) => {
wixWindow.openLightbox("confirmation");
let userId = result.user.id
.catch((err) => {
let errMsg = err;
console.log(err);
$w("#emailExists").expand();
} );
});
}
BACKEND REGISTER.JSW
export function doRegistration(email, password, name, company) {
// register the user
return wixUsers.register(email, password, {
"contactInfo": {
"name": name,
"company": company
}
})
.then((results) => {
wixUsers.emailUser('verifyRegistration', results.user.id, {
variables: {
approvalToken: results.approvalToken
}
});
return results
});
}
export function doApproval(token) {
// approve the user
return wixUsers.approveByToken(token)
// user is now active, but not logged in
// return the session token to log in the user client-side
.then((sessionToken) => {
return { sessionToken, "approved": true };
})
.catch((error) => {
return { "approved": false, "reason": error };
});
}
CONFIRMATION PAGE (where users will be redirected after confirming their email)
import wixLocation from 'wix-location';
import wixUsers from 'wix-users';
import {doApproval} from 'backend/register';
$w.onReady( () => {
// get the token from the URL
let token = wixLocation.query.token;
doApproval(token)
.then( (result) => {
if (result.approved){
// log the user in
wixUsers.applySessionToken(result.sessionToken);
console.log("Approved");
}
else {
console.log("Not approved!");
}
} );
} );
just create the SPF record in the DNS and include everything to it along with the WIX servers (ascendbywix.com ~all).

How to create success notification in apache ofbiz

is there any way to create success notification whenever i create any information via a form in ofbiz.
for example: I want to have success notification when a product is created
Thanks in advance.
You can send a notification like this, but first you have to ask the user if he wants to allow notifications.
Notification.requestPermission(function() {
if (Notification.permission === 'granted') {
// user approved.
// use of new Notification(...) syntax will now be successful
new Notification('Success', { body: 'Success!' });
} else if (Notification.permission === 'denied') {
// user denied.
} else { // Notification.permission === 'default'
// user didn’t make a decision.
// You can’t send notifications until they grant permission.
}
});

FB JS SDK - How to check whether any user has logged in for the first time

I'm using this function as event handler for my onclick event and it is working fine -
var facebookLogin = function() {
checkLoginState(function(data) {
if (data.status !== 'connected') {
FB.login(function(response) {
if (response.status === 'connected')
// I do what I want to do here if login is succesful.
}, {scope: scopes});
}
})
}
But is there a way so that I can get to know that the user has logged in for the first time so that I can save him into my database or perform certain actions?
Every time a user logs in, get the Facebook ID with the getId() method.
If the ID does not exist in your database, add his information to your database, if it does, you know what he has logged in before.

Node.js + socket.io + node-amqp and queue binginds when "re" connecting thru socket.io

I have one scenario which is very close to this sample:
One main screen:
this screen (client side) will connect to the socket.io server thru server:9090/scope (io.connect("http://server:9090/scope)) and will send one event "userBindOk" (socket.emit("userBindOk", message)) to the socket.io server;
the server receives the connection and the "userBindOk". At this moment, the server should get the active connection to rabbitmq server and bind the queue to the respective user that just connected to the application thru socket.io. sample:
socket.on("connection", function(client){
//client id is 1234
// bind rabbitmq exchange, queue, and:
queue.subscribe(//receive callback);
})
So far, no problem - I can send/receive messages thru socket.io without problems.
BUT, If I refresh the page, all those steps will be done again. As consequence, the binding to the queue will occur, but this time related to another session of the socket.io client. This means that if I send a message to the queue which is related to the first socket.io session (before the page refresh), that bind should (I think) receive the message and send it to a invalid socket.io client (page refresh = new client.id on the socket.io context). I can prove this behaviour because every time I refresh the page I need to send x times more messages. For instance: I`ve connected for the first time: - so, 1 message - one screen update; refresh the page: I need to send 2 messages to the queue and only the second message will be received from the "actual" socket.io client session - this behaviour will occur as many as I refresh the page (20 page refreshs, 20 messages to be sent to a queue and the server socket.io "last" client will send the message to the client socket.io to render into the screen).
The solutions I believe are:
Find a way to "unbind" the queue when disconnecting from the socket.io server - I didn`t see this option at the node-amqp api yet (waiting for it :D)
find a way to reconnect the socket.io client using the same client.id. This way I can identify the client that is coming and apply some logic to cache the socket.
Any ideas? I tried to be very clear... But, as you know, it`s not so eaey to expose your problem when trying to clarify something that is very specific to some context...
tks
I solved it like this:
I used to declare the rabbitMq queue as durable=true,autoDelete=false,exclusive=false and in my app there was 1 queue/user and 1 exchange(type=direct) with the routing_key name=queueName, my app also used the queue for other client diffent to browsers like android app or iphone app as push fallback, so i use to crear 1 queue for earch user.
The solution to this problem was to change my rabbitMQ queue and exchange declaration. Now i declare the exchange/user as fanout and autoDelete=True, and the user is going to have N queues with durable=true, autoDelete=true, exclusive=true (No. queue = No. clients) and all the queues are bind to the user-exchange(multicast).
NOTE: my app is wirten in django, and i use node+socket+amqp to be able to comunicate with the browser using web.scokets, so i use node-restler to query my app api to get the user-queue info.
thats the rabbitMQ side, for the node+amqp+socket i did this:
server-side:
onConnect: the declaration of the user exchange as fanout, autoDelete, durable. then declaration of the queue as durable, autodelete and exclusive, then the queue.bind to the user-exchange and finaly the queue.subscribe and the socket.disconnect will destroy the queue so there are going to exist queue as client connected the app and this solve the problem of the refresh and allow the user to have more than 1 window-tab with the app:
Server-side:
/*
* unCaught exception handler
*/
process.on('uncaughtException', function (err) {
sys.p('Caught exception: ' + err);
global.connection.end();
});
/*
* Requiere libraries
*/
global.sys = require('sys');
global.amqp = require('amqp');
var rest = require('restler');
var io = require('socket.io').listen(8080);
/*
* Module global variables
*/
global.amqpReady = 0;
/*
* RabbitMQ connection
*/
global.connection = global.amqp.createConnection({
host: host,
login: adminuser,
password: adminpassword,
vhost: vhost
});
global.connection.addListener('ready',
function () {
sys.p("RabbitMQ connection stablished");
global.amqpReady = 1;
}
);
/*
* Web-Socket declaration
*/
io.sockets.on('connection', function (socket) {
socket.on('message', function (data) {
sys.p(data);
try{
var message = JSON.parse(data);
}catch(error){
socket.emit("message", JSON.stringify({"error": "invalid_params", "code": 400}));
var message = {};
}
var message = JSON.parse(data);
if(message.token != undefined) {
rest.get("http://dev.kinkajougames.com/api/push",
{headers:
{
"x-geochat-auth-token": message.token
}
}).on('complete',
function(data) {
a = data;
}).on('success',
function (data){
sys.p(data);
try{
sys.p("---- creating exchange");
socket.exchange = global.connection.exchange(data.data.bind, {type: 'fanout', durable: true, autoDelete: true});
sys.p("---- declarando queue");
socket.q = global.connection.queue(data.data.queue, {durable: true, autoDelete: true, exclusive: false},
function (){
sys.p("---- bind queue to exchange");
//socket.q.bind(socket.exchange, "*");
socket.q.bind(socket.exchange, "*");
sys.p("---- subscribing queue exchange");
socket.q.subscribe(function (message) {
socket.emit("message", message.data.toString());
});
}
);
}catch(err){
sys.p("Imposible to connection to rabbitMQ-server");
}
}).on('error', function (data){
a = {
data: data,
};
}).on('400', function() {
socket.emit("message", JSON.stringify({"error": "connection_error", "code": 400}));
}).on('401', function() {
socket.emit("message", JSON.stringify({"error": "invalid_token", "code": 401}));
});
}
else {
socket.emit("message", JSON.stringify({"error": "invalid_token", "code": 401}));
}
});
socket.on('disconnect', function () {
socket.q.destroy();
sys.p("closing socket");
});
});
client-side:
The socket intance with options 'force new connection'=true and 'sync disconnect on unload'= false.
The client side use the onbeforeunload and onunload windows object events to send socket.disconnect
The client on socket.connect event send the user token to node.
proces message from socket
var socket;
function webSocket(){
//var socket = new io.Socket();
socket = io.connect("ws.dev.kinkajougames.com", {'force new connection':true, 'sync disconnect on unload': false});
//socket.connect();
onSocketConnect = function(){
alert('Connected');
socket.send(JSON.stringify({
token: Get_Cookie('liveScoopToken')
}));
};
socket.on('connect', onSocketConnect);
socket.on('message', function(data){
message = JSON.parse(data);
if (message.action == "chat") {
if (idList[message.data.sender] != undefined) {
chatboxManager.dispatch(message.data.sender, {
first_name: message.data.sender
}, message.data.message);
}
else {
var username = message.data.sender;
Data.Collections.Chats.add({
id: username,
title: username,
user: username,
desc: "Chat",
first_name: username,
last_name: ""
});
idList[message.data.sender] = message.data.sender;
chatboxManager.addBox(message.data.sender, {
title: username,
user: username,
desc: "Chat",
first_name: username,
last_name: "",
boxClosed: function(id){
alert("closing");
}
});
chatboxManager.dispatch(message.data.sender, {
first_name: message.data.sender
}, message.data.message);
}
}
});
}
webSocket();
window.onbeforeunload = function() {
return "You have made unsaved changes. Would you still like to leave this page?";
}
window.onunload = function (){
socket.disconnect();
}
And that's it, so no more round-robing of the message.