How to dynamically set auth in handshake in Socket.IO client? - javascript

On the frontend/client-side, I have an implementation where I secure my socket.io connection using auth token. This auth token is updated in the store every few minutes.
const authToken = getToken()
socket = io(WS_BASE_URL, {
auth: {
token: authToken
}
});
In this current implementation, when the socket tries to reconnect, authToken is not updated. How to dynamically set it every time reconnect attempt is made?
socket = io(WS_BASE_URL, {
auth: {
token: () => getToken()
}
});
I want this kind of implementation where I pass a function to get the token every time from the store. Or is there any way we can modify handshake data in the reconnect_attempt event?
socket.io.on("reconnect_attempt", () => {
// update handshake
});

socket.io 2.x client api document has this
"The query content can also be updated on reconnection:"
socket.on('reconnect_attempt', () => {
socket.io.opts.query = {
token: 'fgh'
}
});
I think you use 4.x because you use auth option, but I think it is basically the same. The middleware at server side will get the update token when a socket reconnect.

Related

SOCKET.IO: Access cookies from devtools. Express-session

Why can't I access this cookie?
When the user logs in the cookie is recieved and sent back to the Express server.
When initializing a new websocket to the Socket.io server this cookie does not get sent, so I was trying to get it via the document.cookie. However, it did not work since the the cookie was not modifiable.
It is an HttpOnly cookie that cannot be accessed via client-side Javascript.
In other words: The server is able to read and manipulate the cookie. The client receives it and blindly sends it back with every subsequent request, without being able to read or manipulate its contents, at least not with Javascript means.
The official website did something that did not work for me.
express session middleware
const session = require("express-session");
io.use(wrap(session({ secret: "cats" })));
io.on("connection", (socket) => {
const session = socket.request.session;
});
So, I got around it by making:
Logic:
Before establishing websocket, request credentials to express endpoint "/userCredentials"
and then use these credentials to establish the connection
Warning: Down below code is stripped because I did so many auth logics
CLIENT:
...
useEffect(() => {
(async() => {
const pending_creds = await fetch("/userCredentials");
const creds = pending_creds.json();
const ws = io({auth: {creds}})
setSocket(ws)
})()
}, [])
...
SERVER:
...
app.get("/userCredentials", (req,res) => {
const userSession = req.session.user;
res.json(userSession)
})
...
io.use(socket, next){
const creds = socket.handshake.auth.userSession;
if(creds){
next()
} else {
socket.disconnect()
}
}

How to get ID token refreshed when using Firebase Auth javascript SDK

I use a backend app server to communicate with firestore, so the ReactJS client sends ID token to server which in turn communicates with firebase using that token. Client does not make any firebase calls directly other than auth calls. Problem I am facing is, the ID token never gets refreshed and after an hour, backend server fails to recognize token and ends up sending 401 to client. My understanding is that the client should always refresh the token and always send a valid token to backend app server.
firebase.auth().onAuthStateChanged(auth, (user) => {
if (user) {
localStorage.setItem("auth.uid", user.uid)
localStorage.setItem("auth.email", user.email)
user.getIdToken(true).then(function(idToken) {
localStorage.setItem("auth.token", idToken)
})
} else {
// User is signed out
localStorage.removeItem("auth.email")
localStorage.removeItem("auth.uid")
localStorage.removeItem("auth.token")
}
})
firebase.auth().onIdTokenChanged((user) => {
// ** NOT GETTING THIS CALLBACK AFTER 1 HR TO REFRESH **
if (user) {
// User is signed in or token was refreshed
user.getIdToken(true).then(function(idToken) {
localStorage.setItem("auth.token", idToken)
})
}
})
Other option, may be, is to use the REST API to manually fetch a new ID token and use that but having passed forceRefresh value true I would expect this is handled automatically.
What am I missing?

Firebase ID Token expiration in an hour

so I am using redux-saga in my react-native app and tried to use refresh token but didn't work, so my approach was the following in app.js in order to get the token specifically for each request and force refresh it:
handleResponse = async () => {
const {dispatch} = this.store;
await axios.interceptors.request.use(config => {
// Important: request interceptors **must** return the request.
console.log("refreshToken")
let user = firebase.auth().currentUser;
firebase.auth().onAuthStateChanged(function(user) { if (user) {
console.log("auth changed: ",user)
user.getIdToken(true).then((token) => {
setAccessToken(token);
config.headers.authorization = token;
}
);
} else { console.log("didn't auth change") } });
console.log("req in handle response: ",JSON.stringify(config));
return config;
});
axios.interceptors.response.use(config => config, (err) => {
if (err.response) {
const response = err.response;
const state = this.store.getState();
if (
response.status === 401
&& state.auth.isAuthenticated
) {
dispatch(logout());
}
}
return Promise.reject(err);
});
};
But it always ends up after an hour throwing me the following error::
Firebase ID token has expired. Get a fresh token from your client app and try again (auth/id-token-expired). See https://firebase.google.com/docs/auth/admin/verify-id-tokens for details on how to retrieve an ID token.
so I was wondering if there's another approach I can try to solve the issue from my side?
Thanks in advance.
Firebase auth tokens automatically refresh every hour. That's a function of the SDK, and you don't have to do anything to enable it.
You definitely do not want to call firebase.auth().onAuthStateChanged for each request. That starts a persistent listener, and adds a new callback every time it's used. Typically you add just one listener globally for your app, and use its updates as they happen.
If you need to know when the SDK refreshes the token in order to get a new one immediately, you should instead use onIdTokenChanged to set up a callback that will be invoked every time the user's token changes. Again, you should only set up one of these globally for your app, and use its current value at the time of the request.

Send Authorization Credentials to Django-channels WebSocket (without setting token as cookie)

I have a websocket that i want to be authenticated with Token Authorization on handshake on opennig. I looked for answers for this problem and almost all of them suggested to store Authorization cookie first with javascript then connect to web socket (so the header will be sent from cookie stored in web page).
But I prefer to not store the token in browser cookie and just send it in my websocket request scope.
Here is a simple javascript code to connect to websocket. I really appreciate it if any one help me on this context:
<script>
const socket = new WebSocket('ws://localhost:8001/announcement');
socket.onopen = function open() {
console.log('WebSockets connection created.');
};
// Listen for messages
socket.addEventListener('announcement', function (event) {
console.log('Message from server ', event.data);
});
</script>
I found a solution. Correct me if I'm wrong.
Right after web socket connection established, send the token to server. And in Server (in my case django channels) in receive method, I fetch that token and if token is valid, I update the connection information, And if the token is not valid disconnect the connection.
something like this:
js file:
const socket = new WebSocket('ws://localhost:8001/announcement');
socket.onopen = function open() {
console.log('WebSockets connection created.');
let authData = {'token': '<valid-token-here>'}
socket.send(JSON.stringify(authData));
};
and on server side (django for example):
def receive(self, text_data=None, bytes_data=None):
if self.scope['user'].id:
pass
else:
try:
# It means user is not authenticated yet.
data = json.loads(text_data)
if 'token' in data.keys():
token = data['token']
user = fetch_user_from_token(token)
self.scope['user'] = user
except Exception as e:
# Data is not valid, so close it.
print(e)
pass
if not self.scope['user'].id:
self.close()

How to handle Quickblox session expiration?

In quickblox, the session expires two hours after the last request. So to handle this situation I have used the code
config.on.sessionExpired = function(next,retry){
})
and passed the config in QB.init
config.on.sessionExpired = function(next, retry) {
console.log("============session renewal")
var user = self.get('user')
QB.chat.disconnect()
QB.createSession({ login: user.login, password: user.pass }, function(err, res) {
if (res) {
// save session token
token = res.token;
user.id = res.user_id
self.get('user').token = token
QB.chat.connect({ userId: user.id, password: user.pass }, function(err, roster) {
// Do something
})
}
})
}
QB.init(QBApp.appId, QBApp.authKey, QBApp.authSecret, config);
Is this the right way to renew the session by first disconnecting the chat, then creating a new session first and then connecting the chat back again?
I do not want the client to know that the session has expired in quickblox and they have to refresh the page. The chat is a part of the web portal. It is fine if the quickblox takes 2-3 seconds to create a new session token and then connect to chat. By the time, I can show a loader or some message.
I had tried it without the QB.chat.disconnect() but then it did not work and sent me Unprocessable entity 422 error.
I have same problem, and i found some solution at QuickBlox Docs
https://docs.quickblox.com/docs/js-authentication#session-expiration
QuickBlox JavaScript SDK will automatically renew your current session. There is no need to manually call createSession() method. After login, a session is available for 2 hours. When the session expires, any request method will firstly renew it and then execute itself.
And this example from the official documentation:
var CONFIG = {
on: {
sessionExpired: function(handleResponse, retry) {
// call handleResponse() if you do not want to process a session expiration,
// so an error will be returned to origin request
// handleResponse();
QB.createSession(function(error, session) {
retry(session);
});
}
}
};
QB.init(3477, "ChRnwEJ3WzxH9O4", "AS546kpUQ2tfbvv", config);

Categories

Resources