mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-11-12 00:57:27 +00:00
* 🧿 fix: Bind Login Flows to the Browser That Started Them Social OAuth logins (Google, Facebook, GitHub, Discord, Apple) now store a random state in an HttpOnly cookie scoped to the callback path and only exchange the returned code when the callback carries that state. Apple's form_post callback uses a SameSite=None cookie, and passport-apple's own fixed state no longer bypasses the store. Local login, temp-token 2FA verification and admin local login reject requests a browser sent from another origin (Sec-Fetch-Site, falling back to Origin vs Host), trusting DOMAIN_CLIENT and DOMAIN_SERVER. Requests without browser fetch metadata, such as server-side callers, pass unchanged. The sessionless OAuth callbacks drop failureMessage, which wrote to req.session and turned any rejected callback into a 500. * 🧿 fix: Configure Login State Cookies and Localize Origin Rejections - OAuth state cookies take secureCookie and maxAgeMs from the caller; registration.oauthStateTtlMs in librechat.yaml sets the lifetime (default 10 minutes). - Secure deployments use a __Host- cookie at Path=/ so a sibling subdomain cannot plant a state. - The cookie keeps up to three pending states, so logins started in separate tabs all complete, and an unmatched callback no longer discards them. - passport-apple's preset state is dropped by deferStateToStore in packages/api. - The Origin fallback compares scheme and host against the request itself. - The same-origin rejection carries ErrorTypes.AUTH_CROSS_ORIGIN, which the login and 2FA screens show as a localized message. * 🧿 refactor: Sign Login State Against a Per-Browser Binding - The OAuth state store keeps a random per-browser binding in a __Host- cookie (reused across flows) and signs each state as issuedAt.nonce.HMAC(JWT_SECRET, provider, binding, issuedAt, nonce). Nothing is stored per flow, so any number of tabs complete, each state expires on its own clock, and concurrent starts no longer overwrite each other. - The experimental server entrypoint passes its app config to configureSocialLogins. - The same-origin guard also trusts an explicitly configured ADMIN_PANEL_URL. * 🧿 fix: Keep Separate Bindings for Racing First Logins A browser without a login-state binding now gets one under a cookie name with a random suffix, so two tabs that start their first login at once each keep their own binding. Later starts reuse an existing binding, and a callback is accepted when its state was signed for any binding the browser presents. * 🧿 fix: Sanitize Request-Derived Values in the Cross-Site Rejection Log The same-origin guard logged the request Origin and path verbatim. A non-browser client fully controls the Origin header, and the console transport uses a printf format that does not escape control characters, so a crafted value could forge log lines. Strip ASCII control characters (CR, LF, NUL and the rest) before logging, matching the pattern apiNotFound already uses.
9 lines
227 B
JavaScript
9 lines
227 B
JavaScript
const { createSameOriginGuard } = require('@librechat/api');
|
|
|
|
module.exports = createSameOriginGuard({
|
|
trustedOrigins: [
|
|
process.env.DOMAIN_CLIENT,
|
|
process.env.DOMAIN_SERVER,
|
|
process.env.ADMIN_PANEL_URL,
|
|
],
|
|
});
|