Closed sylvainbx closed 8 years ago
So you have a login process that happens before the socket is created, it creates some token and then you want to pass that token to the io.connect
.
I've not had this pattern in the apps that I work on, typically I either get the auth token via the websocket, or auth already happened on a separate page and I don't need to fetch it.
I see two options though...
import { createStore, applyMiddleware } from 'redux';
import createSocketIoMiddleware from 'redux-socket.io';
import io from 'socket.io-client';
function reducer(state = {}, action){
switch(action.type){
case 'message':
return Object.assign({}, {message:action.data});
default:
return state;
}
}
let socketIoMiddleware = null;
const proxyMiddleware = store => next => action => {
if (socketIoMiddleWare !== null) {
return socketIoMiddleware(store)(next)(action);
}
return next(action);
}
let store = applyMiddleware(proxyMiddleware)(createStore)(reducer);
store.subscribe(()=>{
console.log('new client state', store.getState());
});
store.dispatch({type:'server/hello', data:'Hello!'});
//Later after your login code has run and you have your token
const socket = io.connect(API_ROOT, {
'query': 'token=' + jwt_token
});
socketIoMiddleware = createSocketIoMiddleware(socket, "server/");;
Note that I haven't run the above code or anything, it's just meant to be illustrative of the approach, which would be to set up the store first, and let the middleware start taking effect later.
Thanks for your hints. I finally achieved to solve my authentication issue by implementing a solution based on your second proposal.
Loading the middleware dynamic after fetching user's data indeed work, but the action is dispatched multiple times.
Here is my client code :
export const proxyMiddleware: Middleware = (api: MiddlewareAPI<any>) => next => (action: any) => {
if (!middleware) {
const store = api.getState();
const auth = store ? store.authentication : {};
if (auth.idToken) {
const socket = io(SOCKET_URL, {
query: {
token: auth.idToken
}
});
middleware = createSocketIoMiddleware(socket, 'server/');
} else {
return next(action);
}
}
return middleware(api)(next)(action);
};
On server side :
this.webSocket = SocketIO(this.server, { pingTimeout: 30000 });
this.webSocket.use(async (socket: SocketIO.Socket, next: (err?: any) => void) => {
if (socket.handshake.query && socket.handshake.query.token) {
const { token } = socket.handshake.query;
const user = await this.googleAuth.getUser(token);
if (user) {
const sockets = this.app.get('sockets') || {};
socket.id = user._id;
if (!sockets[user._id]) {
sockets[user._id] = socket;
this.app.set('sockets', sockets);
}
return next();
}
}
return next(new Error('Authentication faild'));
});
Then on the server I emit an action to a single client but on the client the action is dispatched multiple times.
If I directly subscribe to the same event by socket.on(eventName)
, it fires only once...
Any directions how to fix it?
Has anyone acheived a working version of this? I am trying to create socket connection at runtime, dynamically, and have tried the following implementation:
let dynamicMiddleware = null;
const setupDynamicMiddleware = store => next => action => {
if (dynamicMiddleware !== null) {
console.log(dynamicMiddleware);
return dynamicMiddleware(store)(next)(action);
}
return next(action);
}
const unsetMiddleware = () => {
dynamicMiddleware = null;
}
const setMiddleware = middleware => {
dynamicMiddleware = middleware;
}
export default setupDynamicMiddleware;
export {
setMiddleware,
unsetMiddleware,
}
and connecting it the the store via
// let rootMiddleware = applyMiddleware(...otherMiddlewares, setupDynamicMiddleware);
but it seems like that for every redux action, another listener is added to the middleware, causing my browser to crash. I am certain that the middleware is created & set only once via the setMiddleware function.
Yeah, I think you'd want to only run the (store)
part of the middleware call chain only once. Maybe something like this?
let dynamicMiddleware = null;
let initedDynamicMiddleware = null;
const setupDynamicMiddleware = store => next => action => {
if (dynamicMiddleware && !initedDynamicMiddleware) {
initedDynamicMiddleware = dynamicMiddleware(store);
}
if (initedDynamicMiddleware) {
return initedDynamicMiddleware(next)(action);
}
return next(action);
}
const unsetMiddleware = () => {
dynamicMiddleware = null;
initedDynamicMiddleware = null;
}
const setMiddleware = middleware => {
dynamicMiddleware = middleware;
initedDynamicMiddleware = null;
}
export default setupDynamicMiddleware;
export {
setMiddleware,
unsetMiddleware,
}
import thunk from "redux-thunk";
import createSocketIoMiddleware from "redux-socket.io";
import io from "socket.io-client";
import { setMiddleware, unsetMiddleware } from "./socketio";
import setupDynamicMiddleware from "./socketio";
import { composeWithDevTools } from "redux-devtools-extension";
import { userLoginReducer } from "./reducers/userReducers";
const userInfoFromStorage = localStorage.getItem("userInfo")
? JSON.parse(localStorage.getItem("userInfo"))
: null;
const reducer = combineReducers({
userLogin: userLoginReducer,
});
const initialState = {
userLogin: { userInfo: userInfoFromStorage },
};
const middleware = [thunk, setupDynamicMiddleware];
let store = createStore(
reducer,
initialState,
composeWithDevTools(applyMiddleware(...middleware))
);
let token = store.getState().userLogin.userInfo.token;
if (token) {
setMiddleware(
createSocketIoMiddleware(
io("http://localhost:3000", { query: `token=${token}` }),
"server/"
)
);
} else {
unsetMiddleware();
}
export default store;
I am trying to use the functions setMiddleware and unsetmiddleware but Cannot read property 'token' of null has anyone implemented a solution to this>
Hi, How to handle authentication using redux-socket.io? For example, I'm using JWT with socketio-jwt but socket.io requires to configure the token at connection time, eg.
As
jwt_token
is retrieved at login time, I store it in the redux's store but redux-socket.io need to be configured before the store is instantiated.How can I solve this vicious circle?