csrf token handling, two jwt tokens
This commit is contained in:
@@ -14,7 +14,7 @@ const authenticateToken = async (req, res, next) => {
|
||||
}
|
||||
|
||||
try {
|
||||
const decoded = jwt.verify(token, process.env.JWT_SECRET);
|
||||
const decoded = jwt.verify(token, process.env.JWT_ACCESS_SECRET);
|
||||
const userId = decoded.id;
|
||||
|
||||
if (!userId) {
|
||||
@@ -78,7 +78,7 @@ const optionalAuth = async (req, res, next) => {
|
||||
}
|
||||
|
||||
try {
|
||||
const decoded = jwt.verify(token, process.env.JWT_SECRET);
|
||||
const decoded = jwt.verify(token, process.env.JWT_ACCESS_SECRET);
|
||||
const userId = decoded.id;
|
||||
|
||||
if (!userId) {
|
||||
|
||||
@@ -72,7 +72,8 @@ const getCSRFToken = (req, res) => {
|
||||
maxAge: 60 * 60 * 1000,
|
||||
});
|
||||
|
||||
res.json({ csrfToken: token });
|
||||
res.set("X-CSRF-Token", token);
|
||||
res.status(204).send();
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
|
||||
145
backend/package-lock.json
generated
145
backend/package-lock.json
generated
@@ -4067,22 +4067,43 @@
|
||||
"integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg=="
|
||||
},
|
||||
"node_modules/body-parser": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz",
|
||||
"integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==",
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.1.tgz",
|
||||
"integrity": "sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bytes": "^3.1.2",
|
||||
"content-type": "^1.0.5",
|
||||
"debug": "^4.4.0",
|
||||
"debug": "^4.4.3",
|
||||
"http-errors": "^2.0.0",
|
||||
"iconv-lite": "^0.6.3",
|
||||
"iconv-lite": "^0.7.0",
|
||||
"on-finished": "^2.4.1",
|
||||
"qs": "^6.14.0",
|
||||
"raw-body": "^3.0.0",
|
||||
"type-is": "^2.0.0"
|
||||
"raw-body": "^3.0.1",
|
||||
"type-is": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/body-parser/node_modules/iconv-lite": {
|
||||
"version": "0.7.0",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz",
|
||||
"integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/bowser": {
|
||||
@@ -4181,6 +4202,7 @@
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
@@ -4745,9 +4767,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
|
||||
"integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
|
||||
"version": "4.4.3",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
||||
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
@@ -5359,27 +5382,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/express-validator": {
|
||||
"version": "7.2.1",
|
||||
"resolved": "https://registry.npmjs.org/express-validator/-/express-validator-7.2.1.tgz",
|
||||
"integrity": "sha512-CjNE6aakfpuwGaHQZ3m8ltCG2Qvivd7RHtVMS/6nVxOM7xVGqr4bhflsm4+N5FP5zI7Zxp+Hae+9RE+o8e3ZOQ==",
|
||||
"version": "7.3.1",
|
||||
"resolved": "https://registry.npmjs.org/express-validator/-/express-validator-7.3.1.tgz",
|
||||
"integrity": "sha512-IGenaSf+DnWc69lKuqlRE9/i/2t5/16VpH5bXoqdxWz1aCpRvEdrBuu1y95i/iL5QP8ZYVATiwLFhwk3EDl5vg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"lodash": "^4.17.21",
|
||||
"validator": "~13.12.0"
|
||||
"validator": "~13.15.23"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/express-validator/node_modules/validator": {
|
||||
"version": "13.12.0",
|
||||
"resolved": "https://registry.npmjs.org/validator/-/validator-13.12.0.tgz",
|
||||
"integrity": "sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/extend": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
|
||||
@@ -5789,9 +5803,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/glob": {
|
||||
"version": "10.4.5",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
|
||||
"integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
|
||||
"version": "10.5.0",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
|
||||
"integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"foreground-child": "^3.1.0",
|
||||
"jackspeak": "^3.1.2",
|
||||
@@ -6006,26 +6021,23 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/http-errors": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
|
||||
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
|
||||
"dependencies": {
|
||||
"depd": "2.0.0",
|
||||
"inherits": "2.0.4",
|
||||
"setprototypeof": "1.2.0",
|
||||
"statuses": "2.0.1",
|
||||
"toidentifier": "1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/http-errors/node_modules/statuses": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
||||
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
|
||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
|
||||
"integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"depd": "~2.0.0",
|
||||
"inherits": "~2.0.4",
|
||||
"setprototypeof": "~1.2.0",
|
||||
"statuses": "~2.0.2",
|
||||
"toidentifier": "~1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/http-proxy-agent": {
|
||||
@@ -7145,9 +7157,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/js-yaml": {
|
||||
"version": "3.14.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
|
||||
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
|
||||
"version": "3.14.2",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz",
|
||||
"integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -8454,17 +8466,34 @@
|
||||
}
|
||||
},
|
||||
"node_modules/raw-body": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz",
|
||||
"integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==",
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz",
|
||||
"integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bytes": "3.1.2",
|
||||
"http-errors": "2.0.0",
|
||||
"iconv-lite": "0.6.3",
|
||||
"unpipe": "1.0.0"
|
||||
"bytes": "~3.1.2",
|
||||
"http-errors": "~2.0.1",
|
||||
"iconv-lite": "~0.7.0",
|
||||
"unpipe": "~1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/raw-body/node_modules/iconv-lite": {
|
||||
"version": "0.7.0",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz",
|
||||
"integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/react-is": {
|
||||
@@ -9812,6 +9841,7 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
||||
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
@@ -9917,9 +9947,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/validator": {
|
||||
"version": "13.15.15",
|
||||
"resolved": "https://registry.npmjs.org/validator/-/validator-13.15.15.tgz",
|
||||
"integrity": "sha512-BgWVbCI72aIQy937xbawcs+hrVaN/CZ2UwutgaJ36hGqRrLNM+f5LUT/YPRbo8IV/ASeFzXszezV+y2+rq3l8A==",
|
||||
"version": "13.15.23",
|
||||
"resolved": "https://registry.npmjs.org/validator/-/validator-13.15.23.tgz",
|
||||
"integrity": "sha512-4yoz1kEWqUjzi5zsPbAS/903QXSYp0UOtHsPpp7p9rHAw/W+dkInskAE386Fat3oKRROwO98d9ZB0G4cObgUyw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
|
||||
@@ -128,13 +128,13 @@ router.post(
|
||||
|
||||
const token = jwt.sign(
|
||||
{ id: user.id, jwtVersion: user.jwtVersion },
|
||||
process.env.JWT_SECRET,
|
||||
process.env.JWT_ACCESS_SECRET,
|
||||
{ expiresIn: "15m" } // Short-lived access token
|
||||
);
|
||||
|
||||
const refreshToken = jwt.sign(
|
||||
{ id: user.id, jwtVersion: user.jwtVersion, type: "refresh" },
|
||||
process.env.JWT_SECRET,
|
||||
process.env.JWT_REFRESH_SECRET,
|
||||
{ expiresIn: "7d" }
|
||||
);
|
||||
|
||||
@@ -223,13 +223,13 @@ router.post(
|
||||
|
||||
const token = jwt.sign(
|
||||
{ id: user.id, jwtVersion: user.jwtVersion },
|
||||
process.env.JWT_SECRET,
|
||||
process.env.JWT_ACCESS_SECRET,
|
||||
{ expiresIn: "15m" } // Short-lived access token
|
||||
);
|
||||
|
||||
const refreshToken = jwt.sign(
|
||||
{ id: user.id, jwtVersion: user.jwtVersion, type: "refresh" },
|
||||
process.env.JWT_SECRET,
|
||||
process.env.JWT_REFRESH_SECRET,
|
||||
{ expiresIn: "7d" }
|
||||
);
|
||||
|
||||
@@ -392,13 +392,13 @@ router.post(
|
||||
// Generate JWT tokens
|
||||
const token = jwt.sign(
|
||||
{ id: user.id, jwtVersion: user.jwtVersion },
|
||||
process.env.JWT_SECRET,
|
||||
process.env.JWT_ACCESS_SECRET,
|
||||
{ expiresIn: "15m" }
|
||||
);
|
||||
|
||||
const refreshToken = jwt.sign(
|
||||
{ id: user.id, jwtVersion: user.jwtVersion, type: "refresh" },
|
||||
process.env.JWT_SECRET,
|
||||
process.env.JWT_REFRESH_SECRET,
|
||||
{ expiresIn: "7d" }
|
||||
);
|
||||
|
||||
@@ -550,7 +550,7 @@ router.post(
|
||||
});
|
||||
}
|
||||
|
||||
const decoded = jwt.verify(accessToken, process.env.JWT_SECRET);
|
||||
const decoded = jwt.verify(accessToken, process.env.JWT_ACCESS_SECRET);
|
||||
const user = await User.findByPk(decoded.id);
|
||||
|
||||
if (!user) {
|
||||
@@ -625,7 +625,7 @@ router.post("/refresh", async (req, res) => {
|
||||
}
|
||||
|
||||
// Verify refresh token
|
||||
const decoded = jwt.verify(refreshToken, process.env.JWT_SECRET);
|
||||
const decoded = jwt.verify(refreshToken, process.env.JWT_REFRESH_SECRET);
|
||||
|
||||
if (!decoded.id || decoded.type !== "refresh") {
|
||||
return res.status(401).json({ error: "Invalid refresh token" });
|
||||
@@ -648,7 +648,7 @@ router.post("/refresh", async (req, res) => {
|
||||
// Generate new access token
|
||||
const newAccessToken = jwt.sign(
|
||||
{ id: user.id, jwtVersion: user.jwtVersion },
|
||||
process.env.JWT_SECRET,
|
||||
process.env.JWT_ACCESS_SECRET,
|
||||
{ expiresIn: "15m" }
|
||||
);
|
||||
|
||||
|
||||
@@ -392,7 +392,8 @@ router.get('/images/:filename',
|
||||
helmet.crossOriginResourcePolicy({ policy: "cross-origin" }),
|
||||
async (req, res) => {
|
||||
try {
|
||||
const { filename } = req.params;
|
||||
// Sanitize filename to prevent path traversal attacks
|
||||
const filename = path.basename(req.params.filename);
|
||||
|
||||
// Verify user is sender or receiver of a message with this image
|
||||
const message = await Message.findOne({
|
||||
|
||||
@@ -108,6 +108,7 @@ app.use(
|
||||
origin: process.env.FRONTEND_URL || "http://localhost:3000",
|
||||
credentials: true,
|
||||
optionsSuccessStatus: 200,
|
||||
exposedHeaders: ["X-CSRF-Token"],
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
@@ -38,8 +38,8 @@ const authenticateSocket = async (socket, next) => {
|
||||
return next(new Error("Authentication required"));
|
||||
}
|
||||
|
||||
// Verify JWT
|
||||
const decoded = jwt.verify(token, process.env.JWT_SECRET);
|
||||
// Verify JWT (access tokens only)
|
||||
const decoded = jwt.verify(token, process.env.JWT_ACCESS_SECRET);
|
||||
const userId = decoded.id;
|
||||
|
||||
if (!userId) {
|
||||
|
||||
@@ -39,7 +39,7 @@ const api = axios.create({
|
||||
export const fetchCSRFToken = async (): Promise<string> => {
|
||||
try {
|
||||
const response = await api.get("/auth/csrf-token");
|
||||
csrfToken = response.data.csrfToken || "";
|
||||
csrfToken = response.headers["x-csrf-token"] || "";
|
||||
return csrfToken || "";
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch CSRF token:", error);
|
||||
@@ -286,10 +286,14 @@ export const forumAPI = {
|
||||
deleteComment: (commentId: string) =>
|
||||
api.delete(`/forum/comments/${commentId}`),
|
||||
// Admin endpoints
|
||||
adminDeletePost: (id: string, reason: string) => api.delete(`/forum/admin/posts/${id}`, { data: { reason } }),
|
||||
adminRestorePost: (id: string) => api.patch(`/forum/admin/posts/${id}/restore`),
|
||||
adminDeleteComment: (id: string, reason: string) => api.delete(`/forum/admin/comments/${id}`, { data: { reason } }),
|
||||
adminRestoreComment: (id: string) => api.patch(`/forum/admin/comments/${id}/restore`),
|
||||
adminDeletePost: (id: string, reason: string) =>
|
||||
api.delete(`/forum/admin/posts/${id}`, { data: { reason } }),
|
||||
adminRestorePost: (id: string) =>
|
||||
api.patch(`/forum/admin/posts/${id}/restore`),
|
||||
adminDeleteComment: (id: string, reason: string) =>
|
||||
api.delete(`/forum/admin/comments/${id}`, { data: { reason } }),
|
||||
adminRestoreComment: (id: string) =>
|
||||
api.patch(`/forum/admin/comments/${id}/restore`),
|
||||
adminClosePost: (id: string) => api.patch(`/forum/admin/posts/${id}/close`),
|
||||
adminReopenPost: (id: string) => api.patch(`/forum/admin/posts/${id}/reopen`),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user