const cookies = require('cookie'); const jwt = require('jsonwebtoken'); const openIdClient = require('openid-client'); const { isEnabled } = require('@librechat/api'); const { logger } = require('@librechat/data-schemas'); const { requestPasswordReset, setOpenIDAuthTokens, resetPassword, setAuthTokens, registerUser, } = require('~/server/services/AuthService'); const { findUser, getUserById, deleteAllUserSessions, findSession } = require('~/models'); const { getOpenIdConfig } = require('~/strategies'); const { getGraphApiToken } = require('~/server/services/GraphTokenService'); const registrationController = async (req, res) => { try { const response = await registerUser(req.body); const { status, message } = response; res.status(status).send({ message }); } catch (err) { logger.error('[registrationController]', err); return res.status(500).json({ message: err.message }); } }; const resetPasswordRequestController = async (req, res) => { try { const resetService = await requestPasswordReset(req); if (resetService instanceof Error) { return res.status(400).json(resetService); } else { return res.status(200).json(resetService); } } catch (e) { logger.error('[resetPasswordRequestController]', e); return res.status(400).json({ message: e.message }); } }; const resetPasswordController = async (req, res) => { try { const resetPasswordService = await resetPassword( req.body.userId, req.body.token, req.body.password, ); if (resetPasswordService instanceof Error) { return res.status(400).json(resetPasswordService); } else { await deleteAllUserSessions({ userId: req.body.userId }); return res.status(200).json(resetPasswordService); } } catch (e) { logger.error('[resetPasswordController]', e); return res.status(400).json({ message: e.message }); } }; const refreshController = async (req, res) => { const refreshToken = req.headers.cookie ? cookies.parse(req.headers.cookie).refreshToken : null; const token_provider = req.headers.cookie ? cookies.parse(req.headers.cookie).token_provider : null; if (!refreshToken) { return res.status(200).send('Refresh token not provided'); } if (token_provider === 'openid' && isEnabled(process.env.OPENID_REUSE_TOKENS) === true) { try { const openIdConfig = getOpenIdConfig(); const tokenset = await openIdClient.refreshTokenGrant(openIdConfig, refreshToken); const claims = tokenset.claims(); const user = await findUser({ email: claims.email }); if (!user) { return res.status(401).redirect('/login'); } const token = setOpenIDAuthTokens(tokenset, res, user._id.toString()); return res.status(200).send({ token, user }); } catch (error) { logger.error('[refreshController] OpenID token refresh error', error); return res.status(403).send('Invalid OpenID refresh token'); } } try { const payload = jwt.verify(refreshToken, process.env.JWT_REFRESH_SECRET); const user = await getUserById(payload.id, '-password -__v -totpSecret -backupCodes'); if (!user) { return res.status(401).redirect('/login'); } const userId = payload.id; if (process.env.NODE_ENV === 'CI') { const token = await setAuthTokens(userId, res); return res.status(200).send({ token, user }); } /** Session with the hashed refresh token */ const session = await findSession( { userId: userId, refreshToken: refreshToken, }, { lean: false }, ); if (session && session.expiration > new Date()) { const token = await setAuthTokens(userId, res, session); res.status(200).send({ token, user }); } else if (req?.query?.retry) { // Retrying from a refresh token request that failed (401) res.status(403).send('No session found'); } else if (payload.exp < Date.now() / 1000) { res.status(403).redirect('/login'); } else { res.status(401).send('Refresh token expired or not found for this user'); } } catch (err) { logger.error(`[refreshController] Refresh token: ${refreshToken}`, err); res.status(403).send('Invalid refresh token'); } }; const graphTokenController = async (req, res) => { try { // Validate user is authenticated via Entra ID if (!req.user.openidId || req.user.provider !== 'openid') { return res.status(403).json({ message: 'Microsoft Graph access requires Entra ID authentication', }); } // Check if OpenID token reuse is active (required for on-behalf-of flow) if (!isEnabled(process.env.OPENID_REUSE_TOKENS)) { return res.status(403).json({ message: 'SharePoint integration requires OpenID token reuse to be enabled', }); } // Extract access token from Authorization header const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith('Bearer ')) { return res.status(401).json({ message: 'Valid authorization token required', }); } // Get scopes from query parameters const scopes = req.query.scopes; if (!scopes) { return res.status(400).json({ message: 'Graph API scopes are required as query parameter', }); } const accessToken = authHeader.substring(7); // Remove 'Bearer ' prefix const tokenResponse = await getGraphApiToken(req.user, accessToken, scopes); res.json(tokenResponse); } catch (error) { logger.error('[graphTokenController] Failed to obtain Graph API token:', error); res.status(500).json({ message: 'Failed to obtain Microsoft Graph token', }); } }; module.exports = { refreshController, registrationController, resetPasswordController, resetPasswordRequestController, graphTokenController, };