@opengeni/api-router 2.6.4 → 2.10.0-canary.0

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.
Files changed (75) hide show
  1. package/dist/access-grant-rls.d.ts +3 -0
  2. package/dist/app.d.ts +2 -1
  3. package/dist/app.js +3 -1
  4. package/dist/auth/organization-user-setup.d.ts +23 -0
  5. package/dist/{chunk-XTLI3CBH.js → chunk-XXH7DW34.js} +6866 -3554
  6. package/dist/chunk-XXH7DW34.js.map +1 -0
  7. package/dist/codemode.d.ts +6 -0
  8. package/dist/github-access.d.ts +25 -2
  9. package/dist/http/request-source.d.ts +14 -0
  10. package/dist/index.js +19 -3
  11. package/dist/index.js.map +1 -1
  12. package/dist/integrations/oauth-client.d.ts +2 -11
  13. package/dist/integrations/personal-github-repositories.d.ts +41 -2
  14. package/dist/integrations/slack-interactions.d.ts +1 -1
  15. package/dist/mcp/server.d.ts +158 -1
  16. package/dist/mcp/session-view.d.ts +56 -2
  17. package/dist/mcp/session-wait.d.ts +1 -1
  18. package/dist/mcp-oauth.d.ts +19 -0
  19. package/dist/model-catalog.d.ts +5 -31
  20. package/dist/routes/codex.d.ts +27 -2
  21. package/dist/routes/github.d.ts +2 -0
  22. package/dist/routes/organization-model-providers.d.ts +3 -0
  23. package/dist/routes/workspace-artifacts.d.ts +2 -1
  24. package/dist/work-discovery-observability.d.ts +1 -1
  25. package/dist/workspace-artifact-content.d.ts +28 -0
  26. package/dist/workspace-artifact-provenance.d.ts +7 -0
  27. package/dist/workspace-deletion.d.ts +6 -0
  28. package/dist/workspace-tool-gateway-observability.d.ts +17 -0
  29. package/dist/workspace-tool-gateway.d.ts +118 -0
  30. package/package.json +19 -18
  31. package/src/access-grant-rls.ts +40 -0
  32. package/src/app.ts +296 -53
  33. package/src/auth/organization-user-setup.ts +95 -5
  34. package/src/codemode.ts +33 -4
  35. package/src/github-access.ts +117 -1
  36. package/src/http/auth.ts +11 -0
  37. package/src/http/request-source.ts +37 -0
  38. package/src/http/sse.ts +15 -7
  39. package/src/index.ts +18 -2
  40. package/src/integrations/oauth-client.ts +168 -203
  41. package/src/integrations/personal-github-repositories.ts +238 -9
  42. package/src/integrations/slack-app-home.ts +2 -2
  43. package/src/integrations/slack-interactions.ts +77 -37
  44. package/src/mcp/company-brain-governed-writes.ts +2 -2
  45. package/src/mcp/company-profile-agent-admin.ts +1 -1
  46. package/src/mcp/remember.ts +1 -1
  47. package/src/mcp/server.ts +504 -133
  48. package/src/mcp/session-view.ts +20 -1
  49. package/src/mcp/session-wait.ts +3 -0
  50. package/src/mcp-oauth.ts +539 -0
  51. package/src/model-catalog.ts +45 -337
  52. package/src/routes/automations.ts +178 -19
  53. package/src/routes/codex.ts +242 -73
  54. package/src/routes/connections.ts +271 -16
  55. package/src/routes/documents.ts +67 -19
  56. package/src/routes/files.ts +54 -6
  57. package/src/routes/github.ts +116 -15
  58. package/src/routes/organization-memberships.ts +59 -16
  59. package/src/routes/organization-model-providers.ts +231 -0
  60. package/src/routes/packs.ts +1 -0
  61. package/src/routes/personal-github.ts +39 -0
  62. package/src/routes/pr-review.ts +72 -4
  63. package/src/routes/scheduled-tasks.ts +40 -13
  64. package/src/routes/sessions.ts +153 -65
  65. package/src/routes/supergrok.ts +3 -2
  66. package/src/routes/workspace-artifacts.ts +176 -67
  67. package/src/routes/workspaces.ts +405 -62
  68. package/src/sandbox/channel-a.ts +17 -4
  69. package/src/work-discovery-observability.ts +3 -3
  70. package/src/workspace-artifact-content.ts +163 -0
  71. package/src/workspace-artifact-provenance.ts +104 -0
  72. package/src/workspace-deletion.ts +64 -0
  73. package/src/workspace-tool-gateway-observability.ts +100 -0
  74. package/src/workspace-tool-gateway.ts +691 -0
  75. package/dist/chunk-XTLI3CBH.js.map +0 -1
@@ -31,12 +31,20 @@ import {
31
31
  import { createSignedState, readSignedState } from "@opengeni/github";
32
32
  import {
33
33
  DestinationPolicyError,
34
+ McpOAuthDiscoveryError,
34
35
  OAUTH_MAX_RESPONSE_BYTES,
35
36
  RequestDeadlineError,
36
37
  isLocalTestEnvironment,
38
+ parseMcpOAuthChallenge,
37
39
  pinnedFetch,
38
40
  readResponseJsonBounded,
41
+ resolveMcpOAuthDiscovery,
39
42
  validateHttpUrl,
43
+ type McpAuthorizationServerMetadata,
44
+ type McpOAuthChallenge,
45
+ type McpOAuthDiscoveryMode,
46
+ type McpOAuthMetadataFetchResult,
47
+ type McpProtectedResourceMetadata,
40
48
  } from "@opengeni/network";
41
49
  import { Buffer } from "node:buffer";
42
50
  import { createHash, randomBytes } from "node:crypto";
@@ -100,30 +108,9 @@ export type OAuthCallbackResult = {
100
108
  redirectTo: string;
101
109
  };
102
110
 
103
- type WwwAuthenticateChallenge = {
104
- resourceMetadata?: string;
105
- scope?: string[];
106
- error?: string;
107
- };
108
-
109
- type ProtectedResourceMetadata = {
110
- resource?: string;
111
- authorizationServers: string[];
112
- scopesSupported: string[];
113
- raw: Record<string, unknown>;
114
- };
115
-
116
- type AuthorizationServerMetadata = {
117
- issuer: string;
118
- authorizationServer: string;
119
- authorizationEndpoint: string;
120
- tokenEndpoint: string;
121
- registrationEndpoint?: string;
122
- clientIdMetadataDocumentSupported: boolean;
123
- tokenEndpointAuthMethodsSupported: string[];
124
- codeChallengeMethodsSupported: string[];
125
- raw: Record<string, unknown>;
126
- };
111
+ type WwwAuthenticateChallenge = McpOAuthChallenge;
112
+ type ProtectedResourceMetadata = McpProtectedResourceMetadata;
113
+ type AuthorizationServerMetadata = McpAuthorizationServerMetadata;
127
114
 
128
115
  type OAuthClientRegistration = {
129
116
  method: "operator" | "manual" | "cimd" | "dcr";
@@ -151,6 +138,10 @@ type OAuthStatePayload = {
151
138
  tokenEndpoint: string;
152
139
  authorizationServer: string;
153
140
  issuer: string;
141
+ discoveryMode: McpOAuthDiscoveryMode;
142
+ discoveryMetadataSha256?: string;
143
+ protectedResourceMetadataUrl?: string;
144
+ authorizationServerMetadataUrl?: string;
154
145
  clientRegistrationMethod: OAuthClientRegistration["method"];
155
146
  tokenEndpointAuthMethod: OAuthClientRegistration["tokenEndpointAuthMethod"];
156
147
  resourceParameterSupported: boolean;
@@ -389,8 +380,9 @@ async function startMcpOAuthWithinDeadline(
389
380
 
390
381
  const discovery = await discoverMcpOAuth(mcpUrl, settings, deadline);
391
382
  assertDiscoveredAuthorizationServer(settings, discovery.as, profile, providerDomain);
392
- const resourceParameterSupported = profile.sendResourceParameter;
393
- const resource = discovery.prm.resource ? canonicalOAuthResource(discovery.prm.resource) : mcpUrl;
383
+ const resourceParameterSupported =
384
+ discovery.mode === "rfc9728_protected_resource" && profile.sendResourceParameter;
385
+ const resource = discovery.resource;
394
386
  const verifier = randomPkceVerifier();
395
387
  // A profile's exact scope override wins outright: the reviewed connector
396
388
  // never lets a caller widen the capability contract.
@@ -433,6 +425,14 @@ async function startMcpOAuthWithinDeadline(
433
425
  tokenEndpoint: discovery.as.tokenEndpoint,
434
426
  authorizationServer: client.authorizationServer,
435
427
  issuer: client.issuer,
428
+ discoveryMode: discovery.mode,
429
+ discoveryMetadataSha256: discovery.provenance.metadataSha256,
430
+ ...(discovery.provenance.protectedResourceMetadataUrl
431
+ ? {
432
+ protectedResourceMetadataUrl: discovery.provenance.protectedResourceMetadataUrl,
433
+ }
434
+ : {}),
435
+ authorizationServerMetadataUrl: discovery.provenance.authorizationServerMetadataUrl,
436
436
  clientRegistrationMethod: client.method,
437
437
  tokenEndpointAuthMethod: client.tokenEndpointAuthMethod,
438
438
  resourceParameterSupported,
@@ -711,6 +711,18 @@ async function completeMcpOAuthCallbackWithinDeadline(
711
711
  tokenEndpoint: state.tokenEndpoint,
712
712
  clientId: client.clientId,
713
713
  clientRegistrationMethod: state.clientRegistrationMethod,
714
+ oauthDiscovery: {
715
+ mode: state.discoveryMode,
716
+ resource: state.resource,
717
+ issuer: state.issuer,
718
+ ...(state.discoveryMetadataSha256 ? { metadataSha256: state.discoveryMetadataSha256 } : {}),
719
+ ...(state.protectedResourceMetadataUrl
720
+ ? { protectedResourceMetadataUrl: state.protectedResourceMetadataUrl }
721
+ : {}),
722
+ ...(state.authorizationServerMetadataUrl
723
+ ? { authorizationServerMetadataUrl: state.authorizationServerMetadataUrl }
724
+ : {}),
725
+ },
714
726
  mcpToolsVerification: verification.metadata,
715
727
  ...(verification.tools ? { mcpTools: verification.tools } : {}),
716
728
  };
@@ -835,28 +847,50 @@ async function discoverMcpOAuth(
835
847
  challenge: WwwAuthenticateChallenge;
836
848
  prm: ProtectedResourceMetadata;
837
849
  as: AuthorizationServerMetadata;
850
+ mode: McpOAuthDiscoveryMode;
851
+ resource: string;
852
+ provenance: {
853
+ protectedResourceMetadataUrl: string | null;
854
+ authorizationServerMetadataUrl: string;
855
+ metadataSha256: string;
856
+ };
838
857
  }> {
839
858
  const challenge = await deadline.run("mcp_challenge", (signal) =>
840
859
  probeMcpChallenge(resource, settings, signal),
841
860
  );
842
- const prm = await deadline.run("protected_resource_metadata", (signal) =>
843
- discoverProtectedResourceMetadata(resource, settings, challenge.resourceMetadata, signal),
844
- );
845
- const authorizationServer = prm.authorizationServers[0];
846
- if (!authorizationServer) {
847
- throw new HTTPException(422, {
848
- message: "MCP protected resource metadata did not advertise an authorization server",
849
- });
850
- }
851
- const as = await deadline.run("authorization_server_metadata", (signal) =>
852
- discoverAuthorizationServerMetadata(authorizationServer, settings, signal),
853
- );
854
- if (!as.codeChallengeMethodsSupported.includes("S256")) {
855
- throw new HTTPException(422, {
856
- message: "authorization server does not support required PKCE S256",
861
+ try {
862
+ const discovery = await resolveMcpOAuthDiscovery({
863
+ resourceUrl: resource,
864
+ challenge,
865
+ fetchMetadata: ({ kind, url }) =>
866
+ deadline.run(
867
+ kind === "protected_resource"
868
+ ? "protected_resource_metadata"
869
+ : "authorization_server_metadata",
870
+ (signal) => fetchOAuthMetadata(url, settings, signal),
871
+ ),
872
+ validateEndpoint: (rawUrl, label) => oauthEndpointUrl(rawUrl, settings, label),
873
+ canonicalizeResource: canonicalOAuthResource,
857
874
  });
875
+ return {
876
+ challenge: discovery.challenge,
877
+ prm: discovery.protectedResourceMetadata,
878
+ as: discovery.authorizationServerMetadata,
879
+ mode: discovery.mode,
880
+ resource: discovery.resource,
881
+ provenance: discovery.provenance,
882
+ };
883
+ } catch (error) {
884
+ if (error instanceof OAuthStartStageError) throw error;
885
+ if (error instanceof McpOAuthDiscoveryError) {
886
+ throw new OAuthStartStageError(
887
+ error.stage,
888
+ error.classification,
889
+ new HTTPException(422, { message: error.message }),
890
+ );
891
+ }
892
+ throw error;
858
893
  }
859
- return { challenge, prm, as };
860
894
  }
861
895
 
862
896
  async function probeMcpChallenge(
@@ -871,115 +905,45 @@ async function probeMcpChallenge(
871
905
  });
872
906
  try {
873
907
  if (response.status !== 401) {
874
- return {};
908
+ return { scheme: null, scope: [] };
875
909
  }
876
- return parseWwwAuthenticate(response.headers.get("www-authenticate"));
910
+ return parseMcpOAuthChallenge(response.headers.get("www-authenticate"));
877
911
  } finally {
878
912
  await cancelResponseBody(response);
879
913
  }
880
914
  }
881
915
 
882
- async function discoverProtectedResourceMetadata(
883
- resource: string,
884
- settings: Settings,
885
- advertisedUrl?: string,
886
- signal?: AbortSignal,
887
- ): Promise<ProtectedResourceMetadata> {
888
- const candidates = uniqueStrings([
889
- ...(advertisedUrl ? [advertisedUrl] : []),
890
- ...wellKnownCandidates(resource, "oauth-protected-resource"),
891
- ]);
892
- for (const candidate of candidates) {
893
- const payload = await fetchJsonObject(candidate, settings, signal).catch((error) => {
894
- if (error instanceof HTTPException) {
895
- throw error;
896
- }
897
- return null;
898
- });
899
- if (!payload) {
900
- continue;
901
- }
902
- const authorizationServers = stringArray(payload.authorization_servers);
903
- if (authorizationServers.length === 0) {
904
- continue;
905
- }
906
- return {
907
- authorizationServers,
908
- scopesSupported: stringArray(payload.scopes_supported),
909
- raw: payload,
910
- ...(stringValue(payload.resource) ? { resource: stringValue(payload.resource)! } : {}),
911
- };
912
- }
913
- throw new HTTPException(422, {
914
- message: "could not discover MCP protected resource metadata",
915
- });
916
- }
917
-
918
- async function discoverAuthorizationServerMetadata(
919
- authorizationServer: string,
916
+ async function fetchOAuthMetadata(
917
+ url: string,
920
918
  settings: Settings,
921
919
  signal: AbortSignal,
922
- ): Promise<AuthorizationServerMetadata> {
923
- const safeAuthorizationServer = oauthEndpointUrl(
924
- authorizationServer,
925
- settings,
926
- "OAuth authorization server",
927
- ).replace(/\/+$/, "");
928
- const candidates = uniqueStrings([
929
- // Prefer the RFC metadata locations before probing the issuer itself. Some
930
- // providers (including Linear) redirect their issuer root to a human docs
931
- // page; following that redirect can leave discovery waiting on an unrelated
932
- // streaming response even though the well-known metadata is immediately
933
- // available.
934
- ...wellKnownCandidates(safeAuthorizationServer, "oauth-authorization-server"),
935
- ...wellKnownCandidates(safeAuthorizationServer, "openid-configuration"),
936
- safeAuthorizationServer,
937
- ]);
938
- for (const candidate of candidates) {
939
- const payload = await fetchJsonObject(candidate, settings, signal).catch((error) => {
940
- if (error instanceof HTTPException) {
941
- throw error;
942
- }
943
- return null;
944
- });
945
- if (!payload) {
946
- continue;
947
- }
948
- const authorizationEndpoint = stringValue(payload.authorization_endpoint);
949
- const tokenEndpoint = stringValue(payload.token_endpoint);
950
- if (!authorizationEndpoint || !tokenEndpoint) {
951
- continue;
952
- }
953
- const safeAuthorizationEndpoint = oauthEndpointUrl(
954
- authorizationEndpoint,
955
- settings,
956
- "OAuth authorization endpoint",
957
- );
958
- const safeTokenEndpoint = oauthEndpointUrl(tokenEndpoint, settings, "OAuth token endpoint");
959
- const registrationEndpoint = stringValue(payload.registration_endpoint);
960
- const issuer = oauthEndpointUrl(
961
- stringValue(payload.issuer) ?? safeAuthorizationServer,
962
- settings,
963
- "OAuth issuer",
964
- );
965
- const safeRegistrationEndpoint = registrationEndpoint
966
- ? oauthEndpointUrl(registrationEndpoint, settings, "OAuth registration endpoint")
967
- : undefined;
968
- return {
969
- issuer,
970
- authorizationServer: safeAuthorizationServer,
971
- authorizationEndpoint: safeAuthorizationEndpoint,
972
- tokenEndpoint: safeTokenEndpoint,
973
- clientIdMetadataDocumentSupported: payload.client_id_metadata_document_supported === true,
974
- tokenEndpointAuthMethodsSupported: stringArray(payload.token_endpoint_auth_methods_supported),
975
- codeChallengeMethodsSupported: stringArray(payload.code_challenge_methods_supported),
976
- raw: payload,
977
- ...(safeRegistrationEndpoint ? { registrationEndpoint: safeRegistrationEndpoint } : {}),
978
- };
979
- }
980
- throw new HTTPException(422, {
981
- message: "could not discover OAuth authorization server metadata",
920
+ ): Promise<McpOAuthMetadataFetchResult> {
921
+ const response = await fetchOAuth(url, settings, {
922
+ headers: { accept: "application/json" },
923
+ signal,
982
924
  });
925
+ if (response.status === 404 || response.status === 410) {
926
+ await cancelResponseBody(response);
927
+ return { status: "absent", url, httpStatus: response.status };
928
+ }
929
+ if (!response.ok) {
930
+ await cancelResponseBody(response);
931
+ throw new Error(`OAuth metadata endpoint returned HTTP ${response.status}`);
932
+ }
933
+ const payload = await readResponseJsonBounded<unknown>(
934
+ response,
935
+ OAUTH_MAX_RESPONSE_BYTES,
936
+ "OAuth metadata response",
937
+ { signal },
938
+ );
939
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
940
+ throw new Error("OAuth metadata response was not a JSON object");
941
+ }
942
+ return {
943
+ status: "present",
944
+ url,
945
+ document: payload as Record<string, unknown>,
946
+ };
983
947
  }
984
948
 
985
949
  async function registerOAuthClient(
@@ -1433,6 +1397,29 @@ function readOAuthState(state: string, settings: Settings): OAuthStatePayload {
1433
1397
  throw new HTTPException(400, { message: "invalid or expired OAuth state" });
1434
1398
  }
1435
1399
  const resource = requiredString(payload.resource, "state.resource");
1400
+ const encodedDiscoveryMode = stringValue(payload.discoveryMode);
1401
+ const discoveryMode = discoveryModeValue(encodedDiscoveryMode);
1402
+ const discoveryMetadataSha256 = stringValue(payload.discoveryMetadataSha256);
1403
+ if (
1404
+ encodedDiscoveryMode &&
1405
+ (!discoveryMetadataSha256 || !/^[0-9a-f]{64}$/.test(discoveryMetadataSha256))
1406
+ ) {
1407
+ throw new HTTPException(400, { message: "invalid OAuth discovery state" });
1408
+ }
1409
+ const protectedResourceMetadataUrl = stringValue(payload.protectedResourceMetadataUrl);
1410
+ const authorizationServerMetadataUrl = stringValue(payload.authorizationServerMetadataUrl);
1411
+ if (
1412
+ encodedDiscoveryMode &&
1413
+ (!authorizationServerMetadataUrl ||
1414
+ (discoveryMode === "rfc9728_protected_resource" && !protectedResourceMetadataUrl) ||
1415
+ (discoveryMode === "legacy_2025_03_26_metadata" && protectedResourceMetadataUrl))
1416
+ ) {
1417
+ throw new HTTPException(400, { message: "invalid OAuth discovery provenance state" });
1418
+ }
1419
+ const resourceParameterSupported = payload.resourceParameterSupported !== false;
1420
+ if (discoveryMode === "legacy_2025_03_26_metadata" && resourceParameterSupported) {
1421
+ throw new HTTPException(400, { message: "invalid legacy OAuth resource parameter state" });
1422
+ }
1436
1423
  const parsed = {
1437
1424
  accountId: requiredString(payload.accountId, "state.accountId"),
1438
1425
  workspaceId: requiredString(payload.workspaceId, "state.workspaceId"),
@@ -1467,11 +1454,31 @@ function readOAuthState(state: string, settings: Settings): OAuthStatePayload {
1467
1454
  settings,
1468
1455
  "OAuth issuer",
1469
1456
  ),
1457
+ discoveryMode,
1458
+ ...(discoveryMetadataSha256 ? { discoveryMetadataSha256 } : {}),
1459
+ ...(protectedResourceMetadataUrl
1460
+ ? {
1461
+ protectedResourceMetadataUrl: oauthEndpointUrl(
1462
+ protectedResourceMetadataUrl,
1463
+ settings,
1464
+ "OAuth protected resource metadata",
1465
+ ),
1466
+ }
1467
+ : {}),
1468
+ ...(authorizationServerMetadataUrl
1469
+ ? {
1470
+ authorizationServerMetadataUrl: oauthEndpointUrl(
1471
+ authorizationServerMetadataUrl,
1472
+ settings,
1473
+ "OAuth authorization server metadata",
1474
+ ),
1475
+ }
1476
+ : {}),
1470
1477
  clientRegistrationMethod: registrationMethod(payload.clientRegistrationMethod),
1471
1478
  tokenEndpointAuthMethod: tokenAuthMethod(stringValue(payload.tokenEndpointAuthMethod), false),
1472
1479
  // States minted before provider-specific compatibility was introduced used
1473
1480
  // the RFC 8707 resource parameter.
1474
- resourceParameterSupported: payload.resourceParameterSupported !== false,
1481
+ resourceParameterSupported,
1475
1482
  ...(stringValue(payload.encryptedClientSecret)
1476
1483
  ? { encryptedClientSecret: stringValue(payload.encryptedClientSecret)! }
1477
1484
  : {}),
@@ -1484,6 +1491,13 @@ function readOAuthState(state: string, settings: Settings): OAuthStatePayload {
1484
1491
  if (Boolean(connectionId) !== Boolean(connectionVersion)) {
1485
1492
  throw new HTTPException(400, { message: "invalid OAuth reconnect state" });
1486
1493
  }
1494
+ if (
1495
+ parsed.discoveryMode === "legacy_2025_03_26_metadata" &&
1496
+ (normalizedIssuerKey(parsed.authorizationServer) !== normalizedIssuerKey(parsed.issuer) ||
1497
+ new URL(parsed.resource).origin !== new URL(parsed.issuer).origin)
1498
+ ) {
1499
+ throw new HTTPException(400, { message: "invalid OAuth discovery binding state" });
1500
+ }
1487
1501
  return {
1488
1502
  ...parsed,
1489
1503
  ...(connectionId ? { connectionId } : {}),
@@ -1491,6 +1505,17 @@ function readOAuthState(state: string, settings: Settings): OAuthStatePayload {
1491
1505
  };
1492
1506
  }
1493
1507
 
1508
+ function discoveryModeValue(value: string | undefined): McpOAuthDiscoveryMode {
1509
+ if (!value) {
1510
+ // Every state minted before discovery modes existed used RFC 9728 PRM.
1511
+ return "rfc9728_protected_resource";
1512
+ }
1513
+ if (value === "rfc9728_protected_resource" || value === "legacy_2025_03_26_metadata") {
1514
+ return value;
1515
+ }
1516
+ throw new HTTPException(400, { message: "invalid OAuth discovery mode state" });
1517
+ }
1518
+
1494
1519
  function connectionOwnership(value: unknown): ConnectionOwnership | undefined {
1495
1520
  return value === "workspace" || value === "personal" ? value : undefined;
1496
1521
  }
@@ -2033,31 +2058,6 @@ function safeReturnPath(value: string): string {
2033
2058
  return `${parsed.pathname}${parsed.search}${parsed.hash}`;
2034
2059
  }
2035
2060
 
2036
- async function fetchJsonObject(
2037
- url: string,
2038
- settings: Settings,
2039
- signal?: AbortSignal,
2040
- ): Promise<Record<string, unknown>> {
2041
- const response = await fetchOAuth(url, settings, {
2042
- headers: { accept: "application/json" },
2043
- ...(signal ? { signal } : {}),
2044
- });
2045
- if (!response.ok) {
2046
- await cancelResponseBody(response);
2047
- throw new Error(`HTTP ${response.status}`);
2048
- }
2049
- const payload = await readResponseJsonBounded<unknown>(
2050
- response,
2051
- OAUTH_MAX_RESPONSE_BYTES,
2052
- "OAuth metadata response",
2053
- { ...(signal ? { signal } : {}) },
2054
- );
2055
- if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
2056
- throw new Error("metadata response was not a JSON object");
2057
- }
2058
- return payload as Record<string, unknown>;
2059
- }
2060
-
2061
2061
  async function fetchOAuth(
2062
2062
  rawUrl: string,
2063
2063
  settings: Settings,
@@ -2130,41 +2130,6 @@ async function cancelResponseBody(response: Response): Promise<void> {
2130
2130
  await response.body?.cancel().catch(() => undefined);
2131
2131
  }
2132
2132
 
2133
- function parseWwwAuthenticate(header: string | null): WwwAuthenticateChallenge {
2134
- if (!header) {
2135
- return {};
2136
- }
2137
- const bearerIndex = header.toLowerCase().indexOf("bearer");
2138
- if (bearerIndex < 0) {
2139
- return {};
2140
- }
2141
- const paramsText = header.slice(bearerIndex + "bearer".length);
2142
- const params: Record<string, string> = {};
2143
- const re = /([a-zA-Z_][a-zA-Z0-9_-]*)\s*=\s*("(?:[^"\\]|\\.)*"|[^,\s]+)/g;
2144
- let match: RegExpExecArray | null;
2145
- while ((match = re.exec(paramsText)) !== null) {
2146
- const raw = match[2]!;
2147
- params[match[1]!.toLowerCase()] = raw.startsWith('"')
2148
- ? raw.slice(1, -1).replace(/\\"/g, '"')
2149
- : raw;
2150
- }
2151
- return {
2152
- ...(params.resource_metadata ? { resourceMetadata: params.resource_metadata } : {}),
2153
- ...(params.scope ? { scope: params.scope.split(/\s+/).filter(Boolean) } : {}),
2154
- ...(params.error ? { error: params.error } : {}),
2155
- };
2156
- }
2157
-
2158
- function wellKnownCandidates(rawUrl: string, name: string): string[] {
2159
- const url = new URL(rawUrl);
2160
- const path = url.pathname.replace(/^\/+|\/+$/g, "");
2161
- return uniqueStrings([
2162
- `${url.origin}/.well-known/${name}${path ? `/${path}` : ""}`,
2163
- `${url.origin}${path ? `/${path}` : ""}/.well-known/${name}`,
2164
- `${url.origin}/.well-known/${name}`,
2165
- ]);
2166
- }
2167
-
2168
2133
  function chooseAuthorizeScopes(
2169
2134
  requested: string[] | undefined,
2170
2135
  challenged: string[] | undefined,