skybridge 0.0.0-dev.f8d2f8c → 0.0.0-dev.f8ef758
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +123 -124
- package/dist/cli/build-helpers.d.ts +7 -0
- package/dist/cli/build-helpers.js +82 -0
- package/dist/cli/build-helpers.js.map +1 -0
- package/dist/cli/build-helpers.test.d.ts +1 -0
- package/dist/cli/build-helpers.test.js +64 -0
- package/dist/cli/build-helpers.test.js.map +1 -0
- package/dist/cli/detect-port.d.ts +2 -2
- package/dist/cli/detect-port.js +9 -20
- package/dist/cli/detect-port.js.map +1 -1
- package/dist/cli/resolve-views-dir.d.ts +1 -0
- package/dist/cli/resolve-views-dir.js +17 -0
- package/dist/cli/resolve-views-dir.js.map +1 -0
- package/dist/cli/use-typescript-check.js +1 -1
- package/dist/cli/use-typescript-check.js.map +1 -1
- package/dist/commands/build.d.ts +0 -1
- package/dist/commands/build.js +18 -30
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/create.d.ts +9 -0
- package/dist/commands/create.js +30 -0
- package/dist/commands/create.js.map +1 -0
- package/dist/commands/dev.js +16 -0
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/start.js +7 -1
- package/dist/commands/start.js.map +1 -1
- package/dist/server/asset-base-url-transform-plugin.d.ts +1 -0
- package/dist/server/asset-base-url-transform-plugin.js +16 -1
- package/dist/server/asset-base-url-transform-plugin.js.map +1 -1
- package/dist/server/asset-base-url-transform-plugin.test.js +51 -1
- package/dist/server/asset-base-url-transform-plugin.test.js.map +1 -1
- package/dist/server/auth/discovery.d.ts +11 -0
- package/dist/server/auth/discovery.js +51 -0
- package/dist/server/auth/discovery.js.map +1 -0
- package/dist/server/auth/discovery.test.d.ts +1 -0
- package/dist/server/auth/discovery.test.js +93 -0
- package/dist/server/auth/discovery.test.js.map +1 -0
- package/dist/server/auth/index.d.ts +18 -0
- package/dist/server/auth/index.js +2 -0
- package/dist/server/auth/index.js.map +1 -0
- package/dist/server/auth/providers/auth0.d.ts +13 -0
- package/dist/server/auth/providers/auth0.js +18 -0
- package/dist/server/auth/providers/auth0.js.map +1 -0
- package/dist/server/auth/providers/auth0.test.d.ts +1 -0
- package/dist/server/auth/providers/auth0.test.js +33 -0
- package/dist/server/auth/providers/auth0.test.js.map +1 -0
- package/dist/server/auth/providers/clerk.d.ts +14 -0
- package/dist/server/auth/providers/clerk.js +20 -0
- package/dist/server/auth/providers/clerk.js.map +1 -0
- package/dist/server/auth/providers/clerk.test.d.ts +1 -0
- package/dist/server/auth/providers/clerk.test.js +48 -0
- package/dist/server/auth/providers/clerk.test.js.map +1 -0
- package/dist/server/auth/providers/custom.d.ts +17 -0
- package/dist/server/auth/providers/custom.js +28 -0
- package/dist/server/auth/providers/custom.js.map +1 -0
- package/dist/server/auth/providers/custom.test.d.ts +1 -0
- package/dist/server/auth/providers/custom.test.js +91 -0
- package/dist/server/auth/providers/custom.test.js.map +1 -0
- package/dist/server/auth/providers/descope.d.ts +14 -0
- package/dist/server/auth/providers/descope.js +14 -0
- package/dist/server/auth/providers/descope.js.map +1 -0
- package/dist/server/auth/providers/descope.test.d.ts +1 -0
- package/dist/server/auth/providers/descope.test.js +35 -0
- package/dist/server/auth/providers/descope.test.js.map +1 -0
- package/dist/server/auth/providers/stytch.d.ts +12 -0
- package/dist/server/auth/providers/stytch.js +17 -0
- package/dist/server/auth/providers/stytch.js.map +1 -0
- package/dist/server/auth/providers/stytch.test.d.ts +1 -0
- package/dist/server/auth/providers/stytch.test.js +33 -0
- package/dist/server/auth/providers/stytch.test.js.map +1 -0
- package/dist/server/auth/providers/workos.d.ts +11 -0
- package/dist/server/auth/providers/workos.js +16 -0
- package/dist/server/auth/providers/workos.js.map +1 -0
- package/dist/server/auth/providers/workos.test.d.ts +1 -0
- package/dist/server/auth/providers/workos.test.js +41 -0
- package/dist/server/auth/providers/workos.test.js.map +1 -0
- package/dist/server/auth/setup.d.ts +4 -0
- package/dist/server/auth/setup.js +51 -0
- package/dist/server/auth/setup.js.map +1 -0
- package/dist/server/auth/setup.test.d.ts +1 -0
- package/dist/server/auth/setup.test.js +185 -0
- package/dist/server/auth/setup.test.js.map +1 -0
- package/dist/server/auth/verify.d.ts +12 -0
- package/dist/server/auth/verify.js +38 -0
- package/dist/server/auth/verify.js.map +1 -0
- package/dist/server/auth/verify.test.d.ts +1 -0
- package/dist/server/auth/verify.test.js +100 -0
- package/dist/server/auth/verify.test.js.map +1 -0
- package/dist/server/auth.d.ts +20 -0
- package/dist/server/auth.js +28 -0
- package/dist/server/auth.js.map +1 -0
- package/dist/server/build-manifest.test.d.ts +1 -0
- package/dist/server/build-manifest.test.js +27 -0
- package/dist/server/build-manifest.test.js.map +1 -0
- package/dist/server/content-helpers.d.ts +40 -0
- package/dist/server/content-helpers.js +33 -0
- package/dist/server/content-helpers.js.map +1 -1
- package/dist/server/express.test.js +61 -0
- package/dist/server/express.test.js.map +1 -1
- package/dist/server/file-ref.d.ts +28 -0
- package/dist/server/file-ref.js +27 -0
- package/dist/server/file-ref.js.map +1 -0
- package/dist/server/index.d.ts +11 -2
- package/dist/server/index.js +9 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/middleware.d.ts +16 -3
- package/dist/server/middleware.js.map +1 -1
- package/dist/server/requestOrigin.d.ts +7 -0
- package/dist/server/requestOrigin.js +25 -0
- package/dist/server/requestOrigin.js.map +1 -0
- package/dist/server/server.d.ts +213 -6
- package/dist/server/server.js +235 -62
- package/dist/server/server.js.map +1 -1
- package/dist/server/view-resource-resolution.test.d.ts +6 -0
- package/dist/server/view-resource-resolution.test.js +88 -0
- package/dist/server/view-resource-resolution.test.js.map +1 -0
- package/dist/test/view.test.js +45 -0
- package/dist/test/view.test.js.map +1 -1
- package/dist/web/bridges/apps-sdk/adaptor.d.ts +6 -1
- package/dist/web/bridges/apps-sdk/adaptor.js +22 -1
- package/dist/web/bridges/apps-sdk/adaptor.js.map +1 -1
- package/dist/web/bridges/apps-sdk/bridge.d.ts +1 -0
- package/dist/web/bridges/apps-sdk/bridge.js +1 -0
- package/dist/web/bridges/apps-sdk/bridge.js.map +1 -1
- package/dist/web/bridges/apps-sdk/types.d.ts +2 -0
- package/dist/web/bridges/apps-sdk/types.js.map +1 -1
- package/dist/web/bridges/apps-sdk/use-apps-sdk-context.d.ts +11 -0
- package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js +11 -0
- package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js.map +1 -1
- package/dist/web/bridges/get-adaptor.d.ts +7 -0
- package/dist/web/bridges/get-adaptor.js +7 -0
- package/dist/web/bridges/get-adaptor.js.map +1 -1
- package/dist/web/bridges/mcp-app/adaptor.d.ts +6 -1
- package/dist/web/bridges/mcp-app/adaptor.js +20 -0
- package/dist/web/bridges/mcp-app/adaptor.js.map +1 -1
- package/dist/web/bridges/mcp-app/bridge.d.ts +4 -2
- package/dist/web/bridges/mcp-app/bridge.js +23 -1
- package/dist/web/bridges/mcp-app/bridge.js.map +1 -1
- package/dist/web/bridges/mcp-app/use-mcp-app-context.d.ts +12 -0
- package/dist/web/bridges/mcp-app/use-mcp-app-context.js +12 -0
- package/dist/web/bridges/mcp-app/use-mcp-app-context.js.map +1 -1
- package/dist/web/bridges/mcp-app/view-tools.test.d.ts +1 -0
- package/dist/web/bridges/mcp-app/view-tools.test.js +144 -0
- package/dist/web/bridges/mcp-app/view-tools.test.js.map +1 -0
- package/dist/web/bridges/types.d.ts +94 -1
- package/dist/web/bridges/types.js.map +1 -1
- package/dist/web/bridges/use-host-context.d.ts +5 -0
- package/dist/web/bridges/use-host-context.js +5 -0
- package/dist/web/bridges/use-host-context.js.map +1 -1
- package/dist/web/create-store.d.ts +26 -0
- package/dist/web/create-store.js +26 -0
- package/dist/web/create-store.js.map +1 -1
- package/dist/web/data-llm.d.ts +33 -0
- package/dist/web/data-llm.js +28 -0
- package/dist/web/data-llm.js.map +1 -1
- package/dist/web/generate-helpers.d.ts +2 -0
- package/dist/web/generate-helpers.js +2 -0
- package/dist/web/generate-helpers.js.map +1 -1
- package/dist/web/generate-helpers.test-d.js +4 -2
- package/dist/web/generate-helpers.test-d.js.map +1 -1
- package/dist/web/hooks/index.d.ts +4 -0
- package/dist/web/hooks/index.js +4 -0
- package/dist/web/hooks/index.js.map +1 -1
- package/dist/web/hooks/test/utils.d.ts +6 -2
- package/dist/web/hooks/test/utils.js +13 -2
- package/dist/web/hooks/test/utils.js.map +1 -1
- package/dist/web/hooks/use-call-tool.d.ts +45 -0
- package/dist/web/hooks/use-call-tool.js +28 -0
- package/dist/web/hooks/use-call-tool.js.map +1 -1
- package/dist/web/hooks/use-call-tool.test.js +27 -2
- package/dist/web/hooks/use-call-tool.test.js.map +1 -1
- package/dist/web/hooks/use-display-mode.d.ts +20 -0
- package/dist/web/hooks/use-display-mode.js +20 -0
- package/dist/web/hooks/use-display-mode.js.map +1 -1
- package/dist/web/hooks/use-download.d.ts +5 -0
- package/dist/web/hooks/use-download.js +8 -0
- package/dist/web/hooks/use-download.js.map +1 -0
- package/dist/web/hooks/use-download.test.d.ts +1 -0
- package/dist/web/hooks/use-download.test.js +95 -0
- package/dist/web/hooks/use-download.test.js.map +1 -0
- package/dist/web/hooks/use-files.d.ts +32 -0
- package/dist/web/hooks/use-files.js +32 -0
- package/dist/web/hooks/use-files.js.map +1 -1
- package/dist/web/hooks/use-layout.d.ts +2 -0
- package/dist/web/hooks/use-layout.js +2 -0
- package/dist/web/hooks/use-layout.js.map +1 -1
- package/dist/web/hooks/use-open-external.d.ts +17 -0
- package/dist/web/hooks/use-open-external.js +16 -0
- package/dist/web/hooks/use-open-external.js.map +1 -1
- package/dist/web/hooks/use-register-view-tool.d.ts +38 -0
- package/dist/web/hooks/use-register-view-tool.js +50 -0
- package/dist/web/hooks/use-register-view-tool.js.map +1 -0
- package/dist/web/hooks/use-request-close.d.ts +16 -0
- package/dist/web/hooks/use-request-close.js +21 -0
- package/dist/web/hooks/use-request-close.js.map +1 -0
- package/dist/web/hooks/use-request-close.test.d.ts +1 -0
- package/dist/web/hooks/use-request-close.test.js +52 -0
- package/dist/web/hooks/use-request-close.test.js.map +1 -0
- package/dist/web/hooks/use-request-modal.d.ts +16 -1
- package/dist/web/hooks/use-request-modal.js +16 -1
- package/dist/web/hooks/use-request-modal.js.map +1 -1
- package/dist/web/hooks/use-request-size.d.ts +20 -0
- package/dist/web/hooks/use-request-size.js +24 -0
- package/dist/web/hooks/use-request-size.js.map +1 -0
- package/dist/web/hooks/use-request-size.test.d.ts +1 -0
- package/dist/web/hooks/use-request-size.test.js +65 -0
- package/dist/web/hooks/use-request-size.test.js.map +1 -0
- package/dist/web/hooks/use-send-follow-up-message.d.ts +17 -0
- package/dist/web/hooks/use-send-follow-up-message.js +17 -0
- package/dist/web/hooks/use-send-follow-up-message.js.map +1 -1
- package/dist/web/hooks/use-set-open-in-app-url.d.ts +17 -0
- package/dist/web/hooks/use-set-open-in-app-url.js +17 -0
- package/dist/web/hooks/use-set-open-in-app-url.js.map +1 -1
- package/dist/web/hooks/use-tool-info.d.ts +53 -2
- package/dist/web/hooks/use-tool-info.js +30 -7
- package/dist/web/hooks/use-tool-info.js.map +1 -1
- package/dist/web/hooks/use-tool-info.test-d.js +11 -29
- package/dist/web/hooks/use-tool-info.test-d.js.map +1 -1
- package/dist/web/hooks/use-tool-info.test.js +5 -5
- package/dist/web/hooks/use-tool-info.test.js.map +1 -1
- package/dist/web/hooks/use-user.d.ts +2 -0
- package/dist/web/hooks/use-user.js +2 -0
- package/dist/web/hooks/use-user.js.map +1 -1
- package/dist/web/hooks/use-view-state.d.ts +21 -0
- package/dist/web/hooks/use-view-state.js.map +1 -1
- package/dist/web/mount-view.d.ts +19 -0
- package/dist/web/mount-view.js +19 -0
- package/dist/web/mount-view.js.map +1 -1
- package/dist/web/plugin/plugin.d.ts +28 -0
- package/dist/web/plugin/plugin.js +33 -0
- package/dist/web/plugin/plugin.js.map +1 -1
- package/dist/web/types.d.ts +4 -0
- package/dist/web/types.js.map +1 -1
- package/package.json +10 -3
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { OAuthMetadataSchema, } from "@modelcontextprotocol/sdk/shared/auth.js";
|
|
2
|
+
const WELL_KNOWN = [
|
|
3
|
+
"/.well-known/openid-configuration",
|
|
4
|
+
"/.well-known/oauth-authorization-server",
|
|
5
|
+
];
|
|
6
|
+
const DISCOVERY_TIMEOUT_MS = 5000;
|
|
7
|
+
/** Fetches and validates an authorization server's discovery document. */
|
|
8
|
+
export async function discoverAuthorizationServer(issuer) {
|
|
9
|
+
const base = issuer.replace(/\/$/, "");
|
|
10
|
+
const errors = [];
|
|
11
|
+
// First valid doc lacking a registration_endpoint, kept as a fallback so a
|
|
12
|
+
// genuinely non-DCR IdP still resolves (and is rejected later by the caller).
|
|
13
|
+
let fallback;
|
|
14
|
+
for (const path of WELL_KNOWN) {
|
|
15
|
+
const url = `${base}${path}`;
|
|
16
|
+
// Any failure (network/timeout, non-2xx, non-JSON, invalid metadata, issuer
|
|
17
|
+
// mismatch) records a per-URL reason and falls through to the next path.
|
|
18
|
+
try {
|
|
19
|
+
const res = await fetch(url, {
|
|
20
|
+
signal: AbortSignal.timeout(DISCOVERY_TIMEOUT_MS),
|
|
21
|
+
});
|
|
22
|
+
if (!res.ok) {
|
|
23
|
+
throw new Error(`HTTP ${res.status}`);
|
|
24
|
+
}
|
|
25
|
+
const parsed = OAuthMetadataSchema.safeParse(await res.json());
|
|
26
|
+
if (!parsed.success) {
|
|
27
|
+
throw new Error(`invalid metadata (${parsed.error.message})`);
|
|
28
|
+
}
|
|
29
|
+
// RFC 8414 §3.3: issuer must match the fetch origin (slash-insensitive).
|
|
30
|
+
if (parsed.data.issuer.replace(/\/$/, "") !== base) {
|
|
31
|
+
throw new Error(`issuer mismatch: ${parsed.data.issuer}`);
|
|
32
|
+
}
|
|
33
|
+
const metadata = parsed.data;
|
|
34
|
+
// Prefer the document that advertises DCR: some IdPs (e.g. Clerk) serve a
|
|
35
|
+
// valid openid-configuration without `registration_endpoint` yet expose
|
|
36
|
+
// it at oauth-authorization-server. Keep looking past a doc that omits it.
|
|
37
|
+
if (metadata.registration_endpoint) {
|
|
38
|
+
return metadata;
|
|
39
|
+
}
|
|
40
|
+
fallback ??= metadata;
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
errors.push(`${url}: ${err instanceof Error ? err.message : String(err)}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (fallback) {
|
|
47
|
+
return fallback;
|
|
48
|
+
}
|
|
49
|
+
throw new Error(`OAuth discovery failed for ${issuer}: ${errors.join("; ")}`);
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=discovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discovery.js","sourceRoot":"","sources":["../../../src/server/auth/discovery.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,mBAAmB,GACpB,MAAM,0CAA0C,CAAC;AASlD,MAAM,UAAU,GAAG;IACjB,mCAAmC;IACnC,yCAAyC;CAC1C,CAAC;AAEF,MAAM,oBAAoB,GAAG,IAAI,CAAC;AAElC,0EAA0E;AAC1E,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAC/C,MAAc;IAEd,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACvC,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,2EAA2E;IAC3E,8EAA8E;IAC9E,IAAI,QAAwC,CAAC;IAE7C,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;QAC9B,MAAM,GAAG,GAAG,GAAG,IAAI,GAAG,IAAI,EAAE,CAAC;QAC7B,4EAA4E;QAC5E,yEAAyE;QACzE,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;gBAC3B,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,oBAAoB,CAAC;aAClD,CAAC,CAAC;YACH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;gBACZ,MAAM,IAAI,KAAK,CAAC,QAAQ,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;YACxC,CAAC;YACD,MAAM,MAAM,GAAG,mBAAmB,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;YAC/D,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBACpB,MAAM,IAAI,KAAK,CAAC,qBAAqB,MAAM,CAAC,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC;YAChE,CAAC;YACD,yEAAyE;YACzE,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC;gBACnD,MAAM,IAAI,KAAK,CAAC,oBAAoB,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;YAC5D,CAAC;YACD,MAAM,QAAQ,GAAG,MAAM,CAAC,IAA0B,CAAC;YACnD,0EAA0E;YAC1E,wEAAwE;YACxE,2EAA2E;YAC3E,IAAI,QAAQ,CAAC,qBAAqB,EAAE,CAAC;gBACnC,OAAO,QAAQ,CAAC;YAClB,CAAC;YACD,QAAQ,KAAK,QAAQ,CAAC;QACxB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,CAAC,IAAI,CACT,GAAG,GAAG,KAAK,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAC9D,CAAC;QACJ,CAAC;IACH,CAAC;IAED,IAAI,QAAQ,EAAE,CAAC;QACb,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,8BAA8B,MAAM,KAAK,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAChF,CAAC","sourcesContent":["import {\n type OAuthMetadata,\n OAuthMetadataSchema,\n} from \"@modelcontextprotocol/sdk/shared/auth.js\";\n\n/**\n * Discovered AS metadata + `jwks_uri` — the SDK's `OAuthMetadata` omits it (its\n * OAuth client never verifies tokens), but we need it to verify token signatures\n * via JWKS, so we re-type the field for typed access.\n */\nexport type DiscoveredMetadata = OAuthMetadata & { jwks_uri?: string };\n\nconst WELL_KNOWN = [\n \"/.well-known/openid-configuration\",\n \"/.well-known/oauth-authorization-server\",\n];\n\nconst DISCOVERY_TIMEOUT_MS = 5000;\n\n/** Fetches and validates an authorization server's discovery document. */\nexport async function discoverAuthorizationServer(\n issuer: string,\n): Promise<DiscoveredMetadata> {\n const base = issuer.replace(/\\/$/, \"\");\n const errors: string[] = [];\n // First valid doc lacking a registration_endpoint, kept as a fallback so a\n // genuinely non-DCR IdP still resolves (and is rejected later by the caller).\n let fallback: DiscoveredMetadata | undefined;\n\n for (const path of WELL_KNOWN) {\n const url = `${base}${path}`;\n // Any failure (network/timeout, non-2xx, non-JSON, invalid metadata, issuer\n // mismatch) records a per-URL reason and falls through to the next path.\n try {\n const res = await fetch(url, {\n signal: AbortSignal.timeout(DISCOVERY_TIMEOUT_MS),\n });\n if (!res.ok) {\n throw new Error(`HTTP ${res.status}`);\n }\n const parsed = OAuthMetadataSchema.safeParse(await res.json());\n if (!parsed.success) {\n throw new Error(`invalid metadata (${parsed.error.message})`);\n }\n // RFC 8414 §3.3: issuer must match the fetch origin (slash-insensitive).\n if (parsed.data.issuer.replace(/\\/$/, \"\") !== base) {\n throw new Error(`issuer mismatch: ${parsed.data.issuer}`);\n }\n const metadata = parsed.data as DiscoveredMetadata;\n // Prefer the document that advertises DCR: some IdPs (e.g. Clerk) serve a\n // valid openid-configuration without `registration_endpoint` yet expose\n // it at oauth-authorization-server. Keep looking past a doc that omits it.\n if (metadata.registration_endpoint) {\n return metadata;\n }\n fallback ??= metadata;\n } catch (err) {\n errors.push(\n `${url}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n\n if (fallback) {\n return fallback;\n }\n throw new Error(`OAuth discovery failed for ${issuer}: ${errors.join(\"; \")}`);\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// @vitest-environment node
|
|
2
|
+
import http from "node:http";
|
|
3
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
4
|
+
import { discoverAuthorizationServer } from "./discovery.js";
|
|
5
|
+
let server;
|
|
6
|
+
afterEach(() => server?.close());
|
|
7
|
+
function doc(origin, extra = {}) {
|
|
8
|
+
return {
|
|
9
|
+
issuer: origin,
|
|
10
|
+
authorization_endpoint: `${origin}/authorize`,
|
|
11
|
+
token_endpoint: `${origin}/token`,
|
|
12
|
+
registration_endpoint: `${origin}/register`,
|
|
13
|
+
response_types_supported: ["code"],
|
|
14
|
+
scopes_supported: ["openid", "email"],
|
|
15
|
+
jwks_uri: `${origin}/jwks`,
|
|
16
|
+
...extra,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
// Serves JSON bodies built from the live origin at their well-known paths;
|
|
20
|
+
// returns the origin URL.
|
|
21
|
+
async function serve(build) {
|
|
22
|
+
let origin = "";
|
|
23
|
+
const srv = http.createServer((req, res) => {
|
|
24
|
+
const body = build(origin)[req.url ?? ""];
|
|
25
|
+
if (body === undefined) {
|
|
26
|
+
res.writeHead(404).end();
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
res.setHeader("content-type", "application/json");
|
|
30
|
+
res.end(JSON.stringify(body));
|
|
31
|
+
});
|
|
32
|
+
await new Promise((resolve) => srv.listen(0, resolve));
|
|
33
|
+
server = srv;
|
|
34
|
+
origin = `http://localhost:${srv.address().port}`;
|
|
35
|
+
return origin;
|
|
36
|
+
}
|
|
37
|
+
describe("discoverAuthorizationServer", () => {
|
|
38
|
+
it("fetches and validates openid-configuration", async () => {
|
|
39
|
+
const base = await serve((o) => ({
|
|
40
|
+
"/.well-known/openid-configuration": doc(o),
|
|
41
|
+
}));
|
|
42
|
+
const meta = await discoverAuthorizationServer(base);
|
|
43
|
+
expect(meta.registration_endpoint).toBe(`${base}/register`);
|
|
44
|
+
expect(meta.jwks_uri).toBe(`${base}/jwks`);
|
|
45
|
+
});
|
|
46
|
+
it("accepts a canonically slash-terminated issuer", async () => {
|
|
47
|
+
const base = await serve((o) => ({
|
|
48
|
+
"/.well-known/openid-configuration": doc(o, { issuer: `${o}/` }),
|
|
49
|
+
}));
|
|
50
|
+
const meta = await discoverAuthorizationServer(base);
|
|
51
|
+
expect(meta.registration_endpoint).toBe(`${base}/register`);
|
|
52
|
+
});
|
|
53
|
+
it("falls back to oauth-authorization-server when oidc is 404", async () => {
|
|
54
|
+
const base = await serve((o) => ({
|
|
55
|
+
"/.well-known/oauth-authorization-server": doc(o),
|
|
56
|
+
}));
|
|
57
|
+
const meta = await discoverAuthorizationServer(base);
|
|
58
|
+
expect(meta.registration_endpoint).toBe(`${base}/register`);
|
|
59
|
+
});
|
|
60
|
+
it("throws when no well-known doc is reachable", async () => {
|
|
61
|
+
const base = await serve(() => ({}));
|
|
62
|
+
await expect(discoverAuthorizationServer(base)).rejects.toThrow(/discovery failed/i);
|
|
63
|
+
});
|
|
64
|
+
it("falls through to oauth-authorization-server when oidc doc is schema-invalid", async () => {
|
|
65
|
+
const base = await serve((o) => ({
|
|
66
|
+
"/.well-known/openid-configuration": doc(o, {
|
|
67
|
+
response_types_supported: undefined,
|
|
68
|
+
}),
|
|
69
|
+
"/.well-known/oauth-authorization-server": doc(o),
|
|
70
|
+
}));
|
|
71
|
+
const meta = await discoverAuthorizationServer(base);
|
|
72
|
+
expect(meta.registration_endpoint).toBe(`${base}/register`);
|
|
73
|
+
});
|
|
74
|
+
it("prefers the doc advertising DCR when oidc omits registration_endpoint", async () => {
|
|
75
|
+
// Clerk-shaped: valid openid-configuration without a registration_endpoint,
|
|
76
|
+
// DCR exposed only at oauth-authorization-server.
|
|
77
|
+
const base = await serve((o) => ({
|
|
78
|
+
"/.well-known/openid-configuration": doc(o, {
|
|
79
|
+
registration_endpoint: undefined,
|
|
80
|
+
}),
|
|
81
|
+
"/.well-known/oauth-authorization-server": doc(o),
|
|
82
|
+
}));
|
|
83
|
+
const meta = await discoverAuthorizationServer(base);
|
|
84
|
+
expect(meta.registration_endpoint).toBe(`${base}/register`);
|
|
85
|
+
});
|
|
86
|
+
it("rejects a doc whose issuer does not match the fetch origin", async () => {
|
|
87
|
+
const base = await serve(() => ({
|
|
88
|
+
"/.well-known/openid-configuration": doc("https://evil.test"),
|
|
89
|
+
}));
|
|
90
|
+
await expect(discoverAuthorizationServer(base)).rejects.toThrow(/discovery failed/i);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
//# sourceMappingURL=discovery.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discovery.test.js","sourceRoot":"","sources":["../../../src/server/auth/discovery.test.ts"],"names":[],"mappings":"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","sourcesContent":["// @vitest-environment node\nimport http from \"node:http\";\nimport { afterEach, describe, expect, it } from \"vitest\";\nimport { discoverAuthorizationServer } from \"./discovery.js\";\n\nlet server: http.Server | undefined;\nafterEach(() => server?.close());\n\nfunction doc(origin: string, extra: Record<string, unknown> = {}) {\n return {\n issuer: origin,\n authorization_endpoint: `${origin}/authorize`,\n token_endpoint: `${origin}/token`,\n registration_endpoint: `${origin}/register`,\n response_types_supported: [\"code\"],\n scopes_supported: [\"openid\", \"email\"],\n jwks_uri: `${origin}/jwks`,\n ...extra,\n };\n}\n\n// Serves JSON bodies built from the live origin at their well-known paths;\n// returns the origin URL.\nasync function serve(build: (origin: string) => Record<string, unknown>) {\n let origin = \"\";\n const srv = http.createServer((req, res) => {\n const body = build(origin)[req.url ?? \"\"];\n if (body === undefined) {\n res.writeHead(404).end();\n return;\n }\n res.setHeader(\"content-type\", \"application/json\");\n res.end(JSON.stringify(body));\n });\n await new Promise<void>((resolve) => srv.listen(0, resolve));\n server = srv;\n origin = `http://localhost:${(srv.address() as { port: number }).port}`;\n return origin;\n}\n\ndescribe(\"discoverAuthorizationServer\", () => {\n it(\"fetches and validates openid-configuration\", async () => {\n const base = await serve((o) => ({\n \"/.well-known/openid-configuration\": doc(o),\n }));\n const meta = await discoverAuthorizationServer(base);\n expect(meta.registration_endpoint).toBe(`${base}/register`);\n expect(meta.jwks_uri).toBe(`${base}/jwks`);\n });\n\n it(\"accepts a canonically slash-terminated issuer\", async () => {\n const base = await serve((o) => ({\n \"/.well-known/openid-configuration\": doc(o, { issuer: `${o}/` }),\n }));\n const meta = await discoverAuthorizationServer(base);\n expect(meta.registration_endpoint).toBe(`${base}/register`);\n });\n\n it(\"falls back to oauth-authorization-server when oidc is 404\", async () => {\n const base = await serve((o) => ({\n \"/.well-known/oauth-authorization-server\": doc(o),\n }));\n const meta = await discoverAuthorizationServer(base);\n expect(meta.registration_endpoint).toBe(`${base}/register`);\n });\n\n it(\"throws when no well-known doc is reachable\", async () => {\n const base = await serve(() => ({}));\n await expect(discoverAuthorizationServer(base)).rejects.toThrow(\n /discovery failed/i,\n );\n });\n\n it(\"falls through to oauth-authorization-server when oidc doc is schema-invalid\", async () => {\n const base = await serve((o) => ({\n \"/.well-known/openid-configuration\": doc(o, {\n response_types_supported: undefined,\n }),\n \"/.well-known/oauth-authorization-server\": doc(o),\n }));\n const meta = await discoverAuthorizationServer(base);\n expect(meta.registration_endpoint).toBe(`${base}/register`);\n });\n\n it(\"prefers the doc advertising DCR when oidc omits registration_endpoint\", async () => {\n // Clerk-shaped: valid openid-configuration without a registration_endpoint,\n // DCR exposed only at oauth-authorization-server.\n const base = await serve((o) => ({\n \"/.well-known/openid-configuration\": doc(o, {\n registration_endpoint: undefined,\n }),\n \"/.well-known/oauth-authorization-server\": doc(o),\n }));\n const meta = await discoverAuthorizationServer(base);\n expect(meta.registration_endpoint).toBe(`${base}/register`);\n });\n\n it(\"rejects a doc whose issuer does not match the fetch origin\", async () => {\n const base = await serve(() => ({\n \"/.well-known/openid-configuration\": doc(\"https://evil.test\"),\n }));\n await expect(discoverAuthorizationServer(base)).rejects.toThrow(\n /discovery failed/i,\n );\n });\n});\n"]}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { OAuthMetadata } from "@modelcontextprotocol/sdk/shared/auth.js";
|
|
2
|
+
import type { JwksVerifyConfig } from "./verify.js";
|
|
3
|
+
/** Resource-server OAuth config for `SkybridgeServerOptions.oauth`. */
|
|
4
|
+
export type OAuthConfig = {
|
|
5
|
+
/**
|
|
6
|
+
* Public URL of this server; sets `resourceServerUrl` and the
|
|
7
|
+
* `resource_metadata` URL. When omitted, it is inferred per request from
|
|
8
|
+
* `x-forwarded-host`/`origin`/`host` headers.
|
|
9
|
+
*/
|
|
10
|
+
baseUrl?: string;
|
|
11
|
+
/** AS metadata served at `/.well-known/oauth-authorization-server`. */
|
|
12
|
+
oauthMetadata: OAuthMetadata;
|
|
13
|
+
verify: JwksVerifyConfig;
|
|
14
|
+
/** Scopes advertised in protected-resource metadata. */
|
|
15
|
+
scopesSupported?: string[];
|
|
16
|
+
/** Server-wide required-scope floor. */
|
|
17
|
+
requiredScopes?: string[];
|
|
18
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/server/auth/index.ts"],"names":[],"mappings":"","sourcesContent":["import type { OAuthMetadata } from \"@modelcontextprotocol/sdk/shared/auth.js\";\nimport type { JwksVerifyConfig } from \"./verify.js\";\n\n/** Resource-server OAuth config for `SkybridgeServerOptions.oauth`. */\nexport type OAuthConfig = {\n /**\n * Public URL of this server; sets `resourceServerUrl` and the\n * `resource_metadata` URL. When omitted, it is inferred per request from\n * `x-forwarded-host`/`origin`/`host` headers.\n */\n baseUrl?: string;\n /** AS metadata served at `/.well-known/oauth-authorization-server`. */\n oauthMetadata: OAuthMetadata;\n verify: JwksVerifyConfig;\n /** Scopes advertised in protected-resource metadata. */\n scopesSupported?: string[];\n /** Server-wide required-scope floor. */\n requiredScopes?: string[];\n};\n"]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { OAuthConfig } from "../index.js";
|
|
2
|
+
import { type CustomProviderOptions } from "./custom.js";
|
|
3
|
+
/**
|
|
4
|
+
* OAuth provider for Auth0. `domain` is the tenant domain (e.g.
|
|
5
|
+
* `acme.us.auth0.com`) or a custom domain. Requires Dynamic Client
|
|
6
|
+
* Registration enabled on the tenant (`enable_dynamic_client_registration`).
|
|
7
|
+
* `audience` is the registered API Identifier — Auth0 only issues a verifiable
|
|
8
|
+
* JWT (rather than an opaque token) when the request targets that audience.
|
|
9
|
+
*/
|
|
10
|
+
export declare function auth0Provider(opts: {
|
|
11
|
+
domain: string;
|
|
12
|
+
audience: string;
|
|
13
|
+
} & Omit<CustomProviderOptions, "issuer" | "audience">): Promise<OAuthConfig>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { customProvider } from "./custom.js";
|
|
2
|
+
/** Normalises a bare host to an https issuer URL and strips a trailing slash. */
|
|
3
|
+
function toIssuerUrl(domain) {
|
|
4
|
+
const withScheme = /^https?:\/\//.test(domain) ? domain : `https://${domain}`;
|
|
5
|
+
return withScheme.replace(/\/$/, "");
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* OAuth provider for Auth0. `domain` is the tenant domain (e.g.
|
|
9
|
+
* `acme.us.auth0.com`) or a custom domain. Requires Dynamic Client
|
|
10
|
+
* Registration enabled on the tenant (`enable_dynamic_client_registration`).
|
|
11
|
+
* `audience` is the registered API Identifier — Auth0 only issues a verifiable
|
|
12
|
+
* JWT (rather than an opaque token) when the request targets that audience.
|
|
13
|
+
*/
|
|
14
|
+
export function auth0Provider(opts) {
|
|
15
|
+
const { domain, ...rest } = opts;
|
|
16
|
+
return customProvider({ issuer: toIssuerUrl(domain), ...rest });
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=auth0.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth0.js","sourceRoot":"","sources":["../../../../src/server/auth/providers/auth0.ts"],"names":[],"mappings":"AACA,OAAO,EAA8B,cAAc,EAAE,MAAM,aAAa,CAAC;AAEzE,iFAAiF;AACjF,SAAS,WAAW,CAAC,MAAc;IACjC,MAAM,UAAU,GAAG,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,WAAW,MAAM,EAAE,CAAC;IAC9E,OAAO,UAAU,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACvC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAC3B,IAGC;IAED,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC;IACjC,OAAO,cAAc,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,EAAE,GAAG,IAAI,EAAE,CAAC,CAAC;AAClE,CAAC","sourcesContent":["import type { OAuthConfig } from \"../index.js\";\nimport { type CustomProviderOptions, customProvider } from \"./custom.js\";\n\n/** Normalises a bare host to an https issuer URL and strips a trailing slash. */\nfunction toIssuerUrl(domain: string): string {\n const withScheme = /^https?:\\/\\//.test(domain) ? domain : `https://${domain}`;\n return withScheme.replace(/\\/$/, \"\");\n}\n\n/**\n * OAuth provider for Auth0. `domain` is the tenant domain (e.g.\n * `acme.us.auth0.com`) or a custom domain. Requires Dynamic Client\n * Registration enabled on the tenant (`enable_dynamic_client_registration`).\n * `audience` is the registered API Identifier — Auth0 only issues a verifiable\n * JWT (rather than an opaque token) when the request targets that audience.\n */\nexport function auth0Provider(\n opts: { domain: string; audience: string } & Omit<\n CustomProviderOptions,\n \"issuer\" | \"audience\"\n >,\n): Promise<OAuthConfig> {\n const { domain, ...rest } = opts;\n return customProvider({ issuer: toIssuerUrl(domain), ...rest });\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// @vitest-environment node
|
|
2
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { auth0Provider } from "./auth0.js";
|
|
4
|
+
afterEach(() => vi.restoreAllMocks());
|
|
5
|
+
function discoveryDoc(issuer) {
|
|
6
|
+
return {
|
|
7
|
+
issuer,
|
|
8
|
+
authorization_endpoint: `${issuer}/authorize`,
|
|
9
|
+
token_endpoint: `${issuer}/oauth/token`,
|
|
10
|
+
registration_endpoint: `${issuer}/oidc/register`,
|
|
11
|
+
response_types_supported: ["code"],
|
|
12
|
+
scopes_supported: ["openid"],
|
|
13
|
+
jwks_uri: `${issuer}/.well-known/jwks.json`,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
function mockDiscovery(issuer) {
|
|
17
|
+
return vi.spyOn(globalThis, "fetch").mockResolvedValue(new Response(JSON.stringify(discoveryDoc(issuer)), {
|
|
18
|
+
headers: { "content-type": "application/json" },
|
|
19
|
+
}));
|
|
20
|
+
}
|
|
21
|
+
describe("auth0Provider", () => {
|
|
22
|
+
it("builds the issuer from the tenant domain", async () => {
|
|
23
|
+
const fetchSpy = mockDiscovery("https://acme.us.auth0.com");
|
|
24
|
+
const config = await auth0Provider({
|
|
25
|
+
domain: "acme.us.auth0.com",
|
|
26
|
+
audience: "https://my-mcp-api",
|
|
27
|
+
});
|
|
28
|
+
expect(fetchSpy).toHaveBeenCalledWith("https://acme.us.auth0.com/.well-known/openid-configuration", expect.anything());
|
|
29
|
+
expect(config.verify.issuer).toBe("https://acme.us.auth0.com");
|
|
30
|
+
expect(config.verify.audience).toBe("https://my-mcp-api");
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
//# sourceMappingURL=auth0.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth0.test.js","sourceRoot":"","sources":["../../../../src/server/auth/providers/auth0.test.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAC3B,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,SAAS,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,eAAe,EAAE,CAAC,CAAC;AAEtC,SAAS,YAAY,CAAC,MAAc;IAClC,OAAO;QACL,MAAM;QACN,sBAAsB,EAAE,GAAG,MAAM,YAAY;QAC7C,cAAc,EAAE,GAAG,MAAM,cAAc;QACvC,qBAAqB,EAAE,GAAG,MAAM,gBAAgB;QAChD,wBAAwB,EAAE,CAAC,MAAM,CAAC;QAClC,gBAAgB,EAAE,CAAC,QAAQ,CAAC;QAC5B,QAAQ,EAAE,GAAG,MAAM,wBAAwB;KAC5C,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CAAC,MAAc;IACnC,OAAO,EAAE,CAAC,KAAK,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,iBAAiB,CACpD,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,EAAE;QACjD,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;KAChD,CAAC,CACH,CAAC;AACJ,CAAC;AAED,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,EAAE,CAAC,0CAA0C,EAAE,KAAK,IAAI,EAAE;QACxD,MAAM,QAAQ,GAAG,aAAa,CAAC,2BAA2B,CAAC,CAAC;QAE5D,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC;YACjC,MAAM,EAAE,mBAAmB;YAC3B,QAAQ,EAAE,oBAAoB;SAC/B,CAAC,CAAC;QAEH,MAAM,CAAC,QAAQ,CAAC,CAAC,oBAAoB,CACnC,4DAA4D,EAC5D,MAAM,CAAC,QAAQ,EAAE,CAClB,CAAC;QACF,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;QAC/D,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["// @vitest-environment node\nimport { afterEach, describe, expect, it, vi } from \"vitest\";\nimport { auth0Provider } from \"./auth0.js\";\n\nafterEach(() => vi.restoreAllMocks());\n\nfunction discoveryDoc(issuer: string) {\n return {\n issuer,\n authorization_endpoint: `${issuer}/authorize`,\n token_endpoint: `${issuer}/oauth/token`,\n registration_endpoint: `${issuer}/oidc/register`,\n response_types_supported: [\"code\"],\n scopes_supported: [\"openid\"],\n jwks_uri: `${issuer}/.well-known/jwks.json`,\n };\n}\n\nfunction mockDiscovery(issuer: string) {\n return vi.spyOn(globalThis, \"fetch\").mockResolvedValue(\n new Response(JSON.stringify(discoveryDoc(issuer)), {\n headers: { \"content-type\": \"application/json\" },\n }),\n );\n}\n\ndescribe(\"auth0Provider\", () => {\n it(\"builds the issuer from the tenant domain\", async () => {\n const fetchSpy = mockDiscovery(\"https://acme.us.auth0.com\");\n\n const config = await auth0Provider({\n domain: \"acme.us.auth0.com\",\n audience: \"https://my-mcp-api\",\n });\n\n expect(fetchSpy).toHaveBeenCalledWith(\n \"https://acme.us.auth0.com/.well-known/openid-configuration\",\n expect.anything(),\n );\n expect(config.verify.issuer).toBe(\"https://acme.us.auth0.com\");\n expect(config.verify.audience).toBe(\"https://my-mcp-api\");\n });\n});\n"]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { OAuthConfig } from "../index.js";
|
|
2
|
+
import { type CustomProviderOptions } from "./custom.js";
|
|
3
|
+
/**
|
|
4
|
+
* OAuth provider for Clerk. `domain` is the Frontend API URL (e.g.
|
|
5
|
+
* `acme.clerk.accounts.dev`, or a production custom domain). Requires Dynamic
|
|
6
|
+
* Client Registration enabled on the instance, and the OAuth application set to
|
|
7
|
+
* issue JWT access tokens (opaque tokens can't be JWKS-verified).
|
|
8
|
+
*
|
|
9
|
+
* Clerk access tokens carry no `aud` claim, so there is no `audience` option —
|
|
10
|
+
* verification is issuer + JWKS only (matching Clerk's own `mcpAuthClerk`).
|
|
11
|
+
*/
|
|
12
|
+
export declare function clerkProvider(opts: {
|
|
13
|
+
domain: string;
|
|
14
|
+
} & Omit<CustomProviderOptions, "issuer" | "audience">): Promise<OAuthConfig>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { customProvider } from "./custom.js";
|
|
2
|
+
/** Normalises a bare host to an https issuer URL and strips a trailing slash. */
|
|
3
|
+
function toIssuerUrl(domain) {
|
|
4
|
+
const withScheme = /^https?:\/\//.test(domain) ? domain : `https://${domain}`;
|
|
5
|
+
return withScheme.replace(/\/$/, "");
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* OAuth provider for Clerk. `domain` is the Frontend API URL (e.g.
|
|
9
|
+
* `acme.clerk.accounts.dev`, or a production custom domain). Requires Dynamic
|
|
10
|
+
* Client Registration enabled on the instance, and the OAuth application set to
|
|
11
|
+
* issue JWT access tokens (opaque tokens can't be JWKS-verified).
|
|
12
|
+
*
|
|
13
|
+
* Clerk access tokens carry no `aud` claim, so there is no `audience` option —
|
|
14
|
+
* verification is issuer + JWKS only (matching Clerk's own `mcpAuthClerk`).
|
|
15
|
+
*/
|
|
16
|
+
export function clerkProvider(opts) {
|
|
17
|
+
const { domain, ...rest } = opts;
|
|
18
|
+
return customProvider({ issuer: toIssuerUrl(domain), ...rest });
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=clerk.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clerk.js","sourceRoot":"","sources":["../../../../src/server/auth/providers/clerk.ts"],"names":[],"mappings":"AACA,OAAO,EAA8B,cAAc,EAAE,MAAM,aAAa,CAAC;AAEzE,iFAAiF;AACjF,SAAS,WAAW,CAAC,MAAc;IACjC,MAAM,UAAU,GAAG,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,WAAW,MAAM,EAAE,CAAC;IAC9E,OAAO,UAAU,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACvC,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CAC3B,IAA6E;IAE7E,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC;IACjC,OAAO,cAAc,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,EAAE,GAAG,IAAI,EAAE,CAAC,CAAC;AAClE,CAAC","sourcesContent":["import type { OAuthConfig } from \"../index.js\";\nimport { type CustomProviderOptions, customProvider } from \"./custom.js\";\n\n/** Normalises a bare host to an https issuer URL and strips a trailing slash. */\nfunction toIssuerUrl(domain: string): string {\n const withScheme = /^https?:\\/\\//.test(domain) ? domain : `https://${domain}`;\n return withScheme.replace(/\\/$/, \"\");\n}\n\n/**\n * OAuth provider for Clerk. `domain` is the Frontend API URL (e.g.\n * `acme.clerk.accounts.dev`, or a production custom domain). Requires Dynamic\n * Client Registration enabled on the instance, and the OAuth application set to\n * issue JWT access tokens (opaque tokens can't be JWKS-verified).\n *\n * Clerk access tokens carry no `aud` claim, so there is no `audience` option —\n * verification is issuer + JWKS only (matching Clerk's own `mcpAuthClerk`).\n */\nexport function clerkProvider(\n opts: { domain: string } & Omit<CustomProviderOptions, \"issuer\" | \"audience\">,\n): Promise<OAuthConfig> {\n const { domain, ...rest } = opts;\n return customProvider({ issuer: toIssuerUrl(domain), ...rest });\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// @vitest-environment node
|
|
2
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { clerkProvider } from "./clerk.js";
|
|
4
|
+
afterEach(() => vi.restoreAllMocks());
|
|
5
|
+
function doc(issuer, extra = {}) {
|
|
6
|
+
return {
|
|
7
|
+
issuer,
|
|
8
|
+
authorization_endpoint: `${issuer}/oauth/authorize`,
|
|
9
|
+
token_endpoint: `${issuer}/oauth/token`,
|
|
10
|
+
registration_endpoint: `${issuer}/oauth/register`,
|
|
11
|
+
response_types_supported: ["code"],
|
|
12
|
+
scopes_supported: ["profile", "email"],
|
|
13
|
+
jwks_uri: `${issuer}/.well-known/jwks.json`,
|
|
14
|
+
...extra,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
describe("clerkProvider", () => {
|
|
18
|
+
it("builds the issuer from the Frontend API domain", async () => {
|
|
19
|
+
const issuer = "https://acme.clerk.accounts.dev";
|
|
20
|
+
const fetchSpy = vi.spyOn(globalThis, "fetch").mockResolvedValue(new Response(JSON.stringify(doc(issuer)), {
|
|
21
|
+
headers: { "content-type": "application/json" },
|
|
22
|
+
}));
|
|
23
|
+
const config = await clerkProvider({
|
|
24
|
+
domain: "acme.clerk.accounts.dev",
|
|
25
|
+
});
|
|
26
|
+
expect(fetchSpy).toHaveBeenCalledWith(`${issuer}/.well-known/openid-configuration`, expect.anything());
|
|
27
|
+
expect(config.verify.issuer).toBe(issuer);
|
|
28
|
+
// Clerk tokens carry no aud — the provider configures no audience check.
|
|
29
|
+
expect(config.verify.audience).toBeUndefined();
|
|
30
|
+
});
|
|
31
|
+
it("uses oauth-authorization-server when oidc omits the registration_endpoint", async () => {
|
|
32
|
+
const issuer = "https://acme.clerk.accounts.dev";
|
|
33
|
+
vi.spyOn(globalThis, "fetch").mockImplementation(async (input) => {
|
|
34
|
+
const url = String(input);
|
|
35
|
+
if (url.endsWith("/.well-known/openid-configuration")) {
|
|
36
|
+
return new Response(JSON.stringify(doc(issuer, { registration_endpoint: undefined })), { headers: { "content-type": "application/json" } });
|
|
37
|
+
}
|
|
38
|
+
return new Response(JSON.stringify(doc(issuer)), {
|
|
39
|
+
headers: { "content-type": "application/json" },
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
const config = await clerkProvider({
|
|
43
|
+
domain: "acme.clerk.accounts.dev",
|
|
44
|
+
});
|
|
45
|
+
expect(config.oauthMetadata.registration_endpoint).toBe(`${issuer}/oauth/register`);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
//# sourceMappingURL=clerk.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clerk.test.js","sourceRoot":"","sources":["../../../../src/server/auth/providers/clerk.test.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAC3B,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,SAAS,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,eAAe,EAAE,CAAC,CAAC;AAEtC,SAAS,GAAG,CAAC,MAAc,EAAE,QAAiC,EAAE;IAC9D,OAAO;QACL,MAAM;QACN,sBAAsB,EAAE,GAAG,MAAM,kBAAkB;QACnD,cAAc,EAAE,GAAG,MAAM,cAAc;QACvC,qBAAqB,EAAE,GAAG,MAAM,iBAAiB;QACjD,wBAAwB,EAAE,CAAC,MAAM,CAAC;QAClC,gBAAgB,EAAE,CAAC,SAAS,EAAE,OAAO,CAAC;QACtC,QAAQ,EAAE,GAAG,MAAM,wBAAwB;QAC3C,GAAG,KAAK;KACT,CAAC;AACJ,CAAC;AAED,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,EAAE,CAAC,gDAAgD,EAAE,KAAK,IAAI,EAAE;QAC9D,MAAM,MAAM,GAAG,iCAAiC,CAAC;QACjD,MAAM,QAAQ,GAAG,EAAE,CAAC,KAAK,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,iBAAiB,CAC9D,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE;YACxC,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;SAChD,CAAC,CACH,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC;YACjC,MAAM,EAAE,yBAAyB;SAClC,CAAC,CAAC;QAEH,MAAM,CAAC,QAAQ,CAAC,CAAC,oBAAoB,CACnC,GAAG,MAAM,mCAAmC,EAC5C,MAAM,CAAC,QAAQ,EAAE,CAClB,CAAC;QACF,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC1C,yEAAyE;QACzE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,aAAa,EAAE,CAAC;IACjD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2EAA2E,EAAE,KAAK,IAAI,EAAE;QACzF,MAAM,MAAM,GAAG,iCAAiC,CAAC;QACjD,EAAE,CAAC,KAAK,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,kBAAkB,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;YAC/D,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;YAC1B,IAAI,GAAG,CAAC,QAAQ,CAAC,mCAAmC,CAAC,EAAE,CAAC;gBACtD,OAAO,IAAI,QAAQ,CACjB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,qBAAqB,EAAE,SAAS,EAAE,CAAC,CAAC,EACjE,EAAE,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,EAAE,CACpD,CAAC;YACJ,CAAC;YACD,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE;gBAC/C,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;aAChD,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC;YACjC,MAAM,EAAE,yBAAyB;SAClC,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,qBAAqB,CAAC,CAAC,IAAI,CACrD,GAAG,MAAM,iBAAiB,CAC3B,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["// @vitest-environment node\nimport { afterEach, describe, expect, it, vi } from \"vitest\";\nimport { clerkProvider } from \"./clerk.js\";\n\nafterEach(() => vi.restoreAllMocks());\n\nfunction doc(issuer: string, extra: Record<string, unknown> = {}) {\n return {\n issuer,\n authorization_endpoint: `${issuer}/oauth/authorize`,\n token_endpoint: `${issuer}/oauth/token`,\n registration_endpoint: `${issuer}/oauth/register`,\n response_types_supported: [\"code\"],\n scopes_supported: [\"profile\", \"email\"],\n jwks_uri: `${issuer}/.well-known/jwks.json`,\n ...extra,\n };\n}\n\ndescribe(\"clerkProvider\", () => {\n it(\"builds the issuer from the Frontend API domain\", async () => {\n const issuer = \"https://acme.clerk.accounts.dev\";\n const fetchSpy = vi.spyOn(globalThis, \"fetch\").mockResolvedValue(\n new Response(JSON.stringify(doc(issuer)), {\n headers: { \"content-type\": \"application/json\" },\n }),\n );\n\n const config = await clerkProvider({\n domain: \"acme.clerk.accounts.dev\",\n });\n\n expect(fetchSpy).toHaveBeenCalledWith(\n `${issuer}/.well-known/openid-configuration`,\n expect.anything(),\n );\n expect(config.verify.issuer).toBe(issuer);\n // Clerk tokens carry no aud — the provider configures no audience check.\n expect(config.verify.audience).toBeUndefined();\n });\n\n it(\"uses oauth-authorization-server when oidc omits the registration_endpoint\", async () => {\n const issuer = \"https://acme.clerk.accounts.dev\";\n vi.spyOn(globalThis, \"fetch\").mockImplementation(async (input) => {\n const url = String(input);\n if (url.endsWith(\"/.well-known/openid-configuration\")) {\n return new Response(\n JSON.stringify(doc(issuer, { registration_endpoint: undefined })),\n { headers: { \"content-type\": \"application/json\" } },\n );\n }\n return new Response(JSON.stringify(doc(issuer)), {\n headers: { \"content-type\": \"application/json\" },\n });\n });\n\n const config = await clerkProvider({\n domain: \"acme.clerk.accounts.dev\",\n });\n\n expect(config.oauthMetadata.registration_endpoint).toBe(\n `${issuer}/oauth/register`,\n );\n });\n});\n"]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { OAuthMetadata } from "@modelcontextprotocol/sdk/shared/auth.js";
|
|
2
|
+
import type { OAuthConfig } from "../index.js";
|
|
3
|
+
/** Options accepted by {@link customProvider} and the branded providers. */
|
|
4
|
+
export type CustomProviderOptions = {
|
|
5
|
+
issuer: string;
|
|
6
|
+
/** Expected token `aud`. Omit to skip audience verification — only for IdPs
|
|
7
|
+
* that don't bind an audience (e.g. Clerk). Branded providers whose IdP does
|
|
8
|
+
* bind an audience re-require it in their own options. */
|
|
9
|
+
audience?: string;
|
|
10
|
+
/** Omit to let the server infer the resource origin from request headers. */
|
|
11
|
+
baseUrl?: string;
|
|
12
|
+
scopes?: string[];
|
|
13
|
+
requiredScopes?: string[];
|
|
14
|
+
metadataOverrides?: Omit<Partial<OAuthMetadata>, "issuer">;
|
|
15
|
+
};
|
|
16
|
+
/** Builds a complete {@link OAuthConfig} for a DCR-compatible IdP via discovery. */
|
|
17
|
+
export declare function customProvider(opts: CustomProviderOptions): Promise<OAuthConfig>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { discoverAuthorizationServer, } from "../discovery.js";
|
|
2
|
+
/** Builds a complete {@link OAuthConfig} for a DCR-compatible IdP via discovery. */
|
|
3
|
+
export async function customProvider(opts) {
|
|
4
|
+
const discovered = await discoverAuthorizationServer(opts.issuer);
|
|
5
|
+
// The IdP's own discovery must advertise DCR and a JWKS; overrides can't fake it.
|
|
6
|
+
if (!discovered.registration_endpoint) {
|
|
7
|
+
throw new Error(`${opts.issuer} is not DCR-compatible (no registration_endpoint); customProvider only supports DCR IdPs.`);
|
|
8
|
+
}
|
|
9
|
+
if (!discovered.jwks_uri) {
|
|
10
|
+
throw new Error(`${opts.issuer} discovery has no jwks_uri; JWKS verification requires it.`);
|
|
11
|
+
}
|
|
12
|
+
// Overrides adjust only advertised metadata (e.g. proxied endpoints). The
|
|
13
|
+
// trust anchor (issuer, jwks_uri) always comes from validated discovery.
|
|
14
|
+
const { issuer: _issuer, jwks_uri: _jwks, ...overrides } = (opts.metadataOverrides ?? {});
|
|
15
|
+
const oauthMetadata = { ...discovered, ...overrides };
|
|
16
|
+
return {
|
|
17
|
+
baseUrl: opts.baseUrl,
|
|
18
|
+
oauthMetadata,
|
|
19
|
+
verify: {
|
|
20
|
+
issuer: discovered.issuer,
|
|
21
|
+
audience: opts.audience,
|
|
22
|
+
jwksUri: discovered.jwks_uri,
|
|
23
|
+
},
|
|
24
|
+
scopesSupported: opts.scopes ?? oauthMetadata.scopes_supported,
|
|
25
|
+
requiredScopes: opts.requiredScopes,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=custom.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"custom.js","sourceRoot":"","sources":["../../../../src/server/auth/providers/custom.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,2BAA2B,GAC5B,MAAM,iBAAiB,CAAC;AAiBzB,oFAAoF;AACpF,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,IAA2B;IAE3B,MAAM,UAAU,GAAG,MAAM,2BAA2B,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAElE,kFAAkF;IAClF,IAAI,CAAC,UAAU,CAAC,qBAAqB,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CACb,GAAG,IAAI,CAAC,MAAM,2FAA2F,CAC1G,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CACb,GAAG,IAAI,CAAC,MAAM,4DAA4D,CAC3E,CAAC;IACJ,CAAC;IAED,0EAA0E;IAC1E,yEAAyE;IACzE,MAAM,EACJ,MAAM,EAAE,OAAO,EACf,QAAQ,EAAE,KAAK,EACf,GAAG,SAAS,EACb,GAAG,CAAC,IAAI,CAAC,iBAAiB,IAAI,EAAE,CAAgC,CAAC;IAClE,MAAM,aAAa,GAAuB,EAAE,GAAG,UAAU,EAAE,GAAG,SAAS,EAAE,CAAC;IAE1E,OAAO;QACL,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,aAAa;QACb,MAAM,EAAE;YACN,MAAM,EAAE,UAAU,CAAC,MAAM;YACzB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,OAAO,EAAE,UAAU,CAAC,QAAQ;SAC7B;QACD,eAAe,EAAE,IAAI,CAAC,MAAM,IAAI,aAAa,CAAC,gBAAgB;QAC9D,cAAc,EAAE,IAAI,CAAC,cAAc;KACpC,CAAC;AACJ,CAAC","sourcesContent":["import type { OAuthMetadata } from \"@modelcontextprotocol/sdk/shared/auth.js\";\nimport {\n type DiscoveredMetadata,\n discoverAuthorizationServer,\n} from \"../discovery.js\";\nimport type { OAuthConfig } from \"../index.js\";\n\n/** Options accepted by {@link customProvider} and the branded providers. */\nexport type CustomProviderOptions = {\n issuer: string;\n /** Expected token `aud`. Omit to skip audience verification — only for IdPs\n * that don't bind an audience (e.g. Clerk). Branded providers whose IdP does\n * bind an audience re-require it in their own options. */\n audience?: string;\n /** Omit to let the server infer the resource origin from request headers. */\n baseUrl?: string;\n scopes?: string[];\n requiredScopes?: string[];\n metadataOverrides?: Omit<Partial<OAuthMetadata>, \"issuer\">;\n};\n\n/** Builds a complete {@link OAuthConfig} for a DCR-compatible IdP via discovery. */\nexport async function customProvider(\n opts: CustomProviderOptions,\n): Promise<OAuthConfig> {\n const discovered = await discoverAuthorizationServer(opts.issuer);\n\n // The IdP's own discovery must advertise DCR and a JWKS; overrides can't fake it.\n if (!discovered.registration_endpoint) {\n throw new Error(\n `${opts.issuer} is not DCR-compatible (no registration_endpoint); customProvider only supports DCR IdPs.`,\n );\n }\n if (!discovered.jwks_uri) {\n throw new Error(\n `${opts.issuer} discovery has no jwks_uri; JWKS verification requires it.`,\n );\n }\n\n // Overrides adjust only advertised metadata (e.g. proxied endpoints). The\n // trust anchor (issuer, jwks_uri) always comes from validated discovery.\n const {\n issuer: _issuer,\n jwks_uri: _jwks,\n ...overrides\n } = (opts.metadataOverrides ?? {}) as Partial<DiscoveredMetadata>;\n const oauthMetadata: DiscoveredMetadata = { ...discovered, ...overrides };\n\n return {\n baseUrl: opts.baseUrl,\n oauthMetadata,\n verify: {\n issuer: discovered.issuer,\n audience: opts.audience,\n jwksUri: discovered.jwks_uri,\n },\n scopesSupported: opts.scopes ?? oauthMetadata.scopes_supported,\n requiredScopes: opts.requiredScopes,\n };\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// @vitest-environment node
|
|
2
|
+
import http from "node:http";
|
|
3
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
4
|
+
import { customProvider } from "./custom.js";
|
|
5
|
+
let server;
|
|
6
|
+
afterEach(() => server?.close());
|
|
7
|
+
function doc(origin, extra = {}) {
|
|
8
|
+
return {
|
|
9
|
+
issuer: origin,
|
|
10
|
+
authorization_endpoint: `${origin}/authorize`,
|
|
11
|
+
token_endpoint: `${origin}/token`,
|
|
12
|
+
registration_endpoint: `${origin}/register`,
|
|
13
|
+
response_types_supported: ["code"],
|
|
14
|
+
scopes_supported: ["openid", "email", "profile"],
|
|
15
|
+
jwks_uri: `${origin}/jwks`,
|
|
16
|
+
...extra,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
// Serves the discovery doc (built from the live origin) at the OIDC well-known path.
|
|
20
|
+
async function serveDiscovery(extra = {}) {
|
|
21
|
+
let origin = "";
|
|
22
|
+
const srv = http.createServer((req, res) => {
|
|
23
|
+
if (req.url !== "/.well-known/openid-configuration") {
|
|
24
|
+
res.writeHead(404).end();
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
res.setHeader("content-type", "application/json");
|
|
28
|
+
res.end(JSON.stringify(doc(origin, extra)));
|
|
29
|
+
});
|
|
30
|
+
await new Promise((resolve) => srv.listen(0, resolve));
|
|
31
|
+
server = srv;
|
|
32
|
+
origin = `http://localhost:${srv.address().port}`;
|
|
33
|
+
return origin;
|
|
34
|
+
}
|
|
35
|
+
describe("customProvider", () => {
|
|
36
|
+
it("builds an OAuthConfig from discovery", async () => {
|
|
37
|
+
const base = await serveDiscovery();
|
|
38
|
+
const config = await customProvider({
|
|
39
|
+
issuer: base,
|
|
40
|
+
audience: "my-api",
|
|
41
|
+
baseUrl: "https://app.example.test",
|
|
42
|
+
scopes: ["openid"],
|
|
43
|
+
});
|
|
44
|
+
expect(config.baseUrl).toBe("https://app.example.test");
|
|
45
|
+
expect(config.verify).toEqual({
|
|
46
|
+
issuer: base,
|
|
47
|
+
audience: "my-api",
|
|
48
|
+
jwksUri: `${base}/jwks`,
|
|
49
|
+
});
|
|
50
|
+
expect(config.scopesSupported).toEqual(["openid"]);
|
|
51
|
+
expect(config.oauthMetadata.registration_endpoint).toBe(`${base}/register`);
|
|
52
|
+
});
|
|
53
|
+
it("ignores a runtime issuer override (keeps the discovered trust anchor)", async () => {
|
|
54
|
+
const base = await serveDiscovery();
|
|
55
|
+
const config = await customProvider({
|
|
56
|
+
issuer: base,
|
|
57
|
+
audience: "a",
|
|
58
|
+
metadataOverrides: { issuer: "https://evil.test" },
|
|
59
|
+
});
|
|
60
|
+
expect(config.verify.issuer).toBe(base);
|
|
61
|
+
expect(config.oauthMetadata.issuer).toBe(base);
|
|
62
|
+
});
|
|
63
|
+
it("ignores a runtime jwks_uri override (keeps the discovered signing keys)", async () => {
|
|
64
|
+
const base = await serveDiscovery();
|
|
65
|
+
const config = await customProvider({
|
|
66
|
+
issuer: base,
|
|
67
|
+
audience: "a",
|
|
68
|
+
metadataOverrides: { jwks_uri: "https://evil.test/keys" },
|
|
69
|
+
});
|
|
70
|
+
expect(config.verify.jwksUri).toBe(`${base}/jwks`);
|
|
71
|
+
});
|
|
72
|
+
it("rejects a non-DCR IdP even if an override supplies registration_endpoint", async () => {
|
|
73
|
+
const base = await serveDiscovery({ registration_endpoint: undefined });
|
|
74
|
+
await expect(customProvider({
|
|
75
|
+
issuer: base,
|
|
76
|
+
audience: "a",
|
|
77
|
+
metadataOverrides: { registration_endpoint: `${base}/register` },
|
|
78
|
+
})).rejects.toThrow(/not DCR-compatible/);
|
|
79
|
+
});
|
|
80
|
+
it("applies metadataOverrides over discovered values", async () => {
|
|
81
|
+
const base = await serveDiscovery();
|
|
82
|
+
const config = await customProvider({
|
|
83
|
+
issuer: base,
|
|
84
|
+
audience: "a",
|
|
85
|
+
baseUrl: "https://app.example.test",
|
|
86
|
+
metadataOverrides: { token_endpoint: "https://override/token" },
|
|
87
|
+
});
|
|
88
|
+
expect(config.oauthMetadata.token_endpoint).toBe("https://override/token");
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
//# sourceMappingURL=custom.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"custom.test.js","sourceRoot":"","sources":["../../../../src/server/auth/providers/custom.test.ts"],"names":[],"mappings":"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","sourcesContent":["// @vitest-environment node\nimport http from \"node:http\";\nimport { afterEach, describe, expect, it } from \"vitest\";\nimport { customProvider } from \"./custom.js\";\n\nlet server: http.Server | undefined;\nafterEach(() => server?.close());\n\nfunction doc(origin: string, extra: Record<string, unknown> = {}) {\n return {\n issuer: origin,\n authorization_endpoint: `${origin}/authorize`,\n token_endpoint: `${origin}/token`,\n registration_endpoint: `${origin}/register`,\n response_types_supported: [\"code\"],\n scopes_supported: [\"openid\", \"email\", \"profile\"],\n jwks_uri: `${origin}/jwks`,\n ...extra,\n };\n}\n\n// Serves the discovery doc (built from the live origin) at the OIDC well-known path.\nasync function serveDiscovery(extra: Record<string, unknown> = {}) {\n let origin = \"\";\n const srv = http.createServer((req, res) => {\n if (req.url !== \"/.well-known/openid-configuration\") {\n res.writeHead(404).end();\n return;\n }\n res.setHeader(\"content-type\", \"application/json\");\n res.end(JSON.stringify(doc(origin, extra)));\n });\n await new Promise<void>((resolve) => srv.listen(0, resolve));\n server = srv;\n origin = `http://localhost:${(srv.address() as { port: number }).port}`;\n return origin;\n}\n\ndescribe(\"customProvider\", () => {\n it(\"builds an OAuthConfig from discovery\", async () => {\n const base = await serveDiscovery();\n\n const config = await customProvider({\n issuer: base,\n audience: \"my-api\",\n baseUrl: \"https://app.example.test\",\n scopes: [\"openid\"],\n });\n\n expect(config.baseUrl).toBe(\"https://app.example.test\");\n expect(config.verify).toEqual({\n issuer: base,\n audience: \"my-api\",\n jwksUri: `${base}/jwks`,\n });\n expect(config.scopesSupported).toEqual([\"openid\"]);\n expect(config.oauthMetadata.registration_endpoint).toBe(`${base}/register`);\n });\n\n it(\"ignores a runtime issuer override (keeps the discovered trust anchor)\", async () => {\n const base = await serveDiscovery();\n const config = await customProvider({\n issuer: base,\n audience: \"a\",\n metadataOverrides: { issuer: \"https://evil.test\" } as never,\n });\n expect(config.verify.issuer).toBe(base);\n expect(config.oauthMetadata.issuer).toBe(base);\n });\n\n it(\"ignores a runtime jwks_uri override (keeps the discovered signing keys)\", async () => {\n const base = await serveDiscovery();\n const config = await customProvider({\n issuer: base,\n audience: \"a\",\n metadataOverrides: { jwks_uri: \"https://evil.test/keys\" } as never,\n });\n expect(config.verify.jwksUri).toBe(`${base}/jwks`);\n });\n\n it(\"rejects a non-DCR IdP even if an override supplies registration_endpoint\", async () => {\n const base = await serveDiscovery({ registration_endpoint: undefined });\n await expect(\n customProvider({\n issuer: base,\n audience: \"a\",\n metadataOverrides: { registration_endpoint: `${base}/register` },\n }),\n ).rejects.toThrow(/not DCR-compatible/);\n });\n\n it(\"applies metadataOverrides over discovered values\", async () => {\n const base = await serveDiscovery();\n const config = await customProvider({\n issuer: base,\n audience: \"a\",\n baseUrl: \"https://app.example.test\",\n metadataOverrides: { token_endpoint: \"https://override/token\" },\n });\n expect(config.oauthMetadata.token_endpoint).toBe(\"https://override/token\");\n });\n});\n"]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { OAuthConfig } from "../index.js";
|
|
2
|
+
import { type CustomProviderOptions } from "./custom.js";
|
|
3
|
+
/**
|
|
4
|
+
* OAuth provider for a Descope MCP Server. `mcpServerId` is the MCP Server's
|
|
5
|
+
* ID (the `MS`-prefixed Resource ID from the console's MCP Servers section),
|
|
6
|
+
* not a generic Inbound App or client ID. Requires DCR enabled on the MCP
|
|
7
|
+
* Server. `audience` is the MCP server URL. Custom-domain Descope tenants
|
|
8
|
+
* should use `customProvider({ issuer })` directly with the full discovery URL.
|
|
9
|
+
*/
|
|
10
|
+
export declare function descopeProvider(opts: {
|
|
11
|
+
projectId: string;
|
|
12
|
+
mcpServerId: string;
|
|
13
|
+
audience: string;
|
|
14
|
+
} & Omit<CustomProviderOptions, "issuer" | "audience">): Promise<OAuthConfig>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { customProvider } from "./custom.js";
|
|
2
|
+
/**
|
|
3
|
+
* OAuth provider for a Descope MCP Server. `mcpServerId` is the MCP Server's
|
|
4
|
+
* ID (the `MS`-prefixed Resource ID from the console's MCP Servers section),
|
|
5
|
+
* not a generic Inbound App or client ID. Requires DCR enabled on the MCP
|
|
6
|
+
* Server. `audience` is the MCP server URL. Custom-domain Descope tenants
|
|
7
|
+
* should use `customProvider({ issuer })` directly with the full discovery URL.
|
|
8
|
+
*/
|
|
9
|
+
export function descopeProvider(opts) {
|
|
10
|
+
const { projectId, mcpServerId, ...rest } = opts;
|
|
11
|
+
const issuer = `https://api.descope.com/v1/apps/agentic/${projectId}/${mcpServerId}`;
|
|
12
|
+
return customProvider({ issuer, ...rest });
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=descope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"descope.js","sourceRoot":"","sources":["../../../../src/server/auth/providers/descope.ts"],"names":[],"mappings":"AACA,OAAO,EAA8B,cAAc,EAAE,MAAM,aAAa,CAAC;AAEzE;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAC7B,IAGC;IAED,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC;IACjD,MAAM,MAAM,GAAG,2CAA2C,SAAS,IAAI,WAAW,EAAE,CAAC;IACrF,OAAO,cAAc,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,CAAC,CAAC;AAC7C,CAAC","sourcesContent":["import type { OAuthConfig } from \"../index.js\";\nimport { type CustomProviderOptions, customProvider } from \"./custom.js\";\n\n/**\n * OAuth provider for a Descope MCP Server. `mcpServerId` is the MCP Server's\n * ID (the `MS`-prefixed Resource ID from the console's MCP Servers section),\n * not a generic Inbound App or client ID. Requires DCR enabled on the MCP\n * Server. `audience` is the MCP server URL. Custom-domain Descope tenants\n * should use `customProvider({ issuer })` directly with the full discovery URL.\n */\nexport function descopeProvider(\n opts: { projectId: string; mcpServerId: string; audience: string } & Omit<\n CustomProviderOptions,\n \"issuer\" | \"audience\"\n >,\n): Promise<OAuthConfig> {\n const { projectId, mcpServerId, ...rest } = opts;\n const issuer = `https://api.descope.com/v1/apps/agentic/${projectId}/${mcpServerId}`;\n return customProvider({ issuer, ...rest });\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|