Merge pull request #71 from Maix0/maix/socketAuth

This commit is contained in:
Nigel 2025-12-24 10:47:24 +01:00 committed by GitHub
commit efdef8f8e9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 122 additions and 2 deletions

12
src/chat/src/@types/socket.io.d.ts vendored Normal file
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 { import type {
FastifyInstance, FastifyInstance,
FastifyPluginAsync, FastifyPluginAsync,
@ -11,6 +15,21 @@ const F: (
) => Omit<FastifyInstance, 'io'> & { io: Server } = (f) => ) => Omit<FastifyInstance, 'io'> & { io: Server } = (f) =>
f as Omit<FastifyInstance, 'io'> & { io: Server }; 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) => { const fastifySocketIO: FastifyPluginAsync = fp(async (fastify) => {
function defaultPreClose(done: HookHandlerDoneFunction) { function defaultPreClose(done: HookHandlerDoneFunction) {
F(fastify).io.local.disconnectSockets(true); F(fastify).io.local.disconnectSockets(true);
@ -20,6 +39,36 @@ const fastifySocketIO: FastifyPluginAsync = fp(async (fastify) => {
'io', 'io',
new Server(fastify.server, { path: '/api/chat/socket.io' }), new Server(fastify.server, { path: '/api/chat/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('preClose', defaultPreClose);
fastify.addHook('onClose', (instance: FastifyInstance, done) => { fastify.addHook('onClose', (instance: FastifyInstance, done) => {
F(instance).io.close(); F(instance).io.close();
@ -28,4 +77,3 @@ const fastifySocketIO: FastifyPluginAsync = fp(async (fastify) => {
}); });
export default fastifySocketIO; export default fastifySocketIO;

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 { import type {
FastifyInstance, FastifyInstance,
FastifyPluginAsync, FastifyPluginAsync,
@ -11,6 +15,21 @@ const F: (
) => Omit<FastifyInstance, 'io'> & { io: Server } = (f) => ) => Omit<FastifyInstance, 'io'> & { io: Server } = (f) =>
f as Omit<FastifyInstance, 'io'> & { io: Server }; 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) => { const fastifySocketIO: FastifyPluginAsync = fp(async (fastify) => {
function defaultPreClose(done: HookHandlerDoneFunction) { function defaultPreClose(done: HookHandlerDoneFunction) {
F(fastify).io.local.disconnectSockets(true); F(fastify).io.local.disconnectSockets(true);
@ -20,6 +39,36 @@ const fastifySocketIO: FastifyPluginAsync = fp(async (fastify) => {
'io', 'io',
new Server(fastify.server, { path: '/api/ttt/socket.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('preClose', defaultPreClose);
fastify.addHook('onClose', (instance: FastifyInstance, done) => { fastify.addHook('onClose', (instance: FastifyInstance, done) => {
F(instance).io.close(); F(instance).io.close();
@ -28,4 +77,3 @@ const fastifySocketIO: FastifyPluginAsync = fp(async (fastify) => {
}); });
export default fastifySocketIO; export default fastifySocketIO;