Skip to content

Commit

Permalink
Add token verification for socket connection + refactor code
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolelim02 committed Nov 7, 2024
1 parent ad3c6c4 commit 70c3c6d
Show file tree
Hide file tree
Showing 19 changed files with 175 additions and 60 deletions.
3 changes: 3 additions & 0 deletions backend/collab-service/.env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ SERVICE_PORT=3003
# Origins for cors
ORIGINS=http://localhost:5173,http://127.0.0.1:5173

# Other service APIs
USER_SERVICE_URL=http://user-service:3001/api

# Redis configuration
REDIS_URI=redis://collab-service-redis:6379

Expand Down
19 changes: 19 additions & 0 deletions backend/collab-service/src/middlewares/basicAccessControl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { ExtendedError, Socket } from "socket.io";
import { verifyToken } from "../api/userService.ts";

export const verifyUserToken = (
socket: Socket,
next: (err?: ExtendedError) => void
) => {
const token =
socket.handshake.headers.authorization || socket.handshake.auth.token;
verifyToken(token)
.then(() => {
console.log("Valid credentials");
next();
})
.catch((err) => {
console.error(err);
next(new Error("Unauthorized"));
});
};
4 changes: 4 additions & 0 deletions backend/collab-service/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ import app, { allowedOrigins } from "./app.ts";
import { handleWebsocketCollabEvents } from "./handlers/websocketHandler.ts";
import { Server, Socket } from "socket.io";
import { connectRedis } from "./config/redis.ts";
import { verifyUserToken } from "./middlewares/basicAccessControl.ts";

const server = http.createServer(app);

export const io = new Server(server, {
cors: {
origin: allowedOrigins,
Expand All @@ -13,6 +15,8 @@ export const io = new Server(server, {
connectionStateRecovery: {},
});

io.use(verifyUserToken);

io.on("connection", (socket: Socket) => {
handleWebsocketCollabEvents(socket);
});
Expand Down
15 changes: 15 additions & 0 deletions backend/communication-service/src/api/userService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import axios from "axios";

const USER_SERVICE_URL =
process.env.USER_SERVICE_URL || "http://localhost:3001/api";

const userClient = axios.create({
baseURL: USER_SERVICE_URL,
withCredentials: true,
});

export const verifyToken = (token: string | undefined) => {
return userClient.get("/auth/verify-token", {
headers: { authorization: token },
});
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { ExtendedError, Socket } from "socket.io";
import { verifyToken } from "../api/userService";

export const verifyUserToken = (
socket: Socket,
next: (err?: ExtendedError) => void
) => {
const token =
socket.handshake.headers.authorization || socket.handshake.auth.token;
verifyToken(token)
.then(() => {
console.log("Valid credentials");
next();
})
.catch((err) => {
console.error(err);
next(new Error("Unauthorized"));
});
};
16 changes: 2 additions & 14 deletions backend/communication-service/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import app, { allowedOrigins } from "./app";
import { createServer } from "http";
import { Server } from "socket.io";
import { handleWebsocketCommunicationEvents } from "./handlers/websocketHandler";
import { verifyToken } from "./utils/userServiceApi";
import { verifyUserToken } from "./middlewares/basicAccessControl";

const PORT = process.env.SERVICE_PORT || 3005;

Expand All @@ -13,19 +13,7 @@ export const io = new Server(server, {
connectionStateRecovery: {},
});

io.use((socket, next) => {
const token =
socket.handshake.headers.authorization || socket.handshake.auth.token;
verifyToken(token)
.then(() => {
console.log("Valid credentials");
next();
})
.catch((err) => {
console.error(err);
next(new Error("Unauthorized"));
});
});
io.use(verifyUserToken);

io.on("connection", handleWebsocketCommunicationEvents);

Expand Down
1 change: 1 addition & 0 deletions backend/matching-service/.env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ ORIGINS=http://localhost:5173,http://127.0.0.1:5173
# Other service APIs
QUESTION_SERVICE_URL=http://question-service:3000/api/questions
QN_HISTORY_SERVICE_URL=http://qn-history-service:3006/api/qnhistories
USER_SERVICE_URL=http://user-service:3001/api

# RabbitMq configuration
RABBITMQ_DEFAULT_USER=admin
Expand Down
31 changes: 31 additions & 0 deletions backend/matching-service/src/api/questionHistoryService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import axios from "axios";

const QN_HISTORY_SERVICE_URL =
process.env.QN_HISTORY_SERVICE_URL ||
"http://qn-history-service:3006/api/qnhistories";

const qnHistoryService = axios.create({
baseURL: QN_HISTORY_SERVICE_URL,
headers: {
"Content-Type": "application/json",
},
});

export const createQuestionHistory = (
questionId: string,
title: string,
submissionStatus: string,
language: string,
...userIds: string[]
) => {
const dateAttempted = new Date();
return qnHistoryService.post("/", {
userIds,
questionId,
title,
submissionStatus,
language,
dateAttempted,
timeTaken: 0,
});
};
16 changes: 16 additions & 0 deletions backend/matching-service/src/api/questionService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import axios from "axios";

const QUESTION_SERVICE_URL =
process.env.QUESTION_SERVICE_URL ||
"http://question-service:3000/api/questions";

const questionClient = axios.create({
baseURL: QUESTION_SERVICE_URL,
headers: {
"Content-Type": "application/json",
},
});

export const getRandomQuestion = (complexity: string, category: string) => {
return questionClient.get("/random", { params: { complexity, category } });
};
15 changes: 15 additions & 0 deletions backend/matching-service/src/api/userService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import axios from "axios";

const USER_SERVICE_URL =
process.env.USER_SERVICE_URL || "http://localhost:3001/api";

const userClient = axios.create({
baseURL: USER_SERVICE_URL,
withCredentials: true,
});

export const verifyToken = (token: string | undefined) => {
return userClient.get("/auth/verify-token", {
headers: { authorization: token },
});
};
2 changes: 1 addition & 1 deletion backend/matching-service/src/config/rabbitmq.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import amqplib, { Connection } from "amqplib";
import dotenv from "dotenv";
import { matchUsers } from "../utils/mq_utils";
import { matchUsers } from "../utils/messageQueue";
import { MatchRequestItem } from "../handlers/matchHandler";
import { Complexities, Categories, Languages } from "../utils/constants";

Expand Down
40 changes: 18 additions & 22 deletions backend/matching-service/src/handlers/websocketHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import {
} from "./matchHandler";
import { io } from "../server";
import { v4 as uuidv4 } from "uuid";
import { qnHistoryService, questionService } from "../utils/api";
import { getRandomQuestion } from "../api/questionService";
import { createQuestionHistory } from "../api/questionHistoryService";

enum MatchEvents {
// Receive
Expand Down Expand Up @@ -130,28 +131,23 @@ export const handleWebsocketMatchEvents = (socket: Socket) => {
}

const { complexity, category, language } = match;
questionService
.get("/random", { params: { complexity, category } })
.then((res) => {
const qnId = res.data.question.id;
qnHistoryService
.post("/", {
userIds: [userId1, userId2],
questionId: qnId,
title: res.data.question.title,
submissionStatus: "Attempted",
dateAttempted: new Date(),
timeTaken: 0,
language: language,
})
.then((res) => {
io.to(matchId).emit(
MatchEvents.MATCH_SUCCESSFUL,
qnId,
res.data.qnHistory.id
);
});
getRandomQuestion(complexity, category).then((res) => {
const qnId = res.data.question.id;
createQuestionHistory(
qnId,
res.data.question.title,
"Attempted",
language,
userId1,
userId2
).then((res) => {
io.to(matchId).emit(
MatchEvents.MATCH_SUCCESSFUL,
qnId,
res.data.qnHistory.id
);
});
});
}
}
);
Expand Down
19 changes: 19 additions & 0 deletions backend/matching-service/src/middlewares/basicAccessControl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { ExtendedError, Socket } from "socket.io";
import { verifyToken } from "../api/userService";

export const verifyUserToken = (
socket: Socket,
next: (err?: ExtendedError) => void
) => {
const token =
socket.handshake.headers.authorization || socket.handshake.auth.token;
verifyToken(token)
.then(() => {
console.log("Valid credentials");
next();
})
.catch((err) => {
console.error(err);
next(new Error("Unauthorized"));
});
};
5 changes: 5 additions & 0 deletions backend/matching-service/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ import app, { allowedOrigins } from "./app.ts";
import { handleWebsocketMatchEvents } from "./handlers/websocketHandler.ts";
import { Server } from "socket.io";
import { connectToRabbitMq } from "./config/rabbitmq.ts";
import { verifyToken } from "./api/userService.ts";
import { verifyUserToken } from "./middlewares/basicAccessControl.ts";

const server = http.createServer(app);

export const io = new Server(server, {
cors: {
origin: allowedOrigins,
Expand All @@ -13,6 +16,8 @@ export const io = new Server(server, {
connectionStateRecovery: {},
});

io.use(verifyUserToken);

io.on("connection", (socket) => {
handleWebsocketMatchEvents(socket);
});
Expand Down
23 changes: 0 additions & 23 deletions backend/matching-service/src/utils/api.ts

This file was deleted.

4 changes: 4 additions & 0 deletions frontend/src/utils/collabSocket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,13 @@ export type CollabSessionData = {
};

const COLLAB_SOCKET_URL = "http://localhost:3003";

export const collabSocket = io(COLLAB_SOCKET_URL, {
reconnectionAttempts: 3,
autoConnect: false,
auth: {
token: `Bearer ${localStorage.getItem("token")}`,
},
});

let doc: Doc;
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/utils/matchSocket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,7 @@ const MATCH_SOCKET_URL = "http://localhost:3002";
export const matchSocket = io(MATCH_SOCKET_URL, {
reconnectionAttempts: 3,
autoConnect: false,
auth: {
token: `Bearer ${localStorage.getItem("token")}`,
},
});

0 comments on commit 70c3c6d

Please sign in to comment.