mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-09-22 06:00:56 +02:00

* feat(sharepoint): integrate SharePoint file picker and download workflow Introduces end‑to‑end SharePoint import support: * Token exchange with Microsoft Graph and scope management (`useSharePointToken`) * Re‑usable hooks: `useSharePointPicker`, `useSharePointDownload`, `useSharePointFileHandling` * FileSearch dropdown now offers **From Local Machine** / **From SharePoint** sources and gracefully falls back when SharePoint is disabled * Agent upload model, `AttachFileMenu`, and `DropdownPopup` extended for SharePoint files and sub‑menus * Blurry overlay with progress indicator and `maxSelectionCount` limit during downloads * Cache‑flush utility (`config/flush-cache.js`) supporting Redis & filesystem, with dry‑run and npm script * Updated `SharePointIcon` (uses `currentColor`) and new i18n keys * Bug fixes: placeholder syntax in progress message, picker event‑listener cleanup * Misc style and performance optimizations * Fix ESLint warnings --------- Co-authored-by: Atef Bellaaj <slalom.bellaaj@external.daimlertruck.com>
86 lines
2.7 KiB
JavaScript
86 lines
2.7 KiB
JavaScript
const { getOpenIdConfig } = require('~/strategies/openidStrategy');
|
|
const { logger } = require('~/config');
|
|
const { CacheKeys } = require('librechat-data-provider');
|
|
const getLogStores = require('~/cache/getLogStores');
|
|
const client = require('openid-client');
|
|
|
|
/**
|
|
* Get Microsoft Graph API token using existing token exchange mechanism
|
|
* @param {Object} user - User object with OpenID information
|
|
* @param {string} accessToken - Current access token from Authorization header
|
|
* @param {string} scopes - Graph API scopes for the token
|
|
* @param {boolean} fromCache - Whether to try getting token from cache first
|
|
* @returns {Promise<Object>} Graph API token response with access_token and expires_in
|
|
*/
|
|
async function getGraphApiToken(user, accessToken, scopes, fromCache = true) {
|
|
try {
|
|
if (!user.openidId) {
|
|
throw new Error('User must be authenticated via Entra ID to access Microsoft Graph');
|
|
}
|
|
|
|
if (!accessToken) {
|
|
throw new Error('Access token is required for token exchange');
|
|
}
|
|
|
|
if (!scopes) {
|
|
throw new Error('Graph API scopes are required for token exchange');
|
|
}
|
|
|
|
const config = getOpenIdConfig();
|
|
if (!config) {
|
|
throw new Error('OpenID configuration not available');
|
|
}
|
|
|
|
const cacheKey = `${user.openidId}:${scopes}`;
|
|
const tokensCache = getLogStores(CacheKeys.OPENID_EXCHANGED_TOKENS);
|
|
|
|
if (fromCache) {
|
|
const cachedToken = await tokensCache.get(cacheKey);
|
|
if (cachedToken) {
|
|
logger.debug(`[GraphTokenService] Using cached Graph API token for user: ${user.openidId}`);
|
|
return cachedToken;
|
|
}
|
|
}
|
|
|
|
logger.debug(`[GraphTokenService] Requesting new Graph API token for user: ${user.openidId}`);
|
|
logger.debug(`[GraphTokenService] Requested scopes: ${scopes}`);
|
|
|
|
const grantResponse = await client.genericGrantRequest(
|
|
config,
|
|
'urn:ietf:params:oauth:grant-type:jwt-bearer',
|
|
{
|
|
scope: scopes,
|
|
assertion: accessToken,
|
|
requested_token_use: 'on_behalf_of',
|
|
},
|
|
);
|
|
|
|
const tokenResponse = {
|
|
access_token: grantResponse.access_token,
|
|
token_type: 'Bearer',
|
|
expires_in: grantResponse.expires_in || 3600,
|
|
scope: scopes,
|
|
};
|
|
|
|
await tokensCache.set(
|
|
cacheKey,
|
|
tokenResponse,
|
|
(grantResponse.expires_in || 3600) * 1000, // Convert to milliseconds
|
|
);
|
|
|
|
logger.debug(
|
|
`[GraphTokenService] Successfully obtained and cached Graph API token for user: ${user.openidId}`,
|
|
);
|
|
return tokenResponse;
|
|
} catch (error) {
|
|
logger.error(
|
|
`[GraphTokenService] Failed to acquire Graph API token for user ${user.openidId}:`,
|
|
error,
|
|
);
|
|
throw new Error(`Graph token acquisition failed: ${error.message}`);
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
getGraphApiToken,
|
|
};
|