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

@ -0,0 +1,69 @@
import type { ClientConfig } from "@inspector/core/client/types.js";
/** Form shape for install-level client settings (client.json). */
export interface ClientSettingsFormValues {
emaEnabled: boolean;
issuer: string;
clientId: string;
clientSecret: string;
}
export const EMPTY_CLIENT_SETTINGS: ClientSettingsFormValues = {
emaEnabled: false,
issuer: "",
clientId: "",
clientSecret: "",
};
export function clientConfigToFormValues(
config: ClientConfig,
): ClientSettingsFormValues {
const ema = config.enterpriseManagedAuth;
const idp = ema?.idp;
if (!idp) {
return { ...EMPTY_CLIENT_SETTINGS };
}
return {
emaEnabled: ema.enabled !== false,
issuer: idp.issuer,
clientId: idp.clientId,
clientSecret: idp.clientSecret ?? "",
};
}
function hasStoredIdpFields(values: ClientSettingsFormValues): boolean {
return (
values.issuer.trim() !== "" ||
values.clientId.trim() !== "" ||
values.clientSecret !== ""
);
}
export function formValuesToClientConfig(
values: ClientSettingsFormValues,
): ClientConfig {
if (!hasStoredIdpFields(values)) {
return {};
}
const idp = {
issuer: values.issuer.trim(),
clientId: values.clientId.trim(),
clientSecret: values.clientSecret,
};
return {
enterpriseManagedAuth: {
enabled: values.emaEnabled,
idp,
},
};
}
/** Skip debounced persist while EMA is enabled but required IdP fields are blank. */
export function canPersistClientSettingsDraft(
values: ClientSettingsFormValues,
): boolean {
if (!values.emaEnabled) return true;
return values.issuer.trim() !== "" && values.clientId.trim() !== "";
}