diff --git a/ui/litellm-dashboard/src/components/SSOModals.test.tsx b/ui/litellm-dashboard/src/components/SSOModals.test.tsx index 7d4b0254e0..002672d477 100644 --- a/ui/litellm-dashboard/src/components/SSOModals.test.tsx +++ b/ui/litellm-dashboard/src/components/SSOModals.test.tsx @@ -95,4 +95,38 @@ describe("SSOModals", () => { expect(getByText("URL must start with http:// or https://")).toBeInTheDocument(); }); }); + + it("should automatically remove trailing slash from the proxy base url", async () => { + const TestWrapper = () => { + const [form] = Form.useForm(); + return ( + {}} + handleAddSSOCancel={() => {}} + handleShowInstructions={() => {}} + handleInstructionsOk={() => {}} + handleInstructionsCancel={() => {}} + form={form} + accessToken={null} + ssoConfigured={false} + /> + ); + }; + + const { getByLabelText, container } = render(); + + // Fill in the proxy base url with a trailing slash + const urlInput = getByLabelText("PROXY BASE URL") as HTMLInputElement; + fireEvent.change(urlInput, { target: { value: "https://example.com/" } }); + + // Trigger blur to ensure normalization is applied + fireEvent.blur(urlInput); + + // Check that the trailing slash was removed by the normalize function + await waitFor(() => { + expect(urlInput.value).toBe("https://example.com"); + }); + }); }); diff --git a/ui/litellm-dashboard/src/components/SSOModals.tsx b/ui/litellm-dashboard/src/components/SSOModals.tsx index 6c5904a8ff..97b015f63b 100644 --- a/ui/litellm-dashboard/src/components/SSOModals.tsx +++ b/ui/litellm-dashboard/src/components/SSOModals.tsx @@ -309,16 +309,20 @@ const SSOModals: React.FC = ({ value?.trim()} + normalize={(value) => value?.trim().replace(/\/+$/, "")} rules={[ { required: true, message: "Please enter the proxy base url" }, { pattern: /^https?:\/\/.+/, message: "URL must start with http:// or https://", }, + { + pattern: /^https?:\/\/[^\s]+[^\/]$/, + message: "URL must not end with a trailing slash", + }, ]} > - +