mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-01-03 09:08:52 +01:00
Removed unnecessary debug logging statements in the extractOpenIDTokenInfo function to streamline the code and improve readability. This change enhances the clarity of the function's logic without altering its functionality.
176 lines
4.9 KiB
TypeScript
176 lines
4.9 KiB
TypeScript
import { logger } from '@librechat/data-schemas';
|
|
import type { IUser } from '@librechat/data-schemas';
|
|
|
|
export interface OpenIDTokenInfo {
|
|
accessToken?: string;
|
|
idToken?: string;
|
|
expiresAt?: number;
|
|
userId?: string;
|
|
userEmail?: string;
|
|
userName?: string;
|
|
claims?: Record<string, unknown>;
|
|
}
|
|
|
|
interface FederatedTokens {
|
|
access_token?: string;
|
|
id_token?: string;
|
|
refresh_token?: string;
|
|
expires_at?: number;
|
|
}
|
|
|
|
function isFederatedTokens(obj: unknown): obj is FederatedTokens {
|
|
if (!obj || typeof obj !== 'object') {
|
|
return false;
|
|
}
|
|
return 'access_token' in obj || 'id_token' in obj || 'expires_at' in obj;
|
|
}
|
|
|
|
const OPENID_TOKEN_FIELDS = [
|
|
'ACCESS_TOKEN',
|
|
'ID_TOKEN',
|
|
'USER_ID',
|
|
'USER_EMAIL',
|
|
'USER_NAME',
|
|
'EXPIRES_AT',
|
|
] as const;
|
|
|
|
export function extractOpenIDTokenInfo(user: IUser | null | undefined): OpenIDTokenInfo | null {
|
|
if (!user) {
|
|
return null;
|
|
}
|
|
|
|
try {
|
|
if (user.provider !== 'openid' && !user.openidId) {
|
|
return null;
|
|
}
|
|
|
|
const tokenInfo: OpenIDTokenInfo = {};
|
|
|
|
if ('federatedTokens' in user && isFederatedTokens(user.federatedTokens)) {
|
|
const tokens = user.federatedTokens;
|
|
logger.debug('[extractOpenIDTokenInfo] Found federatedTokens:', {
|
|
has_access_token: !!tokens.access_token,
|
|
has_id_token: !!tokens.id_token,
|
|
has_refresh_token: !!tokens.refresh_token,
|
|
expires_at: tokens.expires_at,
|
|
});
|
|
tokenInfo.accessToken = tokens.access_token;
|
|
tokenInfo.idToken = tokens.id_token;
|
|
tokenInfo.expiresAt = tokens.expires_at;
|
|
} else if ('openidTokens' in user && isFederatedTokens(user.openidTokens)) {
|
|
const tokens = user.openidTokens;
|
|
logger.debug('[extractOpenIDTokenInfo] Found openidTokens');
|
|
tokenInfo.accessToken = tokens.access_token;
|
|
tokenInfo.idToken = tokens.id_token;
|
|
tokenInfo.expiresAt = tokens.expires_at;
|
|
}
|
|
|
|
tokenInfo.userId = user.openidId || user.id;
|
|
tokenInfo.userEmail = user.email;
|
|
tokenInfo.userName = user.name || user.username;
|
|
|
|
if (tokenInfo.idToken) {
|
|
try {
|
|
const payload = JSON.parse(
|
|
Buffer.from(tokenInfo.idToken.split('.')[1], 'base64').toString(),
|
|
);
|
|
tokenInfo.claims = payload;
|
|
|
|
if (payload.sub) tokenInfo.userId = payload.sub;
|
|
if (payload.email) tokenInfo.userEmail = payload.email;
|
|
if (payload.name) tokenInfo.userName = payload.name;
|
|
if (payload.exp) tokenInfo.expiresAt = payload.exp;
|
|
} catch (jwtError) {
|
|
logger.warn('Could not parse ID token claims:', jwtError);
|
|
}
|
|
}
|
|
|
|
return tokenInfo;
|
|
} catch (error) {
|
|
logger.error('Error extracting OpenID token info:', error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export function isOpenIDTokenValid(tokenInfo: OpenIDTokenInfo | null): boolean {
|
|
if (!tokenInfo || !tokenInfo.accessToken) {
|
|
return false;
|
|
}
|
|
|
|
if (tokenInfo.expiresAt) {
|
|
const now = Math.floor(Date.now() / 1000);
|
|
if (now >= tokenInfo.expiresAt) {
|
|
logger.warn('OpenID token has expired');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
export function processOpenIDPlaceholders(
|
|
value: string,
|
|
tokenInfo: OpenIDTokenInfo | null,
|
|
): string {
|
|
if (!tokenInfo || typeof value !== 'string') {
|
|
return value;
|
|
}
|
|
|
|
let processedValue = value;
|
|
|
|
for (const field of OPENID_TOKEN_FIELDS) {
|
|
const placeholder = `{{LIBRECHAT_OPENID_${field}}}`;
|
|
if (!processedValue.includes(placeholder)) {
|
|
continue;
|
|
}
|
|
|
|
let replacementValue = '';
|
|
|
|
switch (field) {
|
|
case 'ACCESS_TOKEN':
|
|
replacementValue = tokenInfo.accessToken || '';
|
|
break;
|
|
case 'ID_TOKEN':
|
|
replacementValue = tokenInfo.idToken || '';
|
|
break;
|
|
case 'USER_ID':
|
|
replacementValue = tokenInfo.userId || '';
|
|
break;
|
|
case 'USER_EMAIL':
|
|
replacementValue = tokenInfo.userEmail || '';
|
|
break;
|
|
case 'USER_NAME':
|
|
replacementValue = tokenInfo.userName || '';
|
|
break;
|
|
case 'EXPIRES_AT':
|
|
replacementValue = tokenInfo.expiresAt ? String(tokenInfo.expiresAt) : '';
|
|
break;
|
|
}
|
|
|
|
processedValue = processedValue.replace(new RegExp(placeholder, 'g'), replacementValue);
|
|
}
|
|
|
|
const genericPlaceholder = '{{LIBRECHAT_OPENID_TOKEN}}';
|
|
if (processedValue.includes(genericPlaceholder)) {
|
|
const replacementValue = tokenInfo.accessToken || '';
|
|
processedValue = processedValue.replace(new RegExp(genericPlaceholder, 'g'), replacementValue);
|
|
}
|
|
|
|
return processedValue;
|
|
}
|
|
|
|
export function createBearerAuthHeader(tokenInfo: OpenIDTokenInfo | null): string {
|
|
if (!tokenInfo || !tokenInfo.accessToken) {
|
|
return '';
|
|
}
|
|
|
|
return `Bearer ${tokenInfo.accessToken}`;
|
|
}
|
|
|
|
export function isOpenIDAvailable(): boolean {
|
|
const openidClientId = process.env.OPENID_CLIENT_ID;
|
|
const openidClientSecret = process.env.OPENID_CLIENT_SECRET;
|
|
const openidIssuer = process.env.OPENID_ISSUER;
|
|
|
|
return !!(openidClientId && openidClientSecret && openidIssuer);
|
|
}
|