* fix(ui): load MCP tool configuration tools via the OBO/passthrough-aware GET path
* fix(mcp): admin-only include_disabled_tools so the settings UI shows toggled-off tools
* fix(ui): repopulate MCP server edit form when server data loads after mount (OAuth return)
* fix(ui): persist MCP OAuth token on save and return to the Settings tab after authorize
* fix(ui): scope MCP OAuth callback to the initiating form so create and edit flows don't cross-talk
* fix(ui): derive OAuth-return Settings tab via lazy state init instead of setState-in-effect
* Fix MCP OAuth edit token handling
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
(cherry picked from commit 51ba6e39cd)
When the user has visited both the dev UI (e.g. localhost:3000) and the
proxy UI (e.g. localhost:4000) in the same tab, logging out from the dev
origin produced an infinite logout/login redirect.
The proxy-side LoginPage's "is the user still authenticated?" check
was reading getCookie("token"), which falls back to sessionStorage when
document.cookie has no token. The cross-origin clearTokenCookies() call
from the dev origin can clear cookies on the shared hostname, but cannot
reach sessionStorage on the proxy origin (sessionStorage is per-origin),
so the fallback returned a stale token and LoginPage interpreted the
user as logged in, redirecting back to the dev origin. Dev origin then
saw no cookie and redirected to LoginPage, repeating ~20x per second.
This change introduces getCookieFromDocument(), a cookie-only read with
no sessionStorage fallback, and uses it in LoginPage's already-logged-in
check. The HttpOnly-reverse-proxy defense from PR #23532 is unaffected:
storeLoginToken still writes both the JS cookie at /ui and the
sessionStorage backup, and getCookie still falls back for callers that
want the full read path.
* feat(mcp): cache OAuth token client-side so Tools tab loads without re-auth
After a user creates an OAuth MCP server and completes the authorization
flow, the resulting access token is now stored in sessionStorage keyed by
server_id. The MCP Tools tab reads this cached token and includes it as
an MCP auth header when listing and invoking tools, so the user never sees
an empty tool list. When the session ends (tab close / new browser) an
Authorize button re-triggers the flow without leaving the Tools screen.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
* fix(ui/mcp): surface listMCPTools 401 errors so auth gate reappears
listMCPTools previously swallowed all errors (including HTTP 401) by
returning a synthetic { tools: [], error: 'network_error', ... } payload.
That made the useQuery retry-on-401 guard and mcpToolsError dead code,
so expired OAuth tokens never re-triggered the auth gate.
- Throw an enhanced Error with .status attached on non-2xx responses
(still preserves the legacy shape for true network failures so the
caller can render a generic message without crashing).
- Clear the cached OAuth session token when the tools query fails with
401, mirroring callMCPTool's onError handler so the Authorize button
is shown again.
- Surface mcpToolsError in the existing error banner.
Co-authored-by: Yassin Kortam <yassin@berri.ai>
* fix(mcp-tools): stable onSuccess + reuse parsed flow state
- Pass stable setOauthToken setter directly as onSuccess to avoid
recreating useToolsOAuthFlow's resumeOAuthFlow on every render.
- Reuse the already-parsed FLOW_STATE_KEY value (peeked) instead of
re-reading and re-parsing sessionStorage in resumeOAuthFlow.
Co-authored-by: Yassin Kortam <yassin@berri.ai>
* fix(ui/mcp): restore listMCPTools never-throws contract
The previous fix made listMCPTools throw on HTTP errors while still
returning a synthetic object on network errors. This inconsistent
contract broke existing callers (MCPToolPermissions, MCPAppsPanel,
MCPConnectPicker) which inspect result.error / result.message and
expect the function to never throw.
- Return a normalized { tools: [], error, message, status, ... }
object on HTTP errors (instead of throwing) so all callers see a
consistent shape and the user-visible error text from
result.message is preserved.
- Convert the returned error object into a thrown Error inside the
one caller that needs it — the useQuery in mcp_tools.tsx — so the
401 retry/onError handlers still trigger and clear the cached
OAuth token.
Co-authored-by: Yassin Kortam <yassin@berri.ai>
* fix greptile
* fix(mcp): align OAuth header alias lookup with dashboard sanitization
Backend auth header resolution now matches x-mcp-{alias} keys produced by
the dashboard sanitizer, and the Tools tab re-syncs OAuth tokens when
serverId changes.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(mcp): widen auth header lookup types for list_tools
Accept legacy str | dict server auth maps and annotate list_tools
server_auth_header as Union[str, dict] for mypy.
Co-authored-by: Cursor <cursoragent@cursor.com>
* refactor(ui): extract shared buildCallbackUrl/clearStorage for MCP OAuth hooks
Hoist the duplicate buildCallbackUrl and clearStorage helpers out of
useToolsOAuthFlow and useUserMcpOAuthFlow into a new shared module
src/hooks/mcpOAuthUtils.ts so the two hooks cannot drift if the URL
construction or storage cleanup logic needs to change.
Co-authored-by: Yassin Kortam <yassin@berri.ai>
* fix(ui): don't gate M2M OAuth MCP servers behind interactive authorize
M2M (client_credentials) OAuth servers share auth_type="oauth2" with
interactive PKCE servers, but the backend fetches their token internally
and they typically lack a user authorization endpoint. Gating tool
listing on them rendered an Authorize button that would fail or redirect
incorrectly. Detect M2M via the presence of token_url (matching the
existing heuristic in mcp_server_edit.tsx) and skip the auth gate.
Co-authored-by: Yassin Kortam <yassin@berri.ai>
* fix(ui/mcp): return error shape when listMCPTools JSON parse fails
Restore the never-throws contract when response.json() fails on a 2xx
body so callers do not receive null and crash on result.tools.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Yassin Kortam <yassin@berri.ai>
Admin Viewer (proxy_admin_viewer) was being blocked from endpoints it should
be able to read. Most visibly the UI Logs page rendered empty because every
filter and detail call (/spend/logs/ui, /spend/logs/ui/{id},
/spend/logs/session/ui, /customer/list) was rejected at the route_checks
layer even though the underlying handlers permit admin-viewer.
Backend:
- Extend admin_viewer_routes to include spend_tracking_routes,
/customer/{list,info}, /spend/logs/* detail routes, callback / config /
budget / alerting reads, and model cost map status/source.
- Replace bare `user_role != PROXY_ADMIN` checks in read-only handlers
(/budget/list, /budget/settings, /alerting/settings, /invitation/info,
/config/field/info, /config/list, /schedule/model_cost_map_reload/status,
/model/cost_map/source) with `_user_has_admin_view()`.
UI:
- Add `rolesAllowedToViewWriteScopedPages` (rolesWithWriteAccess + Admin
Viewer) and use it for the "Models + Endpoints" and "Agents" sidebar
items so admin viewers see them read-only. Playground stays gated by
rolesWithWriteAccess (cost-incurring).
- Hide Add / Edit / Delete buttons in the LLM Credentials panel for
non-proxy-admin viewers.
Tests:
- 31 parametrized route_checks cases for the Logs + settings endpoints,
with internal-user negative coverage to ensure the gate isn't widened.
- 9 handler-level integration tests (FastAPI TestClient) verifying
admin viewer is no longer blocked at the handler layer.
- New leftnav cases asserting Playground hidden / Models + Agents / Logs
visible to Admin Viewer.
- New roles + credentials test cases for the UI write-gate.
Relative labels ("today", "in 2 days", "on May 12, 2026") mixed three
shapes in one column, breaking scannability. Always render MMM D, YYYY
for consistency and easier at-a-glance comparison across members.
Adds a formatBudgetReset helper (dayjs-based, with validity guard) that
renders the next reset as "today" / "in N days" / "on MMM D, YYYY". The
team budget card now shows the team's reset timestamp and the member-
default reset (when a shared team_member_budget is configured), and the
Members tab gains a Budget Reset column per member.
Deduplicates base64UrlEncode, generateCodeVerifier, and
generateCodeChallenge which were copy-pasted across useMcpOAuthFlow
and useUserMcpOAuthFlow hooks.
Remove the silent try/catch from setSecureItem so OAuth hooks can
surface actionable "enable storage" guidance instead of a cryptic
"state lost" error after the round-trip. Add a local try/catch in
ChatUI where the storage write is non-critical.
- Dockerfile.health_check: HEALTHCHECK now verifies the script is intact
instead of unconditionally exiting 0
- secureStorage.ts: replace deprecated escape/unescape with
encodeURIComponent/decodeURIComponent; don't delete legacy values on
decode failure so in-flight flows can time out naturally
- OAuth callback: add same-origin check before redirecting to stored
return URL
Port security fixes from litellm_v1.82.3.dev.6:
- Use secureStorage (sessionStorage wrapper) instead of raw storage for tokens
- Add URL validation for stored worker URLs to prevent open redirects
- Add same-origin checks before redirecting to stored return URLs
- Harden Dockerfile.health_check with non-root user and exec-form HEALTHCHECK
Unit Tests: Proxy DB Operations / proxy-db (auth-checks, tests/proxy_unit_tests/test_auth_checks.py tests/proxy_unit_tests/test_user_api_key_auth.py, 20, 8) (push) Has been cancelled
Unit Tests: Proxy DB Operations / proxy-db (remaining, tests/proxy_unit_tests --ignore=tests/proxy_unit_tests/test_key_generate_prisma.py --ignore=tests/proxy_unit_tests/test_auth_checks.py --ignore=tests/proxy_unit_tests/test_user_api_key_auth.py, 20, 8) (push) Has been cancelled
* fix(ui): resolve login redirect loop when reverse proxy adds HttpOnly to cookies
When LiteLLM is behind nginx-ingress or similar with security-hardened
configs, the reverse proxy adds HttpOnly to all Set-Cookie headers. This
makes the JWT token unreadable by JavaScript, causing an infinite login
redirect loop. Fix by returning the JWT token in the /v2/login response
body so the frontend can set a JS-accessible cookie directly.
Fixes#19663
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address Greptile review feedback
- Add window guard to setTokenCookie for SSR consistency with clearTokenCookies
- Add SSR test for window undefined case
- Add code comment explaining why JWT is included in response body
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address second round of Greptile review feedback
- Add loginCall integration tests verifying setTokenCookie is called with
token and skipped when absent (backward-compatibility path)
- Use encodeURIComponent/decodeURIComponent in setTokenCookie/getCookie
for defense-in-depth against non-standard token formats
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Update ui/litellm-dashboard/src/utils/cookieUtils.ts
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* Update ui/litellm-dashboard/src/utils/cookieUtils.ts
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* fix(ui): use sessionStorage instead of cookie for login token storage
Replace setTokenCookie (which is a no-op when reverse proxy adds HttpOnly)
with storeLoginToken using sessionStorage. Add sessionStorage fallback to
getCookie so the token is found even when the cookie is HttpOnly. Also handle
'=' in cookie values with .slice(1).join("=") and clear sessionStorage on
logout.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(ui): use shared getCookie in page.tsx and user_dashboard.tsx
Replace local getCookie functions in page.tsx and user_dashboard.tsx
with the shared one from cookieUtils that has the sessionStorage
fallback. Without this, the HttpOnly cookie fix was incomplete —
page.tsx (the dashboard entry point) could not read the token,
causing the redirect loop to persist.
Also scope the sessionStorage fallback to the "token" key only,
and clear sessionStorage in page.tsx deleteCookie.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(ui): scope deleteCookie sessionStorage cleanup to token key only
Also document the sessionStorage cross-tab trade-off: per-tab scope
means users behind an HttpOnly proxy must log in once per tab, but
this is intentional to avoid localStorage XSS exposure.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Update ui/litellm-dashboard/src/utils/cookieUtils.ts
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* Apply suggestion from @greptile-apps[bot]
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* style: remove stray double blank line in user_dashboard.tsx
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(ui): guard storeLoginToken against empty/whitespace-only tokens
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(ui): preserve sessionStorage token across beforeunload clear
The existing beforeunload handler calls sessionStorage.clear() to
flush cached UI data on page refresh. This also wiped the token
stored by storeLoginToken, re-introducing the redirect loop after
any page refresh in the HttpOnly proxy scenario. Now the token is
saved and restored across the clear.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(ui): set JS-accessible cookie at /ui path as HttpOnly workaround
sessionStorage alone is unreliable. Also set the token via
document.cookie at path=/ui — nginx only adds HttpOnly to server-set
Set-Cookie headers, so a JS-set cookie is always readable.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ui): use dynamic cookie path based on server_root_path
Hardcoded path=/ui breaks when LiteLLM is deployed with a custom
server_root_path. Now derives the cookie path from serverRootPath
so it works at /ui, /myapp/ui, etc.
Also reuse clearTokenCookies() in deleteCookie() to avoid duplication.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(ui): remove circular dependency in cookieUtils.ts
Derive the UI cookie path from window.location.pathname instead of
importing serverRootPath from networking.tsx. This breaks the
cookieUtils → networking → cookieUtils cycle that could cause
serverRootPath to be undefined under certain bundler configurations.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ui): harden getUiCookiePath regex and add missing tests
- Use regex /\/ui(?=\/|$)/ to match "/ui" only as a full path segment,
preventing false matches on paths like "/my-ui-tool/login".
- Add unit tests for storeLoginToken empty/whitespace guard and
cookie-at-/ui-path behavior.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: fix Black formatting in audit_logs.py
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix CI: formatting, test params, remove token from login JSON
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: reformat with Black 23.x to match CI
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: keep token in login JSON body for UI storeLoginToken flow
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: use storeLoginToken in exchangeLoginCode, add credentials include
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* revert: remove unrelated changes from HttpOnly cookie fix branch
Reset files not related to the login cookie fix back to main:
- prometheus.py, bedrock converse, guardrail handler
- auth_checks.py, reset_budget_job.py, audit_logs.py
- test_user_api_key_auth.py
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Revert "revert: remove unrelated changes from HttpOnly cookie fix branch"
This reverts commit 0684a1e275.
* Revert "fix: use storeLoginToken in exchangeLoginCode, add credentials include"
This reverts commit 866405f443.
* Revert "fix: keep token in login JSON body for UI storeLoginToken flow"
This reverts commit 086c41640c.
* Revert "fix: reformat with Black 23.x to match CI"
This reverts commit b2c3334c88.
* Revert "fix CI: formatting, test params, remove token from login JSON"
This reverts commit 2905d47bd4.
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Tests added for: UiLoadingSpinner, HashicorpVaultEmptyPlaceholder,
PageVisibilitySettings, errorUtils, and mcpToolCrudClassification.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat(ui): group MCP tools by CRUD risk category in tool permission panels
Adds a CRUD-classification layer to the MCP tool allowlist UI so admins
can allow/block an entire risk category (Read / Create / Update / Delete)
with a single toggle instead of managing a flat list of individual tools.
- New `mcpToolCrudClassification.ts` utility: regex-based classifier that
buckets tool names/descriptions into read/create/update/delete/unknown
- New `McpCrudPermissionPanel` component: collapsible sections per CRUD
group, group-level Switch toggle, individual tool checkboxes, risk badges
(green Safe / yellow Medium / red High Risk)
- `mcp_tool_configuration.tsx`: adds "Risk Groups / Flat List" radio toggle;
defaults to the CRUD-grouped view, flat list is still accessible
- `MCPToolPermissions.tsx` (key/team assignment): replaces flat checkboxes
with the CRUD panel; adds per-server view toggle; delete tools are blocked
by default for newly-added servers (safer default for key/team scoping)
No backend or schema changes — uses existing `allowed_tools` and
`mcp_tool_permissions` fields.
* fix(mcp): OAuth2 chat connect - tools fetch, auth flow, and status fixes
- schema.prisma: add missing MCP table fields (approval_status, submitted_by, submitted_at, reviewed_at, review_notes) to prevent destructive migrations
- rest_endpoints.py: inject user OAuth token via extra_headers for OAuth2 servers so tools list is populated; add server name->UUID resolution so MCPConnectPicker name lookups work
- mcp_registry.json: fix Atlassian defaults (transport: http, url: .../v1/mcp)
- ChatPage.tsx: read mcpOauthReturn param to init sidebarView="apps" on OAuth return, clean up param after mount
- MCPAppsPanel.tsx: auto-add OAuth2 servers to selectedServers when credential detected; onConnect also enables server for chat; disconnect removes from selectedServers
- mcp_servers.tsx: sort servers by created_at DESC
- useUserMcpOAuthFlow.tsx: append mcpOauthReturn=apps to return URL so Apps panel is mounted on return
* fix(mcp-crud-ui): address greptile review feedback
- use Checkbox (not Switch) for group toggle so indeterminate works
- add toolPermissionsRef to avoid stale closure race on concurrent server fetches
- remove unused blockDeleteByDefault prop from McpCrudPermissionPanel
- classify tools by name first; fall back to description only when name yields no match
- add Risk Groups / Flat List toggle to mcp_tool_configuration.tsx
* fix(mcp-crud-ui): address greptile 3/5 review
- remove non-functional XIcon remove-server button (no onRemoveServer prop wired)
- fix stale closure in MCPAppsPanel auto-enable effect: use serversRef/selectedServersRef
- remove utility re-export from McpCrudPermissionPanel (classifyToolOp, groupToolsByCrud)
- remove redundant selectedTools.length === 0 guard (always true when !toolPermissions[id])
* fix(mcp-crud-ui): address greptile 3/5 review round 2
- check READ_RE before DELETE_RE in classifyToolOp so tools like
get_removed_entries are not silently blocked by delete-by-default
- expand undefined (allow-all) to full tool name list instead of
collapsing to [] (allow-none) in MCPToolPermissions and mcp_tool_configuration
- log OAuth credential fetch failures instead of silently swallowing them
* fix: cursor-pointer on read-only rows, stable sort, simplify handleCrudPanelChange
* fix: sanitize user_id/server_id in log to prevent log injection
* fix: add OAuth headers to call_tool_rest_api, fix stale accessToken closure, fix group toggle on filtered subset
* fix: batch OAuth creds query, hide empty CRUD groups on search, onChange stability
* fix: double-add race, conditional bulk query, narrow DELETE_RE, hoist search input
* fix(mcp): clear oauthConnected on deselect; null guard on allowedTools prop
* fix(mcp): remove user-provided values from debug log to fix log-injection lint
* fix(mcp): fix allowedTools undefined semantics; remove unused import and color field
* fix(mcp): fix OpenAPI OAuth flow — transport mapping, error messages, and discovery bypass
Three bugs fixed to make the end-to-end OAuth flow work for OpenAPI MCP servers:
1. **Transport mapping in getTemporaryPayload**: `TRANSPORT.OPENAPI` is a UI-only concept;
the backend only accepts `"http"`, `"sse"`, or `"stdio"`. The pre-OAuth temp-session
call was sending `transport: "openapi"` and getting a 422. Fixed by mapping to `"http"`.
2. **deriveErrorMessage handles FastAPI 422 arrays**: FastAPI validation errors return
`detail` as an array of `{loc, msg, type}` objects. The shared error extractor was
returning the array directly, causing `Error: [object Object]`. Fixed to map each
item to its `.msg` field.
3. **Skip OAuth discovery when authorization_url already provided**: `build_mcp_server_from_table`
was unconditionally calling `_descovery_metadata(server_url)` for OAuth servers. For
OpenAPI servers the url is the spec JSON file, not the API base — this caused a timeout
fetching e.g. the GitHub spec (2 MB). Fixed by skipping discovery when `authorization_url`
is already set.
Also: collapsible auth section in MCP server form, "Create OAuth App →" link next to
Client ID when a docs URL is available (e.g. GitHub OAuth App creation page), and
`extractErrorMessage` helper in `useMcpOAuthFlow` for cleaner error display.
* refactor(mcp): extract needs_discovery flag and reduceStaticHeaders helper
* feat(mcp): user OAuth connect flow — OAuthConnectModal, MCPCredentialsTab, useUserMcpOAuthFlow
Adds the user-facing MCP OAuth2 PKCE connect flow:
- OAuthConnectModal: modal that launches the PKCE flow for a user to connect to an MCP server
- MCPCredentialsTab: credentials management tab in the MCP apps panel
- useUserMcpOAuthFlow: hook that handles the full PKCE auth code exchange for user-level connections
- MCPAppsPanel: wires up the new credentials tab and connect modal
- ChatPage: further cleanup after responses-API revert
- db.py / mcp_management_endpoints.py / _types.py: backend support for storing user MCP credentials
* fix(mcp): make client_id optional in /authorize — use server's stored client_id when not provided
* address greptile review feedback
* fix(mcp): narrow bare except to RecordNotFoundError in BYOK credential delete
* refactor(mcp): move inline imports to module level in db.py
* docs(claude): add MCP OAuth, transport mapping, and browser storage patterns
* fix(security): remove accessToken from sessionStorage in OAuth flow state
The LiteLLM API key was being serialised into sessionStorage as part of
StoredFlowState. After the OAuth redirect the component re-mounts with the
same accessToken prop, so it never needed to be stored. Read it from props
in resumeOAuthFlow instead.
* fix(ui): remove duplicate extractErrorMessage, sessionStorage-only in admin OAuth hook, call delete API on disconnect
* fix(ui): guard resumeOAuthFlow against wrong hook instance consuming OAuth result
* fix(ui): separate OAuth result keys per flow, sessionStorage-only, surface revoke errors
* fix(ui): remove dead OAuthConnectModal, revert tsconfig jsx mode to preserve
* fix(mcp): guard BYOK overwrite in oauth credential store, raise clear error when client_id absent
* fix: forward OAuth error params in callback, fix BYOK guard exception handling in db.py
Enables deep-linking directly to the key creation modal with prefilled
form data via URL parameters, including support for preserving these
deep-links through SSO authentication flows.
Key Creation Deep-links:
- Auto-open key creation modal via ?create=true parameter
- Prefill form fields from URL parameters (team_id, key_alias, models, etc.)
- Role-based access control for auto-open (requires write access)
- Race condition protection for redirect handling
Example: /ui?create=true&team_id=abc&key_alias=my-key&models=gpt-4,claude-3
SSO Return URL Preservation:
- Cookie-based return URL storage (works across ports for SSO flows)
- URL validation to prevent open redirect attacks
- Support for both dev and production environments
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add fallback in sort to prevent NoneType and str comparison
* Hide Default Team Settings from Proxy Admin Viewers
---------
Co-authored-by: Krish Dholakia <krrishdholakia@gmail.com>
simplifies handleCreate function for CreateKeyModal
moved hooks, components, files where I want them
moved virtual-key page to (console) for common layout
added layout for refactored console file structure, sidebar
wired teams to refactored components
fixing type issue
wired new top level hook useAuthorized
Adds routing to unrefactored pages
Update leftnav.tsx
Update useAuthorized.ts
useAuthorized in virtual-keys/page and deprecation notices
prelim VirtualKeyViewPage, removed old comment
Delete page.tsx
* feat(bulk_user_update/): support updating all users on proxy
* fix(bulk_edit_user.tsx): persist user settings when 'add to team' clicked
* fix(team_endpoints.py): bulk add all proxy users to team
supports flow from UI to add all existing users to a team
* fix: minor fixes
* feat(user_edit_view.tsx): support setting no default model on user edit
allows preventing users from calling models outside team scope
* fix(user_edit_view.tsx): prevent triggering submit when 'cancel' is clicked
* refactor(internal_user_endpoints.py): refactor to reduce function size
* added the mcp_connect copy button to key_info_view
* user id copy button
* copy button on session view
* model id copy
* more copy buttons
* icon size