I updated some of my Controller files to implement refresh token rotation, and that included my refreshTokenController file, which used to return a new access token on receiving an api call with a valid refresh token cookie. However, since then, it's throwing a Version Error on receiving the api call, but I can't figure out why the error is specifically coming from the third instance of .save() on line 61 (i.e., the error disappears only when I comment out the third instance of .save(), but not when I comment out either or both of the previous two instances of .save() on lines 32 and 49). Any insight would be appreciated. Thanks.
1 const jwt = require('jsonwebtoken');
2 const { defineModel } = require('../services/dbHandler');
3 const { userSchema } = require('../schemas/userSchema');
4 const { authTokenErr, cookieOptions, serverErr } = require('../utils');
5
6 const refreshAccessToken = async (req, res) => {
7 const prevRefreshToken = req.cookies?.refToken;
8
9 if (!prevRefreshToken) return res.status(401).json(authTokenErr);
10
11 res.clearCookie('refToken', prevRefreshToken, cookieOptions);
12
13 try {
14 const User = await defineModel('global', 'User', userSchema);
15
16 const dbUserWithToken = await User.findOne({
17 refreshToken: prevRefreshToken,
18 }).exec();
19
20 if (!dbUserWithToken) {
21 jwt.verify(
22 prevRefreshToken,
23 process.env.REFRESH_TOKEN_SECRET,
24 async (err, payload) => {
25 if (err) return res.status(401).json(authTokenErr);
26
27 const compromisedDbUser = await User.findOne({
28 _id: payload._uid,
29 }).exec();
30
31 compromisedDbUser.refreshToken = [];
32 await compromisedDbUser.save();
33 }
34 );
35
36 return res.status(401).json(authTokenErr);
37 }
38
39 const updatedTokenArr = dbUserWithToken.refreshToken.filter(
40 (token) => token !== prevRefreshToken
41 );
42
43 jwt.verify(
44 prevRefreshToken,
45 process.env.REFRESH_TOKEN_SECRET,
46 async (err, payload) => {
47 if (err || payload._uid !== dbUserWithToken._id.toString()) {
48 dbUserWithToken.refreshToken = [...updatedTokenArr];
49 const result = await dbUserWithToken.save();
50
51 return res.status(401).json(authTokenErr);
52 }
53
54 const renewedRefreshToken = dbUserWithToken.generateRefreshToken();
55 const renewedAccessToken = dbUserWithToken.generateAccessToken();
56
57 dbUserWithToken.refreshToken = [
58 ...updatedTokenArr,
59 renewedRefreshToken,
60 ];
61 await dbUserWithToken.save(); // <---- Cause of Version Error
62
63 res.cookie('refToken', renewedRefreshToken, cookieOptions);
64
65 return res.status(200).json({ accessToken: renewedAccessToken });
66 }
67 );
68 } catch (err) {
69 res.status(500).json(serverErr);
70 console.error(err);
71 }
72 };
73
74 module.exports = { refreshAccessToken };
CodePudding user response:
I also found that this was only occurring with my useEffect api fetches. Seeing as I'm now on react 18, I tried disabling strict mode, and the Version Error disappeared. So in the end, the versioning error was caused by react 18's new double-rendering of all useEffect hooks in strict mode (and had nothing to do with my 3 instances of document.save() in the same file after all). Now I just have to figure out why my clean-up function is not working on my useFetch hook.