Hello.World
Senior Member
Hi các bác, em đang gặp 1 vấn đề như sau ạ.
Để làm chức năng duy trì đăng nhập ở api e dùng jwt lưu refreshtoken (RT) vào database và đồng thời gửi RT cho client dưới dạng cookie, mỗi khi accesstoken (AT) hết hạn, nếu trong cookie có RT thì client sẽ tự động gửi RT về server để lấy AT và RT mới. Trang client của em có 1 vài trang cùng 1 thời điểm sẽ có nhiều component dispatch action có yêu cầu xác thực trong đó nên sẽ gửi yêu RT khi AT hết hạn, ví dụ như component Cart và Profile, khi AT hết hạn mà mình refresh trang thì đồng thời Cart và Profile sẽ dispatch action để lấy dữ liệu về, khi đó server sẽ response đầu tiên là ""user has been hacked" sau đó nó rơi vào vòng lặp response liên tục "Not found the key", RT và AT sẽ bị xoá.
Theo em hiểu thì khi yêu cầu refreshtoken của th Cart đang được xử lí khi đó RT hiện tại đã bị xoá khỏi database nhưng chưa được cấp RT mới thì đồng thời yêu cầu refreshtoken của th Profile cũng nhảy vào dẫn đến server xác định RT của user đó bị đánh cắp dẫn đến lỗi trên. Em đang tạm giải quyết ở phần client là cho bọn nó xếp hàng lần lượt, khi nào dispatch nhận response từ server thì mới cho dispatch tiếp theo gửi yêu cầu tới server, ví dụ: khi th Cart dispatch action và nhận response từ server thì th Profile mới đc dispatch action thì không bị lỗi trên nữa. Hiện tại em đang thắc mắc có phải logic của em bị sai ở đâu không, có cách giải quyết khác nào không ạ? Văn em lủng củng, mong các bác bỏ qua và giúp đỡ ạ
Code phần axios ở client:
Code request refreshToken của client:
Code xử lí refreshtoken ở api:
Để làm chức năng duy trì đăng nhập ở api e dùng jwt lưu refreshtoken (RT) vào database và đồng thời gửi RT cho client dưới dạng cookie, mỗi khi accesstoken (AT) hết hạn, nếu trong cookie có RT thì client sẽ tự động gửi RT về server để lấy AT và RT mới. Trang client của em có 1 vài trang cùng 1 thời điểm sẽ có nhiều component dispatch action có yêu cầu xác thực trong đó nên sẽ gửi yêu RT khi AT hết hạn, ví dụ như component Cart và Profile, khi AT hết hạn mà mình refresh trang thì đồng thời Cart và Profile sẽ dispatch action để lấy dữ liệu về, khi đó server sẽ response đầu tiên là ""user has been hacked" sau đó nó rơi vào vòng lặp response liên tục "Not found the key", RT và AT sẽ bị xoá.
Theo em hiểu thì khi yêu cầu refreshtoken của th Cart đang được xử lí khi đó RT hiện tại đã bị xoá khỏi database nhưng chưa được cấp RT mới thì đồng thời yêu cầu refreshtoken của th Profile cũng nhảy vào dẫn đến server xác định RT của user đó bị đánh cắp dẫn đến lỗi trên. Em đang tạm giải quyết ở phần client là cho bọn nó xếp hàng lần lượt, khi nào dispatch nhận response từ server thì mới cho dispatch tiếp theo gửi yêu cầu tới server, ví dụ: khi th Cart dispatch action và nhận response từ server thì th Profile mới đc dispatch action thì không bị lỗi trên nữa. Hiện tại em đang thắc mắc có phải logic của em bị sai ở đâu không, có cách giải quyết khác nào không ạ? Văn em lủng củng, mong các bác bỏ qua và giúp đỡ ạ
Code phần axios ở client:
JavaScript:
const axiosPrivate = axios.create({
baseURL: BASE_URL,
headers: {
'Content-Type': 'application/json'
},
withCredentials: true
});
axiosPrivate.interceptors.request.use(
config => {
if (!config.headers['accesstoken']) {
config.headers['accesstoken'] = `Bearer ${getLocalAccessToken()}`;
}
return config;
}, (error) => Promise.reject(error)
)
axiosPrivate.interceptors.response.use(
response => response,
async (error) => {
const prevRequestConfig = error?.config;
if (error?.response?.status === 403 && error?.response?.data === "logout") {
clearLocalStorage();
return window.location.reload();
}
if (error?.response?.status === 401 && !prevRequestConfig.sent) {
const newAccessToken = await refreshToken();
return axiosPrivate({
...prevRequestConfig,
headers: {
...prevRequestConfig.headers,
accesstoken: `Bearer ${newAccessToken}`,
sent: true
}
});
}
return Promise.reject(error);
}
);
Code request refreshToken của client:
JavaScript:
import axiosPrivate from "../shared/axios/requestMethod";
import { updateLocalAccessToken } from "./localStorage";
export const refreshToken = async () => {
const res = await axiosPrivate.get('/refreshToken/client');
updateLocalAccessToken(res.data.accessToken);
return res.data.accessToken;
}
Code xử lí refreshtoken ở api:
JavaScript:
export const refreshTokenClient = async (req, res) => {
const cookies = req.cookies;
if (!cookies?.jwtClient) return res.status(401).json("Not found the key");
const refreshToken = cookies.jwtClient;
res.clearCookie('jwtClient', { httpOnly: true, sameSite: 'None', secure: true });
const user = await Customer.findOne({ refreshToken: refreshToken }).exec();
if (!user) {
jwt.verify(refreshToken, process.env.REFRESH_TOKEN_KEY_CLIENT, async (err, decoded) => {
if (err) return res.status(401).json(err);
const hackedUser = await Customer.findOne({ _id: decoded._id }).exec();
hackedUser.refreshToken = [];
await hackedUser.save();
})
return res.status(401).json("user has been hacked");
}
const newRTArr = user.refreshToken.filter(item => item !== refreshToken);
jwt.verify(refreshToken, process.env.REFRESH_TOKEN_KEY_CLIENT, async (err, decoded) => {
if (err) {
user.refreshToken = [...newRTArr];
await user.save();
return res.status(401).json(err);
}
if (user._id != decoded._id) return res.status(401).json("????");
const accessToken = jwt.sign(
{
_id: user._id,
isActive: user.isActive,
role: user.role
},
process.env.JWT_KEY_CLIENT,
{ expiresIn: '15m' }
);
const newRefreshToken = jwt.sign(
{
_id: user._id,
role: user.role,
isActive: user.isActive
},
process.env.REFRESH_TOKEN_KEY_CLIENT
)
user.refreshToken = [...newRTArr, newRefreshToken];
await user.save();
res.cookie('jwtClient', newRefreshToken, { httpOnly: true, sameSite: 'None', secure: true, maxAge: 24 * 60 * 60 * 1000 });
res.status(201).json({ accessToken });
})
};