-
Notifications
You must be signed in to change notification settings - Fork 10.1k
-
Hello i get a micro connection while validating jwt in my middleware.
Middleware itself:
export const WsAuthMiddleware = ( socket: Socket, next: (err?: any) => void, ): void => { passport.authenticate( 'jwt', { session: false }, (err: unknown, user: IJwtSignPayload) => { if (err) { const errorResponse = err instanceof HttpException ? err.getResponse() : new InternalServerErrorException().getResponse(); return next(errorResponse); } socket.handshake['user'] = user; next(); }, )(socket.request, {}, next); };
It's using in adapter
export class SocketIOAdapter extends IoAdapter { private readonly configService: ConfigService; private readonly redisService: RedisService; private adapterConstructor: ReturnType<typeof createAdapter>; constructor(private readonly app: INestApplicationContext) { super(app); this.configService = this.app.get(ConfigService); this.redisService = this.app.get(RedisService); this._createClient(); } private _createClient(): void { const streamName = this.configService.get<string>('redisConfig.streamName'); const redisClient = this.redisService.getClient; this.adapterConstructor = createAdapter(redisClient, { streamName }); } createIOServer(port: number, options?: ServerOptions): Server { const server: Server = super.createIOServer(port, { ...options, cors: { origin: (_req: any, callback: (arg0: null, arg1: boolean) => void) => { callback(null, true); }, credentials: true, }, transports: ['polling', 'websocket'], allowUpgrades: true, pingTimeout: 2000, pingInterval: 7000, }); server.adapter(this.adapterConstructor); server.use(WsAuthMiddleware); return server; } }
Is there a way to skip this? socket_connection
Beta Was this translation helpful? Give feedback.
All reactions
Replies: 2 comments 7 replies
-
instead of header send token in query and then extract like this socket.handshake.query.token
Beta Was this translation helpful? Give feedback.
All reactions
-
👎 1
-
instead of header send token in query and then extract like this socket.handshake.query.token
I do not think this is best practice to put jwt in the query. Thanks for your advice ❤️
Beta Was this translation helpful? Give feedback.
All reactions
-
Either put the token in socket.handshake.auth, or in socket.handshake.headers.authorization.
I don't think taht the way oh sending token in the issue there.
Beta Was this translation helpful? Give feedback.
All reactions
-
Hi! You can use io.engine.use()
instead of io.use()
.
Reference: https://socket.io/docs/v4/middlewares/#compatibility-with-express-middleware
Beta Was this translation helpful? Give feedback.
All reactions
-
Hi! You can use
io.engine.use()
instead ofio.use()
.Reference: https://socket.io/docs/v4/middlewares/#compatibility-with-express-middleware
Yep, i found this in the docs but the thing is that i can not access handshake here to put the user in it
server.engine.use((req, res, next) => { const isHandshake = req._query.sid === undefined; if (isHandshake) { const socket: Socket = req.socket; if (!socket) { new Logger().error('Cannot get socket in middleware'); return next(new InternalServerErrorException()); } passport.authenticate( 'jwt', { session: false }, (err: unknown, user: IJwtSignPayload) => { //socket.handshake is undefined socket.handshake['user'] = user; return next(); }, )(req, res, next); } });
I need to put user in the handshake because i can get it from the RemoteSocket type
const sockets = await this.io.in(room).fetchSockets(); const socket = sockets.find(({ handshake }) => { const wsUser: IJwtSignPayload = handshake['user']; if (!wsUser) throw new NotFoundException(`User not found`);
If there is a better practice where to put the user that i can get it from the remote socket too it will be super.
Beta Was this translation helpful? Give feedback.
All reactions
-
I found that if i will use passport the user property will be found in the socket.request, but here is rising other question how can i give the connection error properly.
Sience i use Nest.js this is how my strategy looks:
@Injectable() export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') { constructor( private readonly configService: ConfigService, private readonly usersService: UsersService, ) { super({ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), ignoreExpiration: false, secretOrKey: configService.get<string>('jwtConfig.access.secret'), }); } async validate(payload: IJwtSignPayload): Promise<UsersDto> { const user: UsersDto = await this.usersService.validateUser( payload.id, payload.session.id, ); return user; } }
This is how do i use middlleware
server.engine.use( ( req: Request & { _query: { sid?: string }; socket: Socket }, res: Response, next: NextFunction, ) => { const isHandshake = req._query.sid === undefined; if (isHandshake) { passport.authenticate('jwt', (err, user) => { if (err) { const logger = new Logger(); logger.error(err); return next(new Error('Internal Server Error')); } if (!user) { return next(new Error('Unauthorized')); } req['user'] = user; return next(); })(req, res, next); } else { next(); } }, );
In postman instead of unauthorize as server response i get Error: Unexpected server response: 400
if i will use in this mode
if (isHandshake) { passport.authenticate('jwt')(req, res, next); } else { next(); }
I get Error: Unexpected server response: 502
Beta Was this translation helpful? Give feedback.
All reactions
-
You can put it in the data
object:
server.engine.use((req, res, next) => { const isHandshake = req._query.sid === undefined; if (isHandshake) { passport.authenticate("jwt", { session: false })(req, res, next); // init the req.user object } else { next(); } }); server.on("connection", (socket) => { const userId = socket.request.user.id; socket.data.userId = userId; });
References:
Beta Was this translation helpful? Give feedback.
All reactions
-
I apologize for the spamming, @darrachequesne. I disabled nginx, and now when the JWT is invalid, the response I get is "Error: socket hang up" in the example you provided. However, when I manually handle the error, I still receive a 400 Bad Request on localhost:3000. When the JWT is valid, everything works fine, and the user is successfully assigned to the socket request. The issue now is how I can send a proper error message when the JWT is invalid.
Beta Was this translation helpful? Give feedback.
All reactions
-
You can put it in the
data
object:server.engine.use((req, res, next) => { const isHandshake = req._query.sid === undefined; if (isHandshake) { passport.authenticate("jwt", { session: false })(req, res, next); // init the req.user object } else { next(); } }); server.on("connection", (socket) => { const userId = socket.request.user.id; socket.data.userId = userId; });References:
You can even put the user in socket['user'].
This works fine in my case.
I don't think the problem is related to where you put the token ( handshale.auth, handshake.headers.authorization, ... ) or where you set the user data.
I would investigate on two things :
- the authentication middleware. As I can see in the screenshot, there is a 401 error, that indicates a failure in it. Token not retrieved ? invalid ?
- maybe client side ?
a few years ago I had issues with react in strict mode. The component that handeled socket.io connexion was rendered, destroyed and rendered once again, causing an error : connexion closed before established. Looking at the screenshot, I assume that it's probably not the case here, cause the connexion is tested with postman, so no client framework involved here.
Beta Was this translation helpful? Give feedback.