mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-15 23:31:29 +00:00
fix(ui): only clear the unselected ID-JAG client-auth method on an expressed choice
This commit is contained in:
parent
e571ac7c3e
commit
99ca545920
4 changed files with 54 additions and 8 deletions
|
|
@ -20,9 +20,11 @@ const FieldLabel: React.FC<{ label: string; tooltip: string }> = ({ label, toolt
|
|||
const IdJagFormFields: React.FC<IdJagFormFieldsProps> = ({ isEditing = false }) => {
|
||||
const placeholderSuffix = isEditing ? " (leave blank to keep existing)" : "";
|
||||
const form = Form.useFormInstance();
|
||||
const clientAuthMethod =
|
||||
(Form.useWatch("id_jag_client_auth_method", form) as "client_secret" | "private_key_jwt" | undefined) ??
|
||||
"client_secret";
|
||||
const watchedMethod = Form.useWatch("id_jag_client_auth_method", form) as
|
||||
| "client_secret"
|
||||
| "private_key_jwt"
|
||||
| undefined;
|
||||
const clientAuthMethod = watchedMethod ?? (isEditing ? undefined : "client_secret");
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
@ -72,12 +74,14 @@ const IdJagFormFields: React.FC<IdJagFormFieldsProps> = ({ isEditing = false })
|
|||
/>
|
||||
}
|
||||
name="id_jag_client_auth_method"
|
||||
initialValue="client_secret"
|
||||
{...(isEditing ? {} : { initialValue: "client_secret" })}
|
||||
preserve={false}
|
||||
>
|
||||
<Select<"client_secret" | "private_key_jwt">
|
||||
className="rounded-lg"
|
||||
size="large"
|
||||
allowClear={isEditing}
|
||||
placeholder={isEditing ? "Keep existing method" : undefined}
|
||||
options={[
|
||||
{ value: "client_secret", label: <span className="font-medium">Client Secret</span> },
|
||||
{ value: "private_key_jwt", label: <span className="font-medium">Private Key JWT</span> },
|
||||
|
|
|
|||
|
|
@ -582,9 +582,9 @@ const CreateMCPServer: React.FC<CreateMCPServerProps> = ({
|
|||
|
||||
// The selected ID-JAG client-auth method is authoritative: null the other method's
|
||||
// fields so the backend's credentials merge cannot keep a stale method alive.
|
||||
if (restValues.auth_type === AUTH_TYPE.OAUTH2_ID_JAG) {
|
||||
if (restValues.auth_type === AUTH_TYPE.OAUTH2_ID_JAG && idJagClientAuthMethodRaw) {
|
||||
const idJagMethodNulls =
|
||||
(idJagClientAuthMethodRaw ?? "client_secret") === "private_key_jwt"
|
||||
idJagClientAuthMethodRaw === "private_key_jwt"
|
||||
? { client_secret: null }
|
||||
: { client_private_key: null, client_private_key_id: null, client_assertion_signing_alg: null };
|
||||
payload.credentials = { ...(payload.credentials ?? {}), ...idJagMethodNulls };
|
||||
|
|
|
|||
|
|
@ -393,6 +393,48 @@ describe("MCPServerEdit (auth type switch)", () => {
|
|||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("keeps the stored ID-JAG client-auth method when saved without expressing a choice", async () => {
|
||||
vi.mocked(networking.updateMCPServer).mockResolvedValue({
|
||||
...interactiveOAuthServer,
|
||||
auth_type: "oauth2_id_jag",
|
||||
});
|
||||
|
||||
render(
|
||||
<MCPServerEdit
|
||||
mcpServer={{
|
||||
...interactiveOAuthServer,
|
||||
auth_type: "oauth2_id_jag",
|
||||
token_exchange_endpoint: "https://org.example.com/oauth2/v1/token",
|
||||
}}
|
||||
accessToken="access-token"
|
||||
onCancel={vi.fn()}
|
||||
onSuccess={vi.fn()}
|
||||
availableAccessGroups={[]}
|
||||
/>,
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByPlaceholderText("https://your-org.okta.com/oauth2/v1/token")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
const saveButtons = screen.getAllByRole("button", { name: "Save Changes" });
|
||||
await act(async () => {
|
||||
fireEvent.click(saveButtons[0]);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(networking.updateMCPServer).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
const [, payload] = vi.mocked(networking.updateMCPServer).mock.calls[0];
|
||||
expect(payload.auth_type).toBe("oauth2_id_jag");
|
||||
const credentials = payload.credentials ?? {};
|
||||
expect(credentials.client_private_key).toBeUndefined();
|
||||
expect(credentials.client_private_key_id).toBeUndefined();
|
||||
expect(credentials.client_assertion_signing_alg).toBeUndefined();
|
||||
expect(credentials.client_secret).toBeUndefined();
|
||||
});
|
||||
|
||||
it("renders the ID-JAG arm on edit and nulls its shared fields when switching away", async () => {
|
||||
vi.mocked(networking.updateMCPServer).mockResolvedValue({
|
||||
...interactiveOAuthServer,
|
||||
|
|
|
|||
|
|
@ -952,9 +952,9 @@ const MCPServerEdit: React.FC<MCPServerEditProps> = ({
|
|||
|
||||
// The selected ID-JAG client-auth method is authoritative: explicit-null the other
|
||||
// method's stored fields so the backend's credentials merge cannot keep it alive.
|
||||
if (restValues.auth_type === AUTH_TYPE.OAUTH2_ID_JAG) {
|
||||
if (restValues.auth_type === AUTH_TYPE.OAUTH2_ID_JAG && idJagClientAuthMethodRaw) {
|
||||
const idJagMethodNulls =
|
||||
(idJagClientAuthMethodRaw ?? "client_secret") === "private_key_jwt"
|
||||
idJagClientAuthMethodRaw === "private_key_jwt"
|
||||
? { client_secret: null }
|
||||
: { client_private_key: null, client_private_key_id: null, client_assertion_signing_alg: null };
|
||||
payload.credentials = { ...(payload.credentials ?? {}), ...idJagMethodNulls };
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue