feat(auth): close connection on unauthenticated socket connection

This commit is contained in:
Maieul BOYER 2025-12-23 15:22:26 +01:00
parent 90a40aa626
commit 156379ab54
No known key found for this signature in database
4 changed files with 122 additions and 2 deletions

View file

@ -0,0 +1,12 @@
import { type UserId } from '@shared/database/mixin/user';
declare module 'socket.io'
{
interface Socket {
authUser: {
id: UserId;
name: string;
guest: boolean;
}
}
};

View file

@ -1,3 +1,7 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { JwtType } from '@shared/auth';
import { UserId } from '@shared/database/mixin/user';
import { isNullish } from '@shared/utils';
import type {
FastifyInstance,
FastifyPluginAsync,
@ -11,6 +15,21 @@ const F: (
) => Omit<FastifyInstance, 'io'> & { io: Server } = (f) =>
f as Omit<FastifyInstance, 'io'> & { io: Server };
function authenticateToken(
fastify: FastifyInstance,
token: string,
): { id: UserId; name: string; guest: boolean } {
const tok = fastify.jwt.verify<JwtType>(token);
if (tok.kind != 'auth') {
throw new Error('Token isn\'t correct type');
}
const user = fastify.db.getUser(tok.who);
if (isNullish(user)) {
throw new Error('User not found');
}
return { id: user.id, name: user.name, guest: user.guest };
}
const fastifySocketIO: FastifyPluginAsync = fp(async (fastify) => {
function defaultPreClose(done: HookHandlerDoneFunction) {
F(fastify).io.local.disconnectSockets(true);
@ -20,6 +39,36 @@ const fastifySocketIO: FastifyPluginAsync = fp(async (fastify) => {
'io',
new Server(fastify.server, { path: '/api/ttt/socket.io' }),
);
F(fastify).io.use((socket, next) => {
const cookieHeader = socket.request.headers.cookie;
if (!cookieHeader) {
throw new Error('Missing token cookie');
}
const cookies = Object.fromEntries(
cookieHeader.split(';').map((c) => {
const [k, v] = c.trim().split('=');
return [k, v];
}),
);
if (!cookies.token) {
throw new Error('Missing token cookie');
}
try {
socket.authUser = authenticateToken(fastify, cookies.token);
next();
}
catch (e: any) {
next({
name: 'Unauthorized',
message: e.message,
data: { status: 401 },
});
}
});
fastify.addHook('preClose', defaultPreClose);
fastify.addHook('onClose', (instance: FastifyInstance, done) => {
F(instance).io.close();
@ -28,4 +77,3 @@ const fastifySocketIO: FastifyPluginAsync = fp(async (fastify) => {
});
export default fastifySocketIO;