@softure-ai/mcp-access 0.1.7 → 0.1.8

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 (71) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +39 -3
  3. package/dist/index.d.ts +9 -2
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +3 -2
  6. package/dist/index.js.map +1 -1
  7. package/dist/next/actions.d.ts.map +1 -1
  8. package/dist/next/actions.js +4 -2
  9. package/dist/next/actions.js.map +1 -1
  10. package/dist/next/context.d.ts +7 -1
  11. package/dist/next/context.d.ts.map +1 -1
  12. package/dist/next/context.js +12 -10
  13. package/dist/next/context.js.map +1 -1
  14. package/dist/next/index.d.ts +1 -0
  15. package/dist/next/index.d.ts.map +1 -1
  16. package/dist/next/index.js +1 -0
  17. package/dist/next/index.js.map +1 -1
  18. package/dist/next/oauth-page.d.ts.map +1 -1
  19. package/dist/next/oauth-page.js +3 -2
  20. package/dist/next/oauth-page.js.map +1 -1
  21. package/dist/next/oauth-routes.d.ts +5 -2
  22. package/dist/next/oauth-routes.d.ts.map +1 -1
  23. package/dist/next/oauth-routes.js +9 -6
  24. package/dist/next/oauth-routes.js.map +1 -1
  25. package/dist/options.d.ts +24 -0
  26. package/dist/options.d.ts.map +1 -1
  27. package/dist/options.js +66 -0
  28. package/dist/options.js.map +1 -1
  29. package/dist/origins.d.ts +27 -0
  30. package/dist/origins.d.ts.map +1 -0
  31. package/dist/origins.js +32 -0
  32. package/dist/origins.js.map +1 -0
  33. package/dist/server/context.d.ts +4 -0
  34. package/dist/server/context.d.ts.map +1 -0
  35. package/dist/server/context.js +13 -0
  36. package/dist/server/context.js.map +1 -0
  37. package/dist/server/endpoint.d.ts.map +1 -1
  38. package/dist/server/endpoint.js +5 -3
  39. package/dist/server/endpoint.js.map +1 -1
  40. package/dist/server/index.d.ts +5 -1
  41. package/dist/server/index.d.ts.map +1 -1
  42. package/dist/server/index.js +4 -1
  43. package/dist/server/index.js.map +1 -1
  44. package/dist/server/oauth-http.d.ts +30 -16
  45. package/dist/server/oauth-http.d.ts.map +1 -1
  46. package/dist/server/oauth-http.js +64 -41
  47. package/dist/server/oauth-http.js.map +1 -1
  48. package/dist/server/options.d.ts +3 -2
  49. package/dist/server/options.d.ts.map +1 -1
  50. package/dist/server/options.js +3 -3
  51. package/dist/server/options.js.map +1 -1
  52. package/dist/server/origins.d.ts +8 -0
  53. package/dist/server/origins.d.ts.map +1 -0
  54. package/dist/server/origins.js +15 -0
  55. package/dist/server/origins.js.map +1 -0
  56. package/module.json +1 -1
  57. package/package.json +1 -1
  58. package/src/index.ts +7 -2
  59. package/src/next/actions.ts +4 -2
  60. package/src/next/context.ts +15 -8
  61. package/src/next/index.ts +1 -0
  62. package/src/next/oauth-page.tsx +3 -2
  63. package/src/next/oauth-routes.ts +13 -5
  64. package/src/options.ts +75 -0
  65. package/src/origins.ts +48 -0
  66. package/src/server/context.ts +14 -0
  67. package/src/server/endpoint.ts +5 -3
  68. package/src/server/index.ts +6 -0
  69. package/src/server/oauth-http.ts +90 -41
  70. package/src/server/options.ts +4 -3
  71. package/src/server/origins.ts +19 -0
@@ -0,0 +1,27 @@
1
+ /** What the origin helpers read from a request: a Web `Request`, or Next's `headers()` with a URL. */
2
+ export interface McpOriginRequest {
3
+ readonly url: string;
4
+ readonly headers: {
5
+ get(name: string): string | null;
6
+ };
7
+ }
8
+ export interface McpOrigins {
9
+ /** The issuer, and the origin of every endpoint, of `resource_metadata` and of the consent form. */
10
+ readonly appOrigin: string;
11
+ /** Other public hosts (`resourceOrigins`), each a bare origin. */
12
+ readonly resourceOrigins: readonly string[];
13
+ }
14
+ /** The host the request was sent to: `Host`, else the URL's host. Behind a proxy the URL names the server's own. */
15
+ export declare function readRequestHost(request: McpOriginRequest): string;
16
+ /**
17
+ * The origin the request was sent to: the first `X-Forwarded-Proto` value when it is http or https
18
+ * (else the URL's scheme) and `Host` (else the URL's host). For `resolveAppOrigin` in an app whose
19
+ * proxy passes `Host` through and refuses hosts it does not serve.
20
+ */
21
+ export declare function readRequestOrigin(request: McpOriginRequest): string;
22
+ /**
23
+ * The resource origin the request was sent to, when its host is one of `resourceOrigins`; when two
24
+ * share the host, the one whose scheme `X-Forwarded-Proto` names wins, else the first. Null otherwise.
25
+ */
26
+ export declare function findServedResourceOrigin(origins: McpOrigins, request: McpOriginRequest): string | null;
27
+ //# sourceMappingURL=origins.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"origins.d.ts","sourceRoot":"","sources":["../src/origins.ts"],"names":[],"mappings":"AAGA,sGAAsG;AACtG,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,OAAO,EAAE;QAAE,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;CACxD;AAED,MAAM,WAAW,UAAU;IACzB,oGAAoG;IACpG,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,kEAAkE;IAClE,QAAQ,CAAC,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;CAC7C;AAOD,oHAAoH;AACpH,wBAAgB,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,CAEjE;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,CAKnE;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,gBAAgB,GAAG,MAAM,GAAG,IAAI,CAKtG"}
@@ -0,0 +1,32 @@
1
+ // The origins one request is served under: the app origin every OAuth URL is built from, and the
2
+ // other public hosts whose root may be named as a protected resource. Pure: no config, no database.
3
+ function firstValue(header) {
4
+ const value = header?.split(",")[0]?.trim().toLowerCase();
5
+ return value === undefined || value === "" ? null : value;
6
+ }
7
+ /** The host the request was sent to: `Host`, else the URL's host. Behind a proxy the URL names the server's own. */
8
+ export function readRequestHost(request) {
9
+ return firstValue(request.headers.get("host")) ?? new URL(request.url).host;
10
+ }
11
+ /**
12
+ * The origin the request was sent to: the first `X-Forwarded-Proto` value when it is http or https
13
+ * (else the URL's scheme) and `Host` (else the URL's host). For `resolveAppOrigin` in an app whose
14
+ * proxy passes `Host` through and refuses hosts it does not serve.
15
+ */
16
+ export function readRequestOrigin(request) {
17
+ const url = new URL(request.url);
18
+ const proto = firstValue(request.headers.get("x-forwarded-proto"));
19
+ const scheme = proto === "http" || proto === "https" ? proto : url.protocol.replace(/:$/, "");
20
+ return new URL(`${scheme}://${readRequestHost(request)}`).origin;
21
+ }
22
+ /**
23
+ * The resource origin the request was sent to, when its host is one of `resourceOrigins`; when two
24
+ * share the host, the one whose scheme `X-Forwarded-Proto` names wins, else the first. Null otherwise.
25
+ */
26
+ export function findServedResourceOrigin(origins, request) {
27
+ const host = readRequestHost(request);
28
+ const candidates = origins.resourceOrigins.filter((origin) => new URL(origin).host === host);
29
+ const proto = firstValue(request.headers.get("x-forwarded-proto"));
30
+ return candidates.find((origin) => new URL(origin).protocol === `${proto ?? ""}:`) ?? candidates[0] ?? null;
31
+ }
32
+ //# sourceMappingURL=origins.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"origins.js","sourceRoot":"","sources":["../src/origins.ts"],"names":[],"mappings":"AAAA,iGAAiG;AACjG,oGAAoG;AAepG,SAAS,UAAU,CAAC,MAAqB;IACvC,MAAM,KAAK,GAAG,MAAM,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC1D,OAAO,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC;AAC5D,CAAC;AAED,oHAAoH;AACpH,MAAM,UAAU,eAAe,CAAC,OAAyB;IACvD,OAAO,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;AAC9E,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAyB;IACzD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACjC,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC,CAAC;IACnE,MAAM,MAAM,GAAG,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAC9F,OAAO,IAAI,GAAG,CAAC,GAAG,MAAM,MAAM,eAAe,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC;AACnE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,wBAAwB,CAAC,OAAmB,EAAE,OAAyB;IACrF,MAAM,IAAI,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;IACtC,MAAM,UAAU,GAAG,OAAO,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;IAC7F,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC,CAAC;IACnE,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,QAAQ,KAAK,GAAG,KAAK,IAAI,EAAE,GAAG,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;AAC9G,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { type SoftureConfig } from "@softure-ai/core";
2
+ import type { McpAccessContext } from "./tokens.js";
3
+ export declare function createMcpAccessContext(config: SoftureConfig): Promise<McpAccessContext>;
4
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/server/context.ts"],"names":[],"mappings":"AAGA,OAAO,EAAe,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD,wBAAsB,sBAAsB,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAM7F"}
@@ -0,0 +1,13 @@
1
+ // The module context (`{ db, clock, config }`) from the configuration: the process-wide database
2
+ // handle and the wall clock. For apps that compose their own routes from `/server`; the `/next`
3
+ // adapter builds its context with it.
4
+ import { systemClock } from "@softure-ai/core";
5
+ import { getConfiguredDatabase } from "@softure-ai/db";
6
+ export async function createMcpAccessContext(config) {
7
+ if (config.database === null) {
8
+ throw new Error("@softure-ai/mcp-access: softure.config.ts has no database; mcp-access needs one");
9
+ }
10
+ const { db } = await getConfiguredDatabase(config.database);
11
+ return { db, clock: systemClock, config };
12
+ }
13
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/server/context.ts"],"names":[],"mappings":"AAAA,iGAAiG;AACjG,gGAAgG;AAChG,sCAAsC;AACtC,OAAO,EAAE,WAAW,EAAsB,MAAM,kBAAkB,CAAC;AACnE,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAGvD,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAAC,MAAqB;IAChE,IAAI,MAAM,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,iFAAiF,CAAC,CAAC;IACrG,CAAC;IACD,MAAM,EAAE,EAAE,EAAE,GAAG,MAAM,qBAAqB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC5D,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC;AAC5C,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"endpoint.d.ts","sourceRoot":"","sources":["../../src/server/endpoint.ts"],"names":[],"mappings":"AAKA,OAAO,EAML,KAAK,SAAS,EACd,KAAK,MAAM,EACZ,MAAM,8BAA8B,CAAC;AAGtC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAExD,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAqB,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEvE,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,CAAC;AAE3C,8FAA8F;AAC9F,eAAO,MAAM,qBAAqB,QAAQ,CAAC;AAE3C,6FAA6F;AAC7F,eAAO,MAAM,qBAAqB,QAAc,CAAC;AAOjD,mGAAmG;AACnG,MAAM,MAAM,gBAAgB,GAAG,CAAC,QAAQ,EAAE,iBAAiB,KAAK,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,MAAM,CAAC,CAAC;AAEjH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,YAAY,EAAE,gBAAgB,CAAC;CACzC;AAED,8CAA8C;AAC9C,MAAM,MAAM,WAAW,GAAG,CAAC,GAAG,EAAE,gBAAgB,EAAE,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AAEzF;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,YAAY,EAAE,EAAE,kBAAkB,GAAG,WAAW,CAmCnF"}
1
+ {"version":3,"file":"endpoint.d.ts","sourceRoot":"","sources":["../../src/server/endpoint.ts"],"names":[],"mappings":"AAKA,OAAO,EAML,KAAK,SAAS,EACd,KAAK,MAAM,EACZ,MAAM,8BAA8B,CAAC;AAGtC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAGxD,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAqB,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEvE,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,CAAC;AAE3C,8FAA8F;AAC9F,eAAO,MAAM,qBAAqB,QAAQ,CAAC;AAE3C,6FAA6F;AAC7F,eAAO,MAAM,qBAAqB,QAAc,CAAC;AAOjD,mGAAmG;AACnG,MAAM,MAAM,gBAAgB,GAAG,CAAC,QAAQ,EAAE,iBAAiB,KAAK,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,MAAM,CAAC,CAAC;AAEjH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,YAAY,EAAE,gBAAgB,CAAC;CACzC;AAED,8CAA8C;AAC9C,MAAM,MAAM,WAAW,GAAG,CAAC,GAAG,EAAE,gBAAgB,EAAE,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AAEzF;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,YAAY,EAAE,EAAE,kBAAkB,GAAG,WAAW,CAmCnF"}
@@ -7,6 +7,7 @@ import { bearerAuthChallengeResponse, createMcpHandler, OAuthError, OAuthErrorCo
7
7
  import { errorLogLabel, getModule } from "@softure-ai/core";
8
8
  import { consumeRateLimit, identifyClient } from "@softure-ai/security/server";
9
9
  import { getProtectedResourceMetadataUrl, isOAuthEnabled } from "./oauth-http.js";
10
+ import { resolveMcpOrigins } from "./origins.js";
10
11
  import { MCP_READ_SCOPE, MCP_WRITE_SCOPE } from "./scopes.js";
11
12
  import { verifyAccessToken } from "./tokens.js";
12
13
  export { MCP_READ_SCOPE, MCP_WRITE_SCOPE };
@@ -48,7 +49,7 @@ export function createMcpEndpoint({ createServer }) {
48
49
  if (!limit.ok) {
49
50
  return Response.json({ error: "too_many_requests" }, { status: 429, headers: { ...NO_STORE, "retry-after": String(limit.retryAfterSeconds) } });
50
51
  }
51
- const authInfo = await authenticate(ctx, request.headers.get("authorization"));
52
+ const authInfo = await authenticate(ctx, request);
52
53
  if (authInfo instanceof Response)
53
54
  return authInfo;
54
55
  return handler.fetch(request, { authInfo });
@@ -61,9 +62,10 @@ export function createMcpEndpoint({ createServer }) {
61
62
  * names the protected resource metadata (RFC 9728 §5.1): that is how claude.ai and ChatGPT find
62
63
  * the authorization server.
63
64
  */
64
- async function authenticate(ctx, header) {
65
+ async function authenticate(ctx, request) {
66
+ const header = request.headers.get("authorization");
65
67
  const challenge = isOAuthEnabled(ctx.config)
66
- ? { requiredScopes: [MCP_READ_SCOPE], resourceMetadataUrl: getProtectedResourceMetadataUrl(ctx.config) }
68
+ ? { requiredScopes: [MCP_READ_SCOPE], resourceMetadataUrl: getProtectedResourceMetadataUrl(ctx.config, resolveMcpOrigins(ctx.config, request)) }
67
69
  : { requiredScopes: [MCP_READ_SCOPE] };
68
70
  if (header === null || !BEARER_PREFIX.test(header)) {
69
71
  return bearerAuthChallengeResponse(new OAuthError(OAuthErrorCode.InvalidToken, "Missing Bearer token"), challenge);
@@ -1 +1 @@
1
- {"version":3,"file":"endpoint.js","sourceRoot":"","sources":["../../src/server/endpoint.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"endpoint.js","sourceRoot":"","sources":["../../src/server/endpoint.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,11 @@
1
1
  export { createMcpEndpoint, MAX_MCP_REQUEST_BYTES, MCP_RATE_LIMIT_BUCKET, MCP_READ_SCOPE, MCP_WRITE_SCOPE, type McpEndpoint, type McpEndpointOptions, type McpServerFactory, } from "./endpoint.js";
2
+ export { createMcpAccessContext } from "./context.js";
2
3
  export { checkAccessTokensTable } from "./health.js";
3
4
  export { CONSENT_CODE_PREFIX, CLIENT_SECRET_PREFIX, createAuthorizationCode, exchangeAuthorizationCode, findOAuthClient, isClientSecretValid, listOAuthGrants, pruneOAuthRecords, REFRESH_TOKEN_PREFIX, refreshMcpGrant, registerMcpClient, revokeOAuthGrant, type CreateAuthorizationCodeInput, type ExchangeAuthorizationCodeInput, type IssuedOAuthTokens, type OAuthClientRegistration, type PrunedOAuthRecords, type RefreshOAuthGrantInput, type RegisteredOAuthClient, type RevokeOAuthGrantInput, } from "./oauth.js";
4
- export { answerOAuthPreflight, AUTHORIZATION_PARAMS, AUTHORIZATION_SERVER_METADATA_PATH, buildClientRedirect, getAuthorizationServerMetadata, getConsentPath, getOAuthIssuer, getProtectedResourceMetadata, getProtectedResourceMetadataUrl, handleAuthorizationDecision, handleClientRegistration, handleTokenRequest, isAcceptableResource, isOAuthEnabled, isWriteScopeRequested, MCP_OAUTH_RATE_LIMIT_BUCKET, OAUTH_SCOPES, PROTECTED_RESOURCE_METADATA_PATH, readAuthorizationParams, serveDiscoveryDocument, validateAuthorizationRequest, type AuthorizationOutcome, type AuthorizationPageError, type AuthorizationRequest, } from "./oauth-http.js";
5
+ export { answerOAuthPreflight, AUTHORIZATION_PARAMS, AUTHORIZATION_SERVER_METADATA_PATH, buildClientRedirect, getAuthorizationServerMetadata, getConsentPath, getOAuthIssuer, getProtectedResourceMetadata, getProtectedResourceMetadataUrl, getRootProtectedResourceMetadata, handleAuthorizationDecision, handleClientRegistration, handleTokenRequest, isAcceptableResource, isOAuthEnabled, isWriteScopeRequested, MCP_OAUTH_RATE_LIMIT_BUCKET, OAUTH_SCOPES, PROTECTED_RESOURCE_METADATA_PATH, readAuthorizationParams, serveDiscoveryDocument, validateAuthorizationRequest, type AuthorizationOutcome, type AuthorizationPageError, type AuthorizationRequest, type DiscoveryDocument, } from "./oauth-http.js";
6
+ export { findServedResourceOrigin, readRequestHost, readRequestOrigin, type McpOriginRequest, type McpOrigins } from "../origins.js";
7
+ export { resolveMcpOrigins } from "./origins.js";
8
+ export type { OAuthClientRow, OAuthTokenEndpointAuthMethod } from "../schema.js";
5
9
  export { describeRedirectUri, isAllowedRedirectUri, MAX_CLIENT_NAME_LENGTH, parseClientRegistration, type ClientRegistrationResult, type RegistrationError } from "./oauth-validation.js";
6
10
  export { getCodeChallenge, isPkceVerifierValid, isValidCodeChallenge, PKCE_METHOD } from "./pkce.js";
7
11
  export { getMcpAccessMessages, getMcpAccessOptions, getMcpAccessRoutes, getMcpEndpointUrl, type McpAccessRoutes } from "./options.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,qBAAqB,EACrB,cAAc,EACd,eAAe,EACf,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,GACtB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,uBAAuB,EACvB,yBAAyB,EACzB,eAAe,EACf,mBAAmB,EACnB,eAAe,EACf,iBAAiB,EACjB,oBAAoB,EACpB,eAAe,EACf,iBAAiB,EACjB,gBAAgB,EAChB,KAAK,4BAA4B,EACjC,KAAK,8BAA8B,EACnC,KAAK,iBAAiB,EACtB,KAAK,uBAAuB,EAC5B,KAAK,kBAAkB,EACvB,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,GAC3B,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,kCAAkC,EAClC,mBAAmB,EACnB,8BAA8B,EAC9B,cAAc,EACd,cAAc,EACd,4BAA4B,EAC5B,+BAA+B,EAC/B,2BAA2B,EAC3B,wBAAwB,EACxB,kBAAkB,EAClB,oBAAoB,EACpB,cAAc,EACd,qBAAqB,EACrB,2BAA2B,EAC3B,YAAY,EACZ,gCAAgC,EAChC,uBAAuB,EACvB,sBAAsB,EACtB,4BAA4B,EAC5B,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,EAC3B,KAAK,oBAAoB,GAC1B,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,KAAK,wBAAwB,EAAE,KAAK,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1L,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACrG,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AACtI,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACvB,2BAA2B,EAC3B,KAAK,iBAAiB,GACvB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,kBAAkB,EAClB,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,EAC3B,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,sBAAsB,EAC3B,KAAK,mBAAmB,GACzB,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,qBAAqB,EACrB,cAAc,EACd,eAAe,EACf,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,GACtB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,uBAAuB,EACvB,yBAAyB,EACzB,eAAe,EACf,mBAAmB,EACnB,eAAe,EACf,iBAAiB,EACjB,oBAAoB,EACpB,eAAe,EACf,iBAAiB,EACjB,gBAAgB,EAChB,KAAK,4BAA4B,EACjC,KAAK,8BAA8B,EACnC,KAAK,iBAAiB,EACtB,KAAK,uBAAuB,EAC5B,KAAK,kBAAkB,EACvB,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,GAC3B,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,kCAAkC,EAClC,mBAAmB,EACnB,8BAA8B,EAC9B,cAAc,EACd,cAAc,EACd,4BAA4B,EAC5B,+BAA+B,EAC/B,gCAAgC,EAChC,2BAA2B,EAC3B,wBAAwB,EACxB,kBAAkB,EAClB,oBAAoB,EACpB,cAAc,EACd,qBAAqB,EACrB,2BAA2B,EAC3B,YAAY,EACZ,gCAAgC,EAChC,uBAAuB,EACvB,sBAAsB,EACtB,4BAA4B,EAC5B,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,EAC3B,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,GACvB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,wBAAwB,EAAE,eAAe,EAAE,iBAAiB,EAAE,KAAK,gBAAgB,EAAE,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC;AACrI,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,YAAY,EAAE,cAAc,EAAE,4BAA4B,EAAE,MAAM,cAAc,CAAC;AACjF,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,KAAK,wBAAwB,EAAE,KAAK,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1L,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACrG,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AACtI,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACvB,2BAA2B,EAC3B,KAAK,iBAAiB,GACvB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,kBAAkB,EAClB,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,EAC3B,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,sBAAsB,EAC3B,KAAK,mBAAmB,GACzB,MAAM,aAAa,CAAC"}
@@ -1,9 +1,12 @@
1
1
  // Server-only API of @softure-ai/mcp-access. Every function receives the module context
2
2
  // (`{ db, clock, config }`) and never reads request scope; the endpoint takes a Web `Request`.
3
3
  export { createMcpEndpoint, MAX_MCP_REQUEST_BYTES, MCP_RATE_LIMIT_BUCKET, MCP_READ_SCOPE, MCP_WRITE_SCOPE, } from "./endpoint.js";
4
+ export { createMcpAccessContext } from "./context.js";
4
5
  export { checkAccessTokensTable } from "./health.js";
5
6
  export { CONSENT_CODE_PREFIX, CLIENT_SECRET_PREFIX, createAuthorizationCode, exchangeAuthorizationCode, findOAuthClient, isClientSecretValid, listOAuthGrants, pruneOAuthRecords, REFRESH_TOKEN_PREFIX, refreshMcpGrant, registerMcpClient, revokeOAuthGrant, } from "./oauth.js";
6
- export { answerOAuthPreflight, AUTHORIZATION_PARAMS, AUTHORIZATION_SERVER_METADATA_PATH, buildClientRedirect, getAuthorizationServerMetadata, getConsentPath, getOAuthIssuer, getProtectedResourceMetadata, getProtectedResourceMetadataUrl, handleAuthorizationDecision, handleClientRegistration, handleTokenRequest, isAcceptableResource, isOAuthEnabled, isWriteScopeRequested, MCP_OAUTH_RATE_LIMIT_BUCKET, OAUTH_SCOPES, PROTECTED_RESOURCE_METADATA_PATH, readAuthorizationParams, serveDiscoveryDocument, validateAuthorizationRequest, } from "./oauth-http.js";
7
+ export { answerOAuthPreflight, AUTHORIZATION_PARAMS, AUTHORIZATION_SERVER_METADATA_PATH, buildClientRedirect, getAuthorizationServerMetadata, getConsentPath, getOAuthIssuer, getProtectedResourceMetadata, getProtectedResourceMetadataUrl, getRootProtectedResourceMetadata, handleAuthorizationDecision, handleClientRegistration, handleTokenRequest, isAcceptableResource, isOAuthEnabled, isWriteScopeRequested, MCP_OAUTH_RATE_LIMIT_BUCKET, OAUTH_SCOPES, PROTECTED_RESOURCE_METADATA_PATH, readAuthorizationParams, serveDiscoveryDocument, validateAuthorizationRequest, } from "./oauth-http.js";
8
+ export { findServedResourceOrigin, readRequestHost, readRequestOrigin } from "../origins.js";
9
+ export { resolveMcpOrigins } from "./origins.js";
7
10
  export { describeRedirectUri, isAllowedRedirectUri, MAX_CLIENT_NAME_LENGTH, parseClientRegistration } from "./oauth-validation.js";
8
11
  export { getCodeChallenge, isPkceVerifierValid, isValidCodeChallenge, PKCE_METHOD } from "./pkce.js";
9
12
  export { getMcpAccessMessages, getMcpAccessOptions, getMcpAccessRoutes, getMcpEndpointUrl } from "./options.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,wFAAwF;AACxF,+FAA+F;AAC/F,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,qBAAqB,EACrB,cAAc,EACd,eAAe,GAIhB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,uBAAuB,EACvB,yBAAyB,EACzB,eAAe,EACf,mBAAmB,EACnB,eAAe,EACf,iBAAiB,EACjB,oBAAoB,EACpB,eAAe,EACf,iBAAiB,EACjB,gBAAgB,GASjB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,kCAAkC,EAClC,mBAAmB,EACnB,8BAA8B,EAC9B,cAAc,EACd,cAAc,EACd,4BAA4B,EAC5B,+BAA+B,EAC/B,2BAA2B,EAC3B,wBAAwB,EACxB,kBAAkB,EAClB,oBAAoB,EACpB,cAAc,EACd,qBAAqB,EACrB,2BAA2B,EAC3B,YAAY,EACZ,gCAAgC,EAChC,uBAAuB,EACvB,sBAAsB,EACtB,4BAA4B,GAI7B,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,uBAAuB,EAAyD,MAAM,uBAAuB,CAAC;AAC1L,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACrG,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,iBAAiB,EAAwB,MAAM,cAAc,CAAC;AACtI,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACvB,2BAA2B,GAE5B,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,kBAAkB,EAClB,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,GAOlB,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,wFAAwF;AACxF,+FAA+F;AAC/F,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,qBAAqB,EACrB,cAAc,EACd,eAAe,GAIhB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,uBAAuB,EACvB,yBAAyB,EACzB,eAAe,EACf,mBAAmB,EACnB,eAAe,EACf,iBAAiB,EACjB,oBAAoB,EACpB,eAAe,EACf,iBAAiB,EACjB,gBAAgB,GASjB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,kCAAkC,EAClC,mBAAmB,EACnB,8BAA8B,EAC9B,cAAc,EACd,cAAc,EACd,4BAA4B,EAC5B,+BAA+B,EAC/B,gCAAgC,EAChC,2BAA2B,EAC3B,wBAAwB,EACxB,kBAAkB,EAClB,oBAAoB,EACpB,cAAc,EACd,qBAAqB,EACrB,2BAA2B,EAC3B,YAAY,EACZ,gCAAgC,EAChC,uBAAuB,EACvB,sBAAsB,EACtB,4BAA4B,GAK7B,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,wBAAwB,EAAE,eAAe,EAAE,iBAAiB,EAA0C,MAAM,eAAe,CAAC;AACrI,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEjD,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,uBAAuB,EAAyD,MAAM,uBAAuB,CAAC;AAC1L,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACrG,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,iBAAiB,EAAwB,MAAM,cAAc,CAAC;AACtI,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACvB,2BAA2B,GAE5B,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,kBAAkB,EAClB,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,GAOlB,MAAM,aAAa,CAAC"}
@@ -1,4 +1,5 @@
1
1
  import { type SoftureConfig } from "@softure-ai/core";
2
+ import { type McpOriginRequest, type McpOrigins } from "../origins.js";
2
3
  import type { OAuthClientRow } from "../schema.js";
3
4
  import type { McpAccessContext } from "./tokens.js";
4
5
  /** The security bucket OAuth registration and token requests count in. */
@@ -12,14 +13,20 @@ export declare const OAUTH_SCOPES: readonly ["mcp:read", "mcp:write"];
12
13
  export declare const AUTHORIZATION_PARAMS: readonly ["response_type", "client_id", "redirect_uri", "state", "code_challenge", "code_challenge_method", "scope", "resource"];
13
14
  /** `OPTIONS` on the registration, token and discovery routes. */
14
15
  export declare function answerOAuthPreflight(): Response;
15
- /** The issuer: the app's origin, without a path or a trailing slash. */
16
- export declare function getOAuthIssuer(config: SoftureConfig): string;
16
+ /**
17
+ * The issuer: the app's origin, without a path or a trailing slash. `origins` are the request's
18
+ * (`resolveMcpOrigins`); without them, the configured `appOrigin`.
19
+ */
20
+ export declare function getOAuthIssuer(config: SoftureConfig, origins?: McpOrigins): string;
17
21
  /** The URL the endpoint's `401` names in `resource_metadata` (RFC 9728 §5.1). */
18
- export declare function getProtectedResourceMetadataUrl(config: SoftureConfig): string;
22
+ export declare function getProtectedResourceMetadataUrl(config: SoftureConfig, origins?: McpOrigins): string;
19
23
  /** Whether the app turned OAuth on; the OAuth routes answer 404 otherwise. */
20
24
  export declare function isOAuthEnabled(config: SoftureConfig): boolean;
21
- /** Authorization server metadata (RFC 8414 §2). Tokens are opaque: no `jwks_uri`, no OpenID. */
22
- export declare function getAuthorizationServerMetadata(config: SoftureConfig): {
25
+ /**
26
+ * Authorization server metadata (RFC 8414 §2), with the app's `oauth.metadata.authorizationServer`
27
+ * keys added; generated keys win. Tokens are opaque: no OpenID.
28
+ */
29
+ export declare function getAuthorizationServerMetadata(config: SoftureConfig, origins?: McpOrigins): {
23
30
  issuer: string;
24
31
  authorization_endpoint: string;
25
32
  token_endpoint: string;
@@ -33,24 +40,30 @@ export declare function getAuthorizationServerMetadata(config: SoftureConfig): {
33
40
  authorization_response_iss_parameter_supported: boolean;
34
41
  };
35
42
  /**
36
- * Protected resource metadata (RFC 9728 §2). At the endpoint's own path the resource is the
37
- * endpoint URL; at the root it is the origin, because RFC 9728 §3.3 ties `resource` to the URL the
38
- * metadata URL was built from.
43
+ * Protected resource metadata (RFC 9728 §2), with the app's `oauth.metadata.protectedResource`
44
+ * keys added; generated keys win, except the app's `resource_name`. At the endpoint's own path the
45
+ * resource is the endpoint URL; at the root it is the origin the document was asked on
46
+ * (`servedOrigin`, one of `resourceOrigins`) or else the app origin, because RFC 9728 §3.3 ties
47
+ * `resource` to the URL the metadata URL was built from.
39
48
  */
40
- export declare function getProtectedResourceMetadata(config: SoftureConfig, variant: "endpoint" | "root"): {
49
+ export declare function getProtectedResourceMetadata(config: SoftureConfig, variant: "endpoint" | "root", origins?: McpOrigins, servedOrigin?: string | null): {
41
50
  resource: string;
42
51
  authorization_servers: string[];
43
52
  scopes_supported: ("mcp:read" | "mcp:write")[];
44
53
  bearer_methods_supported: string[];
45
54
  resource_name: string;
46
55
  };
47
- /** `GET` of a discovery document; 404 while OAuth is off. */
48
- export declare function serveDiscoveryDocument(config: SoftureConfig, document: (config: SoftureConfig) => unknown): Response;
56
+ /** Builds a discovery document for the request's origins; the request is null outside a route. */
57
+ export type DiscoveryDocument = (config: SoftureConfig, origins: McpOrigins, request: McpOriginRequest | null) => unknown;
58
+ /** `GET` of a discovery document for `request` (its origins); 404 while OAuth is off. */
59
+ export declare function serveDiscoveryDocument(config: SoftureConfig, document: DiscoveryDocument, request?: McpOriginRequest | null): Response;
60
+ /** `serveDiscoveryDocument`'s builder for the root protected resource metadata, on the host it was asked on. */
61
+ export declare const getRootProtectedResourceMetadata: DiscoveryDocument;
49
62
  /**
50
63
  * Whether `resource` (RFC 8707) names this server: absent (clients from before RFC 8707), the
51
- * endpoint URL or the origin, each with or without a trailing slash.
64
+ * endpoint URL, the app origin or one of `resourceOrigins`, each with or without a trailing slash.
52
65
  */
53
- export declare function isAcceptableResource(config: SoftureConfig, resource: string | null): boolean;
66
+ export declare function isAcceptableResource(config: SoftureConfig, resource: string | null, origins?: McpOrigins): boolean;
54
67
  /** Whether the client asks to write. Unknown scopes are ignored (RFC 6749 §3.3); every grant reads. */
55
68
  export declare function isWriteScopeRequested(scope: string | null): boolean;
56
69
  /**
@@ -88,7 +101,7 @@ export type AuthorizationOutcome = {
88
101
  * The client's redirect URI with parameters added, keeping the query it registered (RFC 6749
89
102
  * §3.1.2), and always `iss` (RFC 9207), so the client knows the answer came from this server.
90
103
  */
91
- export declare function buildClientRedirect(config: SoftureConfig, redirectUri: string, params: Readonly<Record<string, string | null>>): string;
104
+ export declare function buildClientRedirect(config: SoftureConfig, redirectUri: string, params: Readonly<Record<string, string | null>>, origins?: McpOrigins): string;
92
105
  /** The authorization parameters of a query string or form, each at most 2048 characters. */
93
106
  export declare function readAuthorizationParams(source: {
94
107
  get(name: string): unknown;
@@ -97,9 +110,10 @@ export declare function readAuthorizationParams(source: {
97
110
  * Validates an authorization request (RFC 6749 §4.1.1), for the consent page and the decision.
98
111
  * Order is a security rule (RFC 6749 §4.1.2.1): until the client and its redirect URI are
99
112
  * confirmed, an error is shown to the person, never redirected, or the server would redirect
100
- * anyone anywhere. Only then do errors go back to the client.
113
+ * anyone anywhere. Only then do errors go back to the client. `origins` are the request's
114
+ * (the consent page's and the decision's must agree); without them, the configured ones.
101
115
  */
102
- export declare function validateAuthorizationRequest(ctx: McpAccessContext, params: URLSearchParams): Promise<AuthorizationOutcome>;
116
+ export declare function validateAuthorizationRequest(ctx: McpAccessContext, params: URLSearchParams, origins?: McpOrigins): Promise<AuthorizationOutcome>;
103
117
  /** The consent page with the request's parameters: where the decision sends the person back. */
104
118
  export declare function getConsentPath(config: SoftureConfig, params: URLSearchParams): string;
105
119
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"oauth-http.d.ts","sourceRoot":"","sources":["../../src/server/oauth-http.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"oauth-http.d.ts","sourceRoot":"","sources":["../../src/server/oauth-http.ts"],"names":[],"mappings":"AAKA,OAAO,EAA4B,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGhF,OAAO,EAA4B,KAAK,gBAAgB,EAAE,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC;AACjG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAenD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD,0EAA0E;AAC1E,eAAO,MAAM,2BAA2B,cAAc,CAAC;AAEvD,iFAAiF;AACjF,eAAO,MAAM,kCAAkC,4CAA4C,CAAC;AAC5F,6FAA6F;AAC7F,eAAO,MAAM,gCAAgC,0CAA0C,CAAC;AAExF,eAAO,MAAM,YAAY,oCAA6C,CAAC;AAEvE,gFAAgF;AAChF,eAAO,MAAM,oBAAoB,kIAAmI,CAAC;AA4BrK,iEAAiE;AACjE,wBAAgB,oBAAoB,IAAI,QAAQ,CAE/C;AAKD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,GAAE,UAAsC,GAAG,MAAM,CAE7G;AAED,iFAAiF;AACjF,wBAAgB,+BAA+B,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,GAAE,UAAsC,GAAG,MAAM,CAE9H;AAED,8EAA8E;AAC9E,wBAAgB,cAAc,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAE7D;AAQD;;;GAGG;AACH,wBAAgB,8BAA8B,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,GAAE,UAAsC;;;;;;;;;;;;EAkBpH;AAED;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,UAAU,GAAG,MAAM,EAC5B,OAAO,GAAE,UAAsC,EAC/C,YAAY,GAAE,MAAM,GAAG,IAAW;;;;;;EAWnC;AAED,kGAAkG;AAClG,MAAM,MAAM,iBAAiB,GAAG,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,gBAAgB,GAAG,IAAI,KAAK,OAAO,CAAC;AAE1H,yFAAyF;AACzF,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,aAAa,EAAE,QAAQ,EAAE,iBAAiB,EAAE,OAAO,GAAE,gBAAgB,GAAG,IAAW,GAAG,QAAQ,CAG5I;AAED,gHAAgH;AAChH,eAAO,MAAM,gCAAgC,EAAE,iBAC8E,CAAC;AAE9H;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,GAAE,UAAsC,GAAG,OAAO,CAI7I;AAED,uGAAuG;AACvG,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAEnE;AAuCD;;;GAGG;AACH,wBAAsB,wBAAwB,CAAC,GAAG,EAAE,gBAAgB,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAiCzG;AA2CD;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,gBAAgB,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAkBnG;AAmCD,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,wCAAwC;IACxC,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC;CACpC;AAED,oGAAoG;AACpG,MAAM,MAAM,sBAAsB,GAAG,gBAAgB,GAAG,mBAAmB,CAAC;AAE5E,MAAM,MAAM,oBAAoB,GAC5B;IAAE,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,sBAAsB,CAAA;CAAE,GACxE;IAAE,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAC5F;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,oBAAoB,CAAA;CAAE,CAAC;AAEvE;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,aAAa,EACrB,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC,EAC/C,OAAO,GAAE,UAAsC,GAC9C,MAAM,CAMR;AAED,4FAA4F;AAC5F,wBAAgB,uBAAuB,CAAC,MAAM,EAAE;IAAE,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,GAAG,eAAe,CAO/F;AAED;;;;;;GAMG;AACH,wBAAsB,4BAA4B,CAChD,GAAG,EAAE,gBAAgB,EACrB,MAAM,EAAE,eAAe,EACvB,OAAO,GAAE,UAA0C,GAClD,OAAO,CAAC,oBAAoB,CAAC,CAqB/B;AAED,gGAAgG;AAChG,wBAAgB,cAAc,CAAC,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,eAAe,GAAG,MAAM,CAErF;AAYD;;;;;;GAMG;AACH,wBAAsB,2BAA2B,CAAC,GAAG,EAAE,gBAAgB,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,QAAQ,CAAC,CAgCnI"}
@@ -5,10 +5,12 @@
5
5
  // the Next adapter only supplies the context and the session.
6
6
  import { errorLogLabel, getModule } from "@softure-ai/core";
7
7
  import { consumeRateLimit, identifyClient } from "@softure-ai/security/server";
8
+ import { findServedResourceOrigin } from "../origins.js";
8
9
  import { MCP_READ_SCOPE, MCP_WRITE_SCOPE } from "./scopes.js";
9
10
  import { createAuthorizationCode, exchangeAuthorizationCode, findOAuthClient, isClientSecretValid, refreshMcpGrant, registerMcpClient, } from "./oauth.js";
10
11
  import { parseClientRegistration } from "./oauth-validation.js";
11
12
  import { getMcpAccessOptions, getMcpAccessRoutes, getMcpEndpointUrl } from "./options.js";
13
+ import { resolveMcpOrigins } from "./origins.js";
12
14
  import { isValidCodeChallenge, PKCE_METHOD } from "./pkce.js";
13
15
  /** The security bucket OAuth registration and token requests count in. */
14
16
  export const MCP_OAUTH_RATE_LIMIT_BUCKET = "mcp-oauth";
@@ -32,8 +34,8 @@ const CORS_HEADERS = {
32
34
  };
33
35
  /** Responses that carry credentials must not be stored (RFC 6749 §5.1). */
34
36
  const NO_STORE = { "cache-control": "no-store", pragma: "no-cache" };
35
- /** Discovery documents change only with a deploy. */
36
- const DISCOVERY_CACHE = { "cache-control": "public, max-age=300" };
37
+ /** Discovery documents change only with a deploy, and with the host they were asked on. */
38
+ const DISCOVERY_CACHE = { "cache-control": "public, max-age=300", vary: "host, x-forwarded-proto" };
37
39
  function protocolJson(body, status = 200, extra = {}) {
38
40
  return Response.json(body, { status, headers: { ...CORS_HEADERS, ...NO_STORE, ...extra } });
39
41
  }
@@ -47,23 +49,36 @@ export function answerOAuthPreflight() {
47
49
  }
48
50
  // ---------------------------------------------------------------------------------------------
49
51
  // Discovery
50
- /** The issuer: the app's origin, without a path or a trailing slash. */
51
- export function getOAuthIssuer(config) {
52
- return new URL(config.appOrigin).origin;
52
+ /**
53
+ * The issuer: the app's origin, without a path or a trailing slash. `origins` are the request's
54
+ * (`resolveMcpOrigins`); without them, the configured `appOrigin`.
55
+ */
56
+ export function getOAuthIssuer(config, origins = resolveMcpOrigins(config)) {
57
+ return origins.appOrigin;
53
58
  }
54
59
  /** The URL the endpoint's `401` names in `resource_metadata` (RFC 9728 §5.1). */
55
- export function getProtectedResourceMetadataUrl(config) {
56
- return `${getOAuthIssuer(config)}${PROTECTED_RESOURCE_METADATA_PATH}${new URL(getMcpEndpointUrl(config)).pathname}`;
60
+ export function getProtectedResourceMetadataUrl(config, origins = resolveMcpOrigins(config)) {
61
+ return `${getOAuthIssuer(config, origins)}${PROTECTED_RESOURCE_METADATA_PATH}${new URL(getMcpEndpointUrl(config, origins)).pathname}`;
57
62
  }
58
63
  /** Whether the app turned OAuth on; the OAuth routes answer 404 otherwise. */
59
64
  export function isOAuthEnabled(config) {
60
65
  return getMcpAccessOptions(config).oauth.enabled;
61
66
  }
62
- /** Authorization server metadata (RFC 8414 §2). Tokens are opaque: no `jwks_uri`, no OpenID. */
63
- export function getAuthorizationServerMetadata(config) {
64
- const issuer = getOAuthIssuer(config);
67
+ /** The app's extra keys under the generated ones (`oauth.metadata`). */
68
+ function readExtension(extension, origins) {
69
+ if (extension === undefined)
70
+ return {};
71
+ return typeof extension === "function" ? extension(origins) : extension;
72
+ }
73
+ /**
74
+ * Authorization server metadata (RFC 8414 §2), with the app's `oauth.metadata.authorizationServer`
75
+ * keys added; generated keys win. Tokens are opaque: no OpenID.
76
+ */
77
+ export function getAuthorizationServerMetadata(config, origins = resolveMcpOrigins(config)) {
78
+ const issuer = getOAuthIssuer(config, origins);
65
79
  const routes = getMcpAccessRoutes(config);
66
80
  return {
81
+ ...readExtension(getMcpAccessOptions(config).oauth.metadata.authorizationServer, origins),
67
82
  issuer,
68
83
  authorization_endpoint: `${issuer}${routes.oauthConsent}`,
69
84
  token_endpoint: `${issuer}${routes.oauthToken}`,
@@ -79,35 +94,40 @@ export function getAuthorizationServerMetadata(config) {
79
94
  };
80
95
  }
81
96
  /**
82
- * Protected resource metadata (RFC 9728 §2). At the endpoint's own path the resource is the
83
- * endpoint URL; at the root it is the origin, because RFC 9728 §3.3 ties `resource` to the URL the
84
- * metadata URL was built from.
97
+ * Protected resource metadata (RFC 9728 §2), with the app's `oauth.metadata.protectedResource`
98
+ * keys added; generated keys win, except the app's `resource_name`. At the endpoint's own path the
99
+ * resource is the endpoint URL; at the root it is the origin the document was asked on
100
+ * (`servedOrigin`, one of `resourceOrigins`) or else the app origin, because RFC 9728 §3.3 ties
101
+ * `resource` to the URL the metadata URL was built from.
85
102
  */
86
- export function getProtectedResourceMetadata(config, variant) {
103
+ export function getProtectedResourceMetadata(config, variant, origins = resolveMcpOrigins(config), servedOrigin = null) {
104
+ const issuer = getOAuthIssuer(config, origins);
87
105
  return {
88
- resource: variant === "endpoint" ? getMcpEndpointUrl(config) : getOAuthIssuer(config),
89
- authorization_servers: [getOAuthIssuer(config)],
106
+ resource_name: getMcpAccessOptions(config).serverName,
107
+ ...readExtension(getMcpAccessOptions(config).oauth.metadata.protectedResource, origins),
108
+ resource: variant === "endpoint" ? getMcpEndpointUrl(config, origins) : (servedOrigin ?? issuer),
109
+ authorization_servers: [issuer],
90
110
  scopes_supported: [...OAUTH_SCOPES],
91
111
  bearer_methods_supported: ["header"],
92
- resource_name: getMcpAccessOptions(config).serverName,
93
112
  };
94
113
  }
95
- /** `GET` of a discovery document; 404 while OAuth is off. */
96
- export function serveDiscoveryDocument(config, document) {
114
+ /** `GET` of a discovery document for `request` (its origins); 404 while OAuth is off. */
115
+ export function serveDiscoveryDocument(config, document, request = null) {
97
116
  if (!isOAuthEnabled(config))
98
117
  return Response.json({ error: "not_found" }, { status: 404, headers: CORS_HEADERS });
99
- return Response.json(document(config), { headers: { ...CORS_HEADERS, ...DISCOVERY_CACHE } });
118
+ return Response.json(document(config, resolveMcpOrigins(config, request), request), { headers: { ...CORS_HEADERS, ...DISCOVERY_CACHE } });
100
119
  }
120
+ /** `serveDiscoveryDocument`'s builder for the root protected resource metadata, on the host it was asked on. */
121
+ export const getRootProtectedResourceMetadata = (config, origins, request) => getProtectedResourceMetadata(config, "root", origins, request === null ? null : findServedResourceOrigin(origins, request));
101
122
  /**
102
123
  * Whether `resource` (RFC 8707) names this server: absent (clients from before RFC 8707), the
103
- * endpoint URL or the origin, each with or without a trailing slash.
124
+ * endpoint URL, the app origin or one of `resourceOrigins`, each with or without a trailing slash.
104
125
  */
105
- export function isAcceptableResource(config, resource) {
126
+ export function isAcceptableResource(config, resource, origins = resolveMcpOrigins(config)) {
106
127
  if (resource === null || resource === "")
107
128
  return true;
108
- const endpoint = getMcpEndpointUrl(config).replace(/\/+$/, "");
109
- const origin = getOAuthIssuer(config);
110
- return [endpoint, `${endpoint}/`, origin, `${origin}/`].includes(resource);
129
+ const endpoint = getMcpEndpointUrl(config, origins).replace(/\/+$/, "");
130
+ return [endpoint, getOAuthIssuer(config, origins), ...origins.resourceOrigins].some((accepted) => resource === accepted || resource === `${accepted}/`);
111
131
  }
112
132
  /** Whether the client asks to write. Unknown scopes are ignored (RFC 6749 §3.3); every grant reads. */
113
133
  export function isWriteScopeRequested(scope) {
@@ -222,6 +242,7 @@ const INVALID_GRANT = "The code or refresh token is invalid, expired, used or is
222
242
  export async function handleTokenRequest(ctx, request) {
223
243
  if (!isOAuthEnabled(ctx.config))
224
244
  return protocolError("not_found", "OAuth is not enabled.", 404);
245
+ const origins = resolveMcpOrigins(ctx.config, request);
225
246
  const form = new URLSearchParams(await request.text().catch(() => ""));
226
247
  const credentials = readClientCredentials(request, form);
227
248
  const limited = await limitProtocolRequest(ctx, request, credentials.clientId);
@@ -232,15 +253,15 @@ export async function handleTokenRequest(ctx, request) {
232
253
  if (client === null || !isClientSecretValid(client, credentials.clientSecret === "" ? null : credentials.clientSecret)) {
233
254
  return protocolError("invalid_client", "Unknown client or wrong client credentials.", 401, credentials.isBasic ? { "www-authenticate": 'Basic realm="mcp"' } : {});
234
255
  }
235
- return await answerGrant(ctx, form, client);
256
+ return await answerGrant(ctx, { form, client, origins });
236
257
  }
237
258
  catch (error) {
238
259
  console.error(`@softure-ai/mcp-access: the OAuth token endpoint failed: ${errorLogLabel(error)}`);
239
260
  return protocolError("server_error", "The token could not be issued.", 500);
240
261
  }
241
262
  }
242
- async function answerGrant(ctx, form, client) {
243
- if (!isAcceptableResource(ctx.config, form.get("resource"))) {
263
+ async function answerGrant(ctx, { form, client, origins }) {
264
+ if (!isAcceptableResource(ctx.config, form.get("resource"), origins)) {
244
265
  return protocolError("invalid_target", "The resource parameter does not name this MCP server.");
245
266
  }
246
267
  const grantType = form.get("grant_type");
@@ -267,9 +288,9 @@ async function answerGrant(ctx, form, client) {
267
288
  * The client's redirect URI with parameters added, keeping the query it registered (RFC 6749
268
289
  * §3.1.2), and always `iss` (RFC 9207), so the client knows the answer came from this server.
269
290
  */
270
- export function buildClientRedirect(config, redirectUri, params) {
291
+ export function buildClientRedirect(config, redirectUri, params, origins = resolveMcpOrigins(config)) {
271
292
  const url = new URL(redirectUri);
272
- for (const [key, value] of Object.entries({ ...params, iss: getOAuthIssuer(config) })) {
293
+ for (const [key, value] of Object.entries({ ...params, iss: getOAuthIssuer(config, origins) })) {
273
294
  if (value !== null)
274
295
  url.searchParams.set(key, value);
275
296
  }
@@ -289,9 +310,10 @@ export function readAuthorizationParams(source) {
289
310
  * Validates an authorization request (RFC 6749 §4.1.1), for the consent page and the decision.
290
311
  * Order is a security rule (RFC 6749 §4.1.2.1): until the client and its redirect URI are
291
312
  * confirmed, an error is shown to the person, never redirected, or the server would redirect
292
- * anyone anywhere. Only then do errors go back to the client.
313
+ * anyone anywhere. Only then do errors go back to the client. `origins` are the request's
314
+ * (the consent page's and the decision's must agree); without them, the configured ones.
293
315
  */
294
- export async function validateAuthorizationRequest(ctx, params) {
316
+ export async function validateAuthorizationRequest(ctx, params, origins = resolveMcpOrigins(ctx.config)) {
295
317
  const clientId = params.get("client_id");
296
318
  const client = clientId === null ? null : await findOAuthClient(ctx, clientId);
297
319
  if (client === null)
@@ -303,7 +325,7 @@ export async function validateAuthorizationRequest(ctx, params) {
303
325
  const back = (error, description) => ({
304
326
  kind: "redirect-error",
305
327
  redirectUri,
306
- location: buildClientRedirect(ctx.config, redirectUri, { error, error_description: description, state }),
328
+ location: buildClientRedirect(ctx.config, redirectUri, { error, error_description: description, state }, origins),
307
329
  });
308
330
  if (params.get("response_type") !== "code")
309
331
  return back("unsupported_response_type", "Only response_type=code is supported.");
@@ -311,7 +333,7 @@ export async function validateAuthorizationRequest(ctx, params) {
311
333
  if (codeChallenge === null || !isValidCodeChallenge(codeChallenge) || params.get("code_challenge_method") !== PKCE_METHOD) {
312
334
  return back("invalid_request", "PKCE is required: code_challenge with code_challenge_method S256.");
313
335
  }
314
- if (!isAcceptableResource(ctx.config, params.get("resource")))
336
+ if (!isAcceptableResource(ctx.config, params.get("resource"), origins))
315
337
  return back("invalid_target", "The resource parameter does not name this MCP server.");
316
338
  return { kind: "valid", request: { client, redirectUri, state, codeChallenge, isWriteRequested: isWriteScopeRequested(params.get("scope")) } };
317
339
  }
@@ -322,10 +344,10 @@ export function getConsentPath(config, params) {
322
344
  function seeOther(location) {
323
345
  return new Response(null, { status: 303, headers: { location, "cache-control": "no-store" } });
324
346
  }
325
- /** Whether the form was posted from this app: `Origin` must be the app's origin. */
326
- function isSameOrigin(config, request) {
347
+ /** Whether the form was posted from this app: `Origin` must be the request's app origin. */
348
+ function isSameOrigin(request, origins) {
327
349
  const origin = request.headers.get("origin");
328
- return origin !== null && origin === getOAuthIssuer(config);
350
+ return origin !== null && origin === origins.appOrigin;
329
351
  }
330
352
  /**
331
353
  * `POST` of the consent form, answered with `303`: a plain form post, not a server action, because
@@ -337,7 +359,8 @@ function isSameOrigin(config, request) {
337
359
  export async function handleAuthorizationDecision(ctx, request, userId) {
338
360
  if (!isOAuthEnabled(ctx.config))
339
361
  return new Response(null, { status: 404 });
340
- if (!isSameOrigin(ctx.config, request))
362
+ const origins = resolveMcpOrigins(ctx.config, request);
363
+ if (!isSameOrigin(request, origins))
341
364
  return new Response(null, { status: 403, headers: { "cache-control": "no-store" } });
342
365
  const form = await request.formData().catch(() => null);
343
366
  if (form === null)
@@ -347,14 +370,14 @@ export async function handleAuthorizationDecision(ctx, request, userId) {
347
370
  if (userId === null)
348
371
  return seeOther(consent);
349
372
  try {
350
- const outcome = await validateAuthorizationRequest(ctx, params);
373
+ const outcome = await validateAuthorizationRequest(ctx, params, origins);
351
374
  if (outcome.kind === "show-error")
352
375
  return seeOther(consent);
353
376
  if (outcome.kind === "redirect-error")
354
377
  return seeOther(outcome.location);
355
378
  const { client, redirectUri, state, codeChallenge, isWriteRequested } = outcome.request;
356
379
  if (form.get("decision") !== "allow") {
357
- return seeOther(buildClientRedirect(ctx.config, redirectUri, { error: "access_denied", error_description: "The person denied access.", state }));
380
+ return seeOther(buildClientRedirect(ctx.config, redirectUri, { error: "access_denied", error_description: "The person denied access.", state }, origins));
358
381
  }
359
382
  const code = await createAuthorizationCode(ctx, {
360
383
  clientRowId: client.id,
@@ -364,7 +387,7 @@ export async function handleAuthorizationDecision(ctx, request, userId) {
364
387
  // Writes need the client's request, the app's allowWrites and the person's tick.
365
388
  canWrite: isWriteRequested && getMcpAccessOptions(ctx.config).allowWrites && form.get("canWrite") !== null,
366
389
  });
367
- return seeOther(buildClientRedirect(ctx.config, redirectUri, { code, state }));
390
+ return seeOther(buildClientRedirect(ctx.config, redirectUri, { code, state }, origins));
368
391
  }
369
392
  catch (error) {
370
393
  console.error(`@softure-ai/mcp-access: recording an OAuth decision failed: ${errorLogLabel(error)}`);