feat(web,core): enterprise-managed authorization (EMA) (#1509) (#1512)

Adds enterprise-managed authorization (EMA): browser-side IdP login + RFC 8693 token-exchange and JWT-bearer resource-token mint, per-server EMA opt-in in Server Settings, IdP config in Client Settings, and IdP clientSecret keychain storage. Includes the v2/main merge and review follow-ups (400 on malformed client config; normalizeIdpIssuer import consolidation).
This commit is contained in:
Bob Dickinson 2026-06-24 19:15:12 -07:00 • committed by GitHub
commit 5de4e911e3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -35,6 +35,7 @@ describe("ServerSettingsModal", () => {
<ServerSettingsModal
opened={false}
settings={emptySettings}
serverType="streamable-http"
isStdio={false}
onClose={vi.fn()}
onSettingsChange={vi.fn()}
@ -48,6 +49,7 @@ describe("ServerSettingsModal", () => {
<ServerSettingsModal
opened
settings={emptySettings}
serverType="streamable-http"
isStdio={false}
onClose={vi.fn()}
onSettingsChange={vi.fn()}
@ -57,6 +59,20 @@ describe("ServerSettingsModal", () => {
expect(screen.getByText("Custom Headers")).toBeInTheDocument();
});
it("hides the OAuth Settings section for stdio servers", () => {
renderWithMantine(
<ServerSettingsModal
opened
settings={emptySettings}
serverType="stdio"
isStdio
onClose={vi.fn()}
onSettingsChange={vi.fn()}
/>,
);
expect(screen.queryByText("OAuth Settings")).not.toBeInTheDocument();
});
it("invokes onClose when the CloseButton is clicked", async () => {
const user = userEvent.setup();
const onClose = vi.fn();
@ -64,6 +80,7 @@ describe("ServerSettingsModal", () => {
<ServerSettingsModal
opened
settings={emptySettings}
serverType="streamable-http"
isStdio={false}
onClose={onClose}
onSettingsChange={vi.fn()}
@ -85,6 +102,7 @@ describe("ServerSettingsModal", () => {
<ServerSettingsModal
opened
settings={emptySettings}
serverType="streamable-http"
isStdio={false}
onClose={vi.fn()}
onSettingsChange={onSettingsChange}
@ -105,6 +123,7 @@ describe("ServerSettingsModal", () => {
<ServerSettingsModal
opened
settings={initialSettings}
serverType="streamable-http"
isStdio={false}
onClose={vi.fn()}
onSettingsChange={onSettingsChange}
@ -126,6 +145,7 @@ describe("ServerSettingsModal", () => {
<ServerSettingsModal
opened
settings={initialSettings}
serverType="streamable-http"
isStdio={false}
onClose={vi.fn()}
onSettingsChange={onSettingsChange}
@ -148,6 +168,7 @@ describe("ServerSettingsModal", () => {
<ServerSettingsModal
opened
settings={emptySettings}
serverType="streamable-http"
isStdio={false}
onClose={vi.fn()}
onSettingsChange={onSettingsChange}
@ -168,6 +189,7 @@ describe("ServerSettingsModal", () => {
<ServerSettingsModal
opened
settings={initialSettings}
serverType="streamable-http"
isStdio={false}
onClose={vi.fn()}
onSettingsChange={onSettingsChange}
@ -191,6 +213,7 @@ describe("ServerSettingsModal", () => {
<ServerSettingsModal
opened
settings={initialSettings}
serverType="streamable-http"
isStdio={false}
onClose={vi.fn()}
onSettingsChange={onSettingsChange}
@ -212,6 +235,7 @@ describe("ServerSettingsModal", () => {
<ServerSettingsModal
opened
settings={emptySettings}
serverType="streamable-http"
isStdio={false}
onClose={vi.fn()}
onSettingsChange={onSettingsChange}
@ -232,6 +256,7 @@ describe("ServerSettingsModal", () => {
<ServerSettingsModal
opened
settings={emptySettings}
serverType="streamable-http"
isStdio={false}
onClose={vi.fn()}
onSettingsChange={onSettingsChange}
@ -245,6 +270,34 @@ describe("ServerSettingsModal", () => {
expect(call.oauthClientId).toBe("a");
});
it("calls onSettingsChange when toggling enterprise-managed authorization", async () => {
const user = userEvent.setup();
const onSettingsChange = vi.fn();
renderWithMantine(
<ServerSettingsModal
opened
settings={emptySettings}
serverType="streamable-http"
isStdio={false}
onClose={vi.fn()}
onSettingsChange={onSettingsChange}
/>,
);
await user.click(screen.getByRole("button", { name: "OAuth Settings" }));
await user.click(
screen.getByRole("checkbox", {
name: "Enterprise-managed authorization",
}),
);
expect(onSettingsChange).toHaveBeenCalledWith({
...emptySettings,
oauthClientId: "",
oauthClientSecret: "",
oauthScopes: "",
enterpriseManaged: true,
});
});
it("calls onSettingsChange with a blank row when adding a root", async () => {
const user = userEvent.setup();
const onSettingsChange = vi.fn();
@ -252,6 +305,7 @@ describe("ServerSettingsModal", () => {
<ServerSettingsModal
opened
settings={emptySettings}
serverType="streamable-http"
isStdio={false}
onClose={vi.fn()}
onSettingsChange={onSettingsChange}
@ -272,6 +326,7 @@ describe("ServerSettingsModal", () => {
<ServerSettingsModal
opened
settings={initialSettings}
serverType="streamable-http"
isStdio={false}
onClose={vi.fn()}
onSettingsChange={onSettingsChange}
@ -294,6 +349,7 @@ describe("ServerSettingsModal", () => {
<ServerSettingsModal
opened
settings={initialSettings}
serverType="streamable-http"
isStdio={false}
onClose={vi.fn()}
onSettingsChange={onSettingsChange}
@ -315,6 +371,7 @@ describe("ServerSettingsModal", () => {
<ServerSettingsModal
opened
settings={emptySettings}
serverType="streamable-http"
isStdio={false}
onClose={vi.fn()}
onSettingsChange={vi.fn()}
@ -359,6 +416,7 @@ describe("ServerSettingsModal", () => {
<ServerSettingsModal
opened
settings={emptySettings}
serverType="streamable-http"
isStdio={false}
onClose={vi.fn()}
onSettingsChange={vi.fn()}
@ -376,6 +434,7 @@ describe("ServerSettingsModal", () => {
<ServerSettingsModal
opened
settings={emptySettings}
serverType="stdio"
isStdio
onClose={vi.fn()}
onSettingsChange={onSettingsChange}
@ -401,6 +460,7 @@ describe("ServerSettingsModal", () => {
<ServerSettingsModal
opened
settings={{ ...emptySettings, env: [{ key: "A", value: "1" }] }}
serverType="stdio"
isStdio
onClose={vi.fn()}
onSettingsChange={onSettingsChange}
@ -424,6 +484,7 @@ describe("ServerSettingsModal", () => {
<ServerSettingsModal
opened
settings={{ ...emptySettings, env: [{ key: "A", value: "1" }] }}
serverType="stdio"
isStdio
onClose={vi.fn()}
onSettingsChange={onSettingsChange}
@ -444,6 +505,7 @@ describe("ServerSettingsModal", () => {
<ServerSettingsModal
opened
settings={{ ...emptySettings, cwd: "/srv" }}
serverType="stdio"
isStdio
onClose={vi.fn()}
onSettingsChange={onSettingsChange}