🔃 fix: Refresh Token Edge Cases (#9625)

* 🔃 fix: Refresh Token Edge Cases

* chore: Update parameter type for setAuthTokens function
This commit is contained in:
Danny Avila 2025-09-13 21:36:45 -04:00 committed by GitHub
parent 5245aeea8f
commit 1a47601533
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 28 additions and 23 deletions

View file

@ -96,14 +96,17 @@ const refreshController = async (req, res) => {
return res.status(200).send({ token, user }); return res.status(200).send({ token, user });
} }
// Find the session with the hashed refresh token /** Session with the hashed refresh token */
const session = await findSession({ const session = await findSession(
{
userId: userId, userId: userId,
refreshToken: refreshToken, refreshToken: refreshToken,
}); },
{ lean: false },
);
if (session && session.expiration > new Date()) { if (session && session.expiration > new Date()) {
const token = await setAuthTokens(userId, res, session._id); const token = await setAuthTokens(userId, res, session);
res.status(200).send({ token, user }); res.status(200).send({ token, user });
} else if (req?.query?.retry) { } else if (req?.query?.retry) {
// Retrying from a refresh token request that failed (401) // Retrying from a refresh token request that failed (401)

View file

@ -357,23 +357,18 @@ const resetPassword = async (userId, token, password) => {
/** /**
* Set Auth Tokens * Set Auth Tokens
*
* @param {String | ObjectId} userId * @param {String | ObjectId} userId
* @param {Object} res * @param {ServerResponse} res
* @param {String} sessionId * @param {ISession | null} [session=null]
* @returns * @returns
*/ */
const setAuthTokens = async (userId, res, sessionId = null) => { const setAuthTokens = async (userId, res, _session = null) => {
try { try {
const user = await getUserById(userId); let session = _session;
const token = await generateToken(user);
let session;
let refreshToken; let refreshToken;
let refreshTokenExpires; let refreshTokenExpires;
if (sessionId) { if (session && session._id && session.expiration != null) {
session = await findSession({ sessionId: sessionId }, { lean: false });
refreshTokenExpires = session.expiration.getTime(); refreshTokenExpires = session.expiration.getTime();
refreshToken = await generateRefreshToken(session); refreshToken = await generateRefreshToken(session);
} else { } else {
@ -383,6 +378,9 @@ const setAuthTokens = async (userId, res, sessionId = null) => {
refreshTokenExpires = session.expiration.getTime(); refreshTokenExpires = session.expiration.getTime();
} }
const user = await getUserById(userId);
const token = await generateToken(user);
res.cookie('refreshToken', refreshToken, { res.cookie('refreshToken', refreshToken, {
expires: new Date(refreshTokenExpires), expires: new Date(refreshTokenExpires),
httpOnly: true, httpOnly: true,

View file

@ -873,6 +873,13 @@
* @typedef {import('@librechat/data-schemas').IMongoFile} MongoFile * @typedef {import('@librechat/data-schemas').IMongoFile} MongoFile
* @memberof typedefs * @memberof typedefs
*/ */
/**
* @exports ISession
* @typedef {import('@librechat/data-schemas').ISession} ISession
* @memberof typedefs
*/
/** /**
* @exports IBalance * @exports IBalance
* @typedef {import('@librechat/data-schemas').IBalance} IBalance * @typedef {import('@librechat/data-schemas').IBalance} IBalance

View file

@ -98,12 +98,6 @@ if (typeof window !== 'undefined') {
if (originalRequest.url?.includes('/api/auth/logout') === true) { if (originalRequest.url?.includes('/api/auth/logout') === true) {
return Promise.reject(error); return Promise.reject(error);
} }
if (originalRequest.url?.includes('/api/auth/refresh') === true) {
// Refresh token itself failed - redirect to login
console.log('Refresh token request failed, redirecting to login...');
window.location.href = '/login';
return Promise.reject(error);
}
if (error.response.status === 401 && !originalRequest._retry) { if (error.response.status === 401 && !originalRequest._retry) {
console.warn('401 error, refreshing token'); console.warn('401 error, refreshing token');
@ -124,7 +118,10 @@ if (typeof window !== 'undefined') {
isRefreshing = true; isRefreshing = true;
try { try {
const response = await refreshToken(); const response = await refreshToken(
// Handle edge case where we get a blank screen if the initial 401 error is from a refresh token request
originalRequest.url?.includes('api/auth/refresh') === true ? true : false,
);
const token = response?.token ?? ''; const token = response?.token ?? '';