mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-09-21 21:50:49 +02:00
🔃 fix: Token Refresh in Browser Only, Redirect on Refresh Failure (#9583)
* 🔃 fix: Token Refresh in Browser Only, Redirect on Refresh Failure
* chore: Update import for SearchResultData and fix FormattedToolResponse type build warning
This commit is contained in:
parent
180046a3c5
commit
e3a645e8fb
2 changed files with 63 additions and 61 deletions
|
@ -8,7 +8,7 @@ import {
|
|||
StreamableHTTPOptionsSchema,
|
||||
Tools,
|
||||
} from 'librechat-data-provider';
|
||||
import type { UIResource, TPlugin, TUser } from 'librechat-data-provider';
|
||||
import type { SearchResultData, UIResource, TPlugin, TUser } from 'librechat-data-provider';
|
||||
import type * as t from '@modelcontextprotocol/sdk/types.js';
|
||||
import type { TokenMethods } from '@librechat/data-schemas';
|
||||
import type { FlowStateManager } from '~/flow/manager';
|
||||
|
@ -133,7 +133,7 @@ export type Artifacts =
|
|||
sources: FileSearchSource[];
|
||||
fileCitations?: boolean;
|
||||
};
|
||||
[Tools.web_search]?: import('librechat-data-provider').SearchResultData;
|
||||
[Tools.web_search]?: SearchResultData;
|
||||
files?: Array<{ id: string; name: string }>;
|
||||
session_id?: string;
|
||||
file_ids?: string[];
|
||||
|
@ -144,10 +144,7 @@ export type FormattedContentResult = [string | FormattedContent[], undefined | A
|
|||
|
||||
export type ImageFormatter = (item: ImageContent) => FormattedContent;
|
||||
|
||||
export type FormattedToolResponse = [
|
||||
string | FormattedContent[],
|
||||
{ content: FormattedContent[] } | undefined,
|
||||
];
|
||||
export type FormattedToolResponse = FormattedContentResult;
|
||||
|
||||
export type ParsedServerConfig = MCPOptions & {
|
||||
url?: string;
|
||||
|
|
|
@ -83,70 +83,75 @@ const processQueue = (error: AxiosError | null, token: string | null = null) =>
|
|||
failedQueue = [];
|
||||
};
|
||||
|
||||
axios.interceptors.response.use(
|
||||
(response) => response,
|
||||
async (error) => {
|
||||
const originalRequest = error.config;
|
||||
if (!error.response) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
if (typeof window !== 'undefined') {
|
||||
axios.interceptors.response.use(
|
||||
(response) => response,
|
||||
async (error) => {
|
||||
const originalRequest = error.config;
|
||||
if (!error.response) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
if (originalRequest.url?.includes('/api/auth/2fa') === true) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
if (originalRequest.url?.includes('/api/auth/logout') === true) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
if (originalRequest.url?.includes('/api/auth/2fa') === true) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
if (originalRequest.url?.includes('/api/auth/logout') === true) {
|
||||
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) {
|
||||
console.warn('401 error, refreshing token');
|
||||
originalRequest._retry = true;
|
||||
if (error.response.status === 401 && !originalRequest._retry) {
|
||||
console.warn('401 error, refreshing token');
|
||||
originalRequest._retry = true;
|
||||
|
||||
if (isRefreshing) {
|
||||
try {
|
||||
const token = await new Promise((resolve, reject) => {
|
||||
failedQueue.push({ resolve, reject });
|
||||
});
|
||||
originalRequest.headers['Authorization'] = 'Bearer ' + token;
|
||||
return await axios(originalRequest);
|
||||
} catch (err) {
|
||||
return Promise.reject(err);
|
||||
}
|
||||
}
|
||||
|
||||
isRefreshing = true;
|
||||
|
||||
if (isRefreshing) {
|
||||
try {
|
||||
const token = await new Promise((resolve, reject) => {
|
||||
failedQueue.push({ resolve, reject });
|
||||
});
|
||||
originalRequest.headers['Authorization'] = 'Bearer ' + token;
|
||||
return await axios(originalRequest);
|
||||
const response = await refreshToken();
|
||||
|
||||
const token = response?.token ?? '';
|
||||
|
||||
if (token) {
|
||||
originalRequest.headers['Authorization'] = 'Bearer ' + token;
|
||||
dispatchTokenUpdatedEvent(token);
|
||||
processQueue(null, token);
|
||||
return await axios(originalRequest);
|
||||
} else if (window.location.href.includes('share/')) {
|
||||
console.log(
|
||||
`Refresh token failed from shared link, attempting request to ${originalRequest.url}`,
|
||||
);
|
||||
} else {
|
||||
window.location.href = '/login';
|
||||
}
|
||||
} catch (err) {
|
||||
processQueue(err as AxiosError, null);
|
||||
return Promise.reject(err);
|
||||
} finally {
|
||||
isRefreshing = false;
|
||||
}
|
||||
}
|
||||
|
||||
isRefreshing = true;
|
||||
|
||||
try {
|
||||
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 ?? '';
|
||||
|
||||
if (token) {
|
||||
originalRequest.headers['Authorization'] = 'Bearer ' + token;
|
||||
dispatchTokenUpdatedEvent(token);
|
||||
processQueue(null, token);
|
||||
return await axios(originalRequest);
|
||||
} else if (window.location.href.includes('share/')) {
|
||||
console.log(
|
||||
`Refresh token failed from shared link, attempting request to ${originalRequest.url}`,
|
||||
);
|
||||
} else {
|
||||
window.location.href = '/login';
|
||||
}
|
||||
} catch (err) {
|
||||
processQueue(err as AxiosError, null);
|
||||
return Promise.reject(err);
|
||||
} finally {
|
||||
isRefreshing = false;
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.reject(error);
|
||||
},
|
||||
);
|
||||
return Promise.reject(error);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export default {
|
||||
get: _get,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue