[pull] main from danny-avila:main#147
Merged
Merged
Conversation
…yle providers (#13402) * feat(mcp/oauth): support audience parameter for Auth0/Cognito-style providers LibreChat already follows RFC 9728 (Protected Resource Metadata discovery) and RFC 8707 (resource indicators on /authorize). However, authorization servers that pre-date RFC 8707 — most prominently Auth0 — issue API-scoped access tokens only when an Auth0-specific 'audience' parameter is supplied on /authorize and /token. Without it, refresh_token responses strip the API audience and the next MCP call 401s. This change adds an optional 'audience' field to OAuthOptionsSchema and forwards it on: * pre-configured authorize URL build * discovered (DCR + RFC 9728) authorize URL build * refresh_token grant body 'resource' (RFC 8707) is left untouched and remains the standards-conformant route; 'audience' covers providers that ignore 'resource'. The two are independent — providers may accept either, both, or neither, so we forward whichever the operator configures. Schema tests added; no behavioral change for existing configs (field is optional with no default). Refs: MCP Authorization Spec 2025-06-18, RFC 9728, RFC 8707. * ci: build audience-fix branch image to ghcr.io/freudator86/librechat:audience-fix * Revert "ci: build audience-fix branch image to ghcr.io/freudator86/librechat:audience-fix" This reverts commit 7b3dfa6. * tests: assert audience param in authorize URL + refresh body; tighten schema (.min(1)); refine comment to reflect actual code paths Adresses PR review: - audience: z.string().min(1).optional() rejects empty strings - schema comment now precisely lists the two code paths (authorize + refresh_token grant); explicitly notes the authorization_code exchange intentionally does not receive audience because Auth0 binds it from the initial /authorize request - new MCPOAuthAudience.test.ts: 4 cases — authorize URL with/without audience, refresh body with/without audience — using a local recording HTTP server (no shared helper changes) - new schema test: empty-string audience is rejected * style: inline two logger.debug calls (prettier) * style: inline third audience-debug log (prettier) * feat(mcp/oauth): add forward_audience_on_refresh opt-out for strict token endpoints (Cognito) Addresses Codex review P2 'Avoid sending audience on refresh grants': the previous behavior forwarded audience on every refresh_token grant, which is correct for Auth0 (strips the audience claim otherwise) but is non-standard for Cognito and other strict OAuth 2.0 token endpoints that document refresh as grant_type + client_id + refresh_token only. New optional boolean 'forward_audience_on_refresh' (default: true) preserves the existing Auth0-friendly default while letting operators of strict tenants opt out cleanly. Schema + handler tests cover both cases. No behavioral change for existing configs. * style: format MCP OAuth refresh audience log --------- Co-authored-by: Tim Freudenthal <tim@allesknut.de> Co-authored-by: Danny Avila <danny@librechat.ai>
* fix: 'Key ... not found in userinfo token!' for OPENID_REQUIRED_ROLE_TOKEN_KIND Added userinfo as option to: OPENID_REQUIRED_ROLE_TOKEN_KIND handler. Added a small refactor to case match the OPENID_REQUIRED_ROLE_TOKEN_KIND setting and throw an explicit error. * Addressed review feedback and switched from case match to if/else * Extracted a function to be called so Admin and User token use same code to resolve token types
* fix: disable RUM user JWT auth * fix: remove stale RUM bootstrap import
…tings (#12669) The `set-balance` script called `getBalanceConfig()` without the app config, so it always reported balance as disabled regardless of the librechat.yaml configuration. Mirror the working `add-balance` script by loading the app config first and passing it into `getBalanceConfig`. Fixes #12413 Co-authored-by: Claude <noreply@anthropic.com>
…13204) * fix: honor admin-panel allowedDomains override at registration registerUser called getAppConfig({ baseOnly: true }), which short- circuits before any DB override merge. As a result, admin-panel edits to registration.allowedDomains were silently ignored at signup, even though they correctly apply to SSO callbacks via checkDomainAllowed (which calls getAppConfig() with the full resolution). The admin panel writes registration.allowedDomains to the __base__ principal in the configs collection. That principal is unconditionally injected by getApplicableConfigs (no user identity required), so a fully-resolved getAppConfig call picks up the override even before any user exists. This aligns native signup with the SSO paths and lets admins tighten or relax the allowed list without a backend restart. Per review feedback: pass the ALS tenantId explicitly. /api/auth runs through preAuthTenantMiddleware, which puts a tenantId into AsyncLocalStorage. Mongoose queries inside getApplicableConfigs are ALS-scoped, but the per-principal merged-config cache key uses the *explicit* tenantId parameter (see overrideCacheKey in packages/api/src/app/service.ts). If we leave tenantId undefined while ALS holds tenant A, the merged result caches at `__default__` — and a later request from tenant B would hit that entry, leaking tenant A's allowedDomains (and balance) across tenants. Reading getTenantId() and forwarding it makes the cache key match the DB scope, so __base__ overrides apply per-tenant correctly. Behavior when no admin override exists is unchanged (the merged config equals the YAML config; optional chaining handles missing fields). Tests in AuthService.spec.js: - Regression guard that getAppConfig is called with `{}` (no baseOnly) when ALS has no tenant — protects against reintroduction of the short-circuit. - New tenant-context test verifying getAppConfig({ tenantId }) when getTenantId() returns a tenant ID — protects against cross-tenant cache bleed. - Behavioral test confirming a disallowed domain returns 403 before any DB user lookup. * test: remove unused registerSchema import after merge resolution --------- Co-authored-by: Danny Avila <danny@librechat.ai>
- Update dependencies for @hyperdx/otel-web to 0.18.0 and @hyperdx/otel-web-session-recorder to 2.0.0 - Upgrade @hyperdx/instrumentation-exception to 0.3.0 and its dependencies - Adjust peer dependencies and engine requirements for compatibility
…3417) #12669 added `const { getAppConfig } = require('~/server/services/Config');` near the top of `config/set-balance.js` but the same import already existed lower in the file, producing: config/set-balance.js 9:9 error 'getAppConfig' is already defined no-redeclare The fork's sync CI surfaced this when its pre-commit hook ran eslint on the merged file. The upstream `eslint-ci.yml` is path-filtered on `api/**`, `client/**`, and `packages/**` — none of which match `config/**`, which is why CI didn't catch it upstream. Drop the second declaration. Functionally identical, lint clean. No other changes.
…nse (#13102) * 🔒 fix: Strip post-login fields from unauthenticated /api/config response Follow-up to #12490 reported in #12688. The unauthenticated /api/config response still included fields that are only consumed after login (helpAndFaqURL, sharedLinksEnabled, publicSharedLinksEnabled, showBirthdayIcon, analyticsGtmId, openidReuseTokens, allowAccountDeletion, customFooter, cloudFront). None of these are read by the auth pages (Login, Registration, RequestPasswordReset, ResetPassword, VerifyEmail, TwoFactorScreen, AuthLayout, Footer, SocialLoginRender). Split buildSharedPayload into two helpers: - buildPreLoginPayload returns only the fields the unauthenticated auth pages need (appTitle, server domain, social-login flags, OpenID/SAML labels and image URLs, registration/email/password-reset flags, minPasswordLength, ldap). - buildPostLoginPayload returns the post-login informational fields and is merged into the response only when req.user is present. Also move buildCloudFrontStartupConfig into the authenticated branch: useAppStartup is the only consumer and it runs after login. Tests updated: existing CloudFront and allowAccountDeletion assertions move to the authenticated context, and two new assertions cover the stripped fields (one for the post-login informational fields, one for cloudFront) in the unauthenticated context. Signed-off-by: ChrisJr404 <chris@hacknow.com> * fix: Request share-context startup config * fix: Pass share startup config into footer --------- Signed-off-by: ChrisJr404 <chris@hacknow.com> Co-authored-by: Danny Avila <danny@librechat.ai>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
See Commits and Changes for more details.
Created by
pull[bot] (v2.0.0-alpha.4)
Can you help keep this open source service alive? 💖 Please sponsor : )