mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-02-14 14:38:11 +01:00
* fix(openid): distinguish ID tokens from access tokens in federated auth Fix OpenID Connect token handling to properly distinguish ID tokens from access tokens. ID tokens and access tokens are now stored and propagated separately, preventing token placeholders from resolving to identical values. - AuthService.js: Added idToken field to session storage - openIdJwtStrategy.js: Updated to read idToken from session - openidStrategy.js: Explicitly included id_token in federatedTokens - Test suites: Added comprehensive test coverage for token distinction Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(openid): add separate openid_id_token cookie for ID token storage Store the OIDC ID token in its own cookie rather than relying solely on the access token, ensuring correct token type is used for identity verification vs API authorization. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test(openid): add JWT strategy cookie fallback tests Cover the token source resolution logic in openIdJwtStrategy: session-only, cookie-only, partial session fallback, raw Bearer fallback, and distinct id_token/access_token from cookies. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
66 lines
2.5 KiB
JavaScript
66 lines
2.5 KiB
JavaScript
const cookies = require('cookie');
|
|
const { isEnabled } = require('@librechat/api');
|
|
const { logger } = require('@librechat/data-schemas');
|
|
const { logoutUser } = require('~/server/services/AuthService');
|
|
const { getOpenIdConfig } = require('~/strategies');
|
|
|
|
const logoutController = async (req, res) => {
|
|
const parsedCookies = req.headers.cookie ? cookies.parse(req.headers.cookie) : {};
|
|
const isOpenIdUser = req.user?.openidId != null && req.user?.provider === 'openid';
|
|
|
|
/** For OpenID users, read refresh token from session; for others, use cookie */
|
|
let refreshToken;
|
|
if (isOpenIdUser && req.session?.openidTokens) {
|
|
refreshToken = req.session.openidTokens.refreshToken;
|
|
delete req.session.openidTokens;
|
|
}
|
|
refreshToken = refreshToken || parsedCookies.refreshToken;
|
|
|
|
try {
|
|
const logout = await logoutUser(req, refreshToken);
|
|
const { status, message } = logout;
|
|
|
|
res.clearCookie('refreshToken');
|
|
res.clearCookie('openid_access_token');
|
|
res.clearCookie('openid_id_token');
|
|
res.clearCookie('openid_user_id');
|
|
res.clearCookie('token_provider');
|
|
const response = { message };
|
|
if (
|
|
isOpenIdUser &&
|
|
isEnabled(process.env.OPENID_USE_END_SESSION_ENDPOINT) &&
|
|
process.env.OPENID_ISSUER
|
|
) {
|
|
const openIdConfig = getOpenIdConfig();
|
|
if (!openIdConfig) {
|
|
logger.warn(
|
|
'[logoutController] OpenID config not found. Please verify that the open id configuration and initialization are correct.',
|
|
);
|
|
} else {
|
|
const endSessionEndpoint = openIdConfig
|
|
? openIdConfig.serverMetadata().end_session_endpoint
|
|
: null;
|
|
if (endSessionEndpoint) {
|
|
const endSessionUrl = new URL(endSessionEndpoint);
|
|
/** Redirect back to app's login page after IdP logout */
|
|
const postLogoutRedirectUri =
|
|
process.env.OPENID_POST_LOGOUT_REDIRECT_URI || `${process.env.DOMAIN_CLIENT}/login`;
|
|
endSessionUrl.searchParams.set('post_logout_redirect_uri', postLogoutRedirectUri);
|
|
response.redirect = endSessionUrl.toString();
|
|
} else {
|
|
logger.warn(
|
|
'[logoutController] end_session_endpoint not found in OpenID issuer metadata. Please verify that the issuer is correct.',
|
|
);
|
|
}
|
|
}
|
|
}
|
|
return res.status(status).send(response);
|
|
} catch (err) {
|
|
logger.error('[logoutController]', err);
|
|
return res.status(500).json({ message: err.message });
|
|
}
|
|
};
|
|
|
|
module.exports = {
|
|
logoutController,
|
|
};
|