@kya-os/mcp-i 0.1.0 → 1.2.1

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 (229) hide show
  1. package/README.md +406 -71
  2. package/dist/149.js +1 -0
  3. package/dist/189.js +1 -0
  4. package/dist/261.js +1 -0
  5. package/dist/28.js +1 -0
  6. package/dist/295.js +1 -0
  7. package/dist/460.js +1 -0
  8. package/dist/570.js +1 -0
  9. package/dist/634.js +1 -0
  10. package/dist/647.js +1 -0
  11. package/dist/67.js +1 -0
  12. package/dist/739.js +1 -0
  13. package/dist/742.js +1 -0
  14. package/dist/904.js +1 -0
  15. package/dist/938.js +1 -0
  16. package/dist/auth/api-key.d.ts +16 -0
  17. package/dist/auth/api-key.js +82 -0
  18. package/dist/auth/jwt.d.ts +43 -0
  19. package/dist/auth/jwt.js +51 -0
  20. package/dist/auth/oauth/factory.d.ts +12 -0
  21. package/dist/auth/oauth/factory.js +36 -0
  22. package/dist/auth/oauth/index.d.ts +5 -0
  23. package/dist/auth/oauth/index.js +27 -0
  24. package/dist/auth/oauth/providers/proxy-provider.d.ts +13 -0
  25. package/dist/auth/oauth/providers/proxy-provider.js +159 -0
  26. package/dist/auth/oauth/router.d.ts +4 -0
  27. package/dist/auth/oauth/router.js +294 -0
  28. package/dist/auth/oauth/storage/memory-storage.d.ts +12 -0
  29. package/dist/auth/oauth/storage/memory-storage.js +40 -0
  30. package/dist/auth/oauth/types.d.ts +112 -0
  31. package/dist/auth/oauth/types.js +2 -0
  32. package/dist/cache/__tests__/cloudflare-kv-nonce-cache.test.d.ts +4 -0
  33. package/dist/cache/__tests__/cloudflare-kv-nonce-cache.test.js +176 -0
  34. package/dist/cache/__tests__/concurrency.test.d.ts +5 -0
  35. package/dist/cache/__tests__/concurrency.test.js +300 -0
  36. package/dist/cache/__tests__/dynamodb-nonce-cache.test.d.ts +4 -0
  37. package/dist/cache/__tests__/dynamodb-nonce-cache.test.js +176 -0
  38. package/dist/cache/__tests__/memory-nonce-cache.test.d.ts +4 -0
  39. package/dist/cache/__tests__/memory-nonce-cache.test.js +132 -0
  40. package/dist/cache/__tests__/nonce-cache-factory-simple.test.d.ts +4 -0
  41. package/dist/cache/__tests__/nonce-cache-factory-simple.test.js +133 -0
  42. package/dist/cache/__tests__/nonce-cache-factory.test.d.ts +4 -0
  43. package/dist/cache/__tests__/nonce-cache-factory.test.js +252 -0
  44. package/dist/cache/__tests__/redis-nonce-cache.test.d.ts +4 -0
  45. package/dist/cache/__tests__/redis-nonce-cache.test.js +95 -0
  46. package/dist/cache/cloudflare-kv-nonce-cache.d.ts +14 -0
  47. package/dist/cache/cloudflare-kv-nonce-cache.js +93 -0
  48. package/dist/cache/dynamodb-nonce-cache.d.ts +15 -0
  49. package/dist/cache/dynamodb-nonce-cache.js +92 -0
  50. package/dist/cache/index.d.ts +16 -0
  51. package/dist/cache/index.js +32 -0
  52. package/dist/cache/memory-nonce-cache.d.ts +44 -0
  53. package/dist/cache/memory-nonce-cache.js +105 -0
  54. package/dist/cache/nonce-cache-factory.d.ts +20 -0
  55. package/dist/cache/nonce-cache-factory.js +208 -0
  56. package/dist/cache/redis-nonce-cache.d.ts +14 -0
  57. package/dist/cache/redis-nonce-cache.js +53 -0
  58. package/dist/compiler/compiler-context.d.ts +23 -0
  59. package/dist/compiler/compiler-context.js +24 -0
  60. package/dist/compiler/config/constants.d.ts +41 -0
  61. package/dist/compiler/config/constants.js +45 -0
  62. package/dist/compiler/config/index.d.ts +252 -0
  63. package/dist/compiler/config/index.js +15 -0
  64. package/dist/compiler/config/injection.d.ts +26 -0
  65. package/dist/compiler/config/injection.js +58 -0
  66. package/dist/compiler/config/schemas/experimental/index.d.ts +91 -0
  67. package/dist/compiler/config/schemas/experimental/index.js +16 -0
  68. package/dist/compiler/config/schemas/experimental/oauth.d.ts +74 -0
  69. package/dist/compiler/config/schemas/experimental/oauth.js +25 -0
  70. package/dist/compiler/config/schemas/index.d.ts +6 -0
  71. package/dist/compiler/config/schemas/index.js +17 -0
  72. package/dist/compiler/config/schemas/paths.d.ts +9 -0
  73. package/dist/compiler/config/schemas/paths.js +12 -0
  74. package/dist/compiler/config/schemas/transport/http.d.ts +82 -0
  75. package/dist/compiler/config/schemas/transport/http.js +33 -0
  76. package/dist/compiler/config/schemas/transport/stdio.d.ts +9 -0
  77. package/dist/compiler/config/schemas/transport/stdio.js +15 -0
  78. package/dist/compiler/config/schemas/webpack.d.ts +3 -0
  79. package/dist/compiler/config/schemas/webpack.js +15 -0
  80. package/dist/compiler/config/types.d.ts +1 -0
  81. package/dist/compiler/config/types.js +2 -0
  82. package/dist/compiler/config/utils.d.ts +20 -0
  83. package/dist/compiler/config/utils.js +36 -0
  84. package/dist/compiler/generate-env-code.d.ts +1 -0
  85. package/dist/compiler/generate-env-code.js +8 -0
  86. package/dist/compiler/generate-import-code.d.ts +1 -0
  87. package/dist/compiler/generate-import-code.js +24 -0
  88. package/dist/compiler/get-webpack-config/get-entries.d.ts +3 -0
  89. package/dist/compiler/get-webpack-config/get-entries.js +29 -0
  90. package/dist/compiler/get-webpack-config/get-externals.d.ts +7 -0
  91. package/dist/compiler/get-webpack-config/get-externals.js +88 -0
  92. package/dist/compiler/get-webpack-config/get-injected-variables.d.ts +8 -0
  93. package/dist/compiler/get-webpack-config/get-injected-variables.js +25 -0
  94. package/dist/compiler/get-webpack-config/index.d.ts +4 -0
  95. package/dist/compiler/get-webpack-config/index.js +101 -0
  96. package/dist/compiler/get-webpack-config/plugins.d.ts +8 -0
  97. package/dist/compiler/get-webpack-config/plugins.js +132 -0
  98. package/dist/compiler/get-webpack-config/resolve-tsconfig-paths.d.ts +9 -0
  99. package/dist/compiler/get-webpack-config/resolve-tsconfig-paths.js +40 -0
  100. package/dist/compiler/index.d.ts +6 -0
  101. package/dist/compiler/index.js +194 -0
  102. package/dist/compiler/on-first-build.d.ts +3 -0
  103. package/dist/compiler/on-first-build.js +58 -0
  104. package/dist/compiler/parse-xmcp-config.d.ts +9 -0
  105. package/dist/compiler/parse-xmcp-config.js +155 -0
  106. package/dist/compiler/start-http-server.d.ts +1 -0
  107. package/dist/compiler/start-http-server.js +34 -0
  108. package/dist/index.d.ts +12 -54
  109. package/dist/index.js +22 -190
  110. package/dist/index.js.LICENSE.txt +49 -0
  111. package/dist/runtime/__tests__/audit.test.d.ts +4 -0
  112. package/dist/runtime/__tests__/audit.test.js +328 -0
  113. package/dist/runtime/__tests__/identity.test.d.ts +4 -0
  114. package/dist/runtime/__tests__/identity.test.js +164 -0
  115. package/dist/runtime/__tests__/mcpi-runtime.test.d.ts +4 -0
  116. package/dist/runtime/__tests__/mcpi-runtime.test.js +372 -0
  117. package/dist/runtime/__tests__/proof.test.d.ts +4 -0
  118. package/dist/runtime/__tests__/proof.test.js +302 -0
  119. package/dist/runtime/__tests__/session.test.d.ts +4 -0
  120. package/dist/runtime/__tests__/session.test.js +254 -0
  121. package/dist/runtime/__tests__/well-known.test.d.ts +4 -0
  122. package/dist/runtime/__tests__/well-known.test.js +312 -0
  123. package/dist/runtime/adapter-express.js +2 -0
  124. package/dist/runtime/adapter-express.js.LICENSE.txt +252 -0
  125. package/dist/runtime/adapter-nextjs.js +2 -0
  126. package/dist/runtime/adapter-nextjs.js.LICENSE.txt +53 -0
  127. package/dist/runtime/adapters/express/index.d.ts +2 -0
  128. package/dist/runtime/adapters/express/index.js +48 -0
  129. package/dist/runtime/adapters/nextjs/index.d.ts +8 -0
  130. package/dist/runtime/adapters/nextjs/index.js +18 -0
  131. package/dist/runtime/audit.d.ts +93 -0
  132. package/dist/runtime/audit.js +212 -0
  133. package/dist/runtime/debug.d.ts +118 -0
  134. package/dist/runtime/debug.js +612 -0
  135. package/dist/runtime/delegation-hooks.d.ts +85 -0
  136. package/dist/runtime/delegation-hooks.js +116 -0
  137. package/dist/runtime/demo.d.ts +71 -0
  138. package/dist/runtime/demo.js +135 -0
  139. package/dist/runtime/headers.d.ts +1 -0
  140. package/dist/runtime/headers.js +9 -0
  141. package/dist/runtime/http.js +2 -0
  142. package/dist/runtime/http.js.LICENSE.txt +252 -0
  143. package/dist/runtime/identity.d.ts +105 -0
  144. package/dist/runtime/identity.js +232 -0
  145. package/dist/runtime/index.d.ts +16 -0
  146. package/dist/runtime/index.js +56 -0
  147. package/dist/runtime/mcpi-runtime.d.ts +164 -0
  148. package/dist/runtime/mcpi-runtime.js +352 -0
  149. package/dist/runtime/proof.d.ts +87 -0
  150. package/dist/runtime/proof.js +223 -0
  151. package/dist/runtime/session.d.ts +88 -0
  152. package/dist/runtime/session.js +216 -0
  153. package/dist/runtime/stdio.js +2 -0
  154. package/dist/runtime/stdio.js.LICENSE.txt +1 -0
  155. package/dist/runtime/templates/home.d.ts +2 -0
  156. package/dist/runtime/templates/home.js +50 -0
  157. package/dist/runtime/transports/http/base-streamable-http.d.ts +25 -0
  158. package/dist/runtime/transports/http/base-streamable-http.js +16 -0
  159. package/dist/runtime/transports/http/http-context.d.ts +9 -0
  160. package/dist/runtime/transports/http/http-context.js +8 -0
  161. package/dist/runtime/transports/http/index.d.ts +1 -0
  162. package/dist/runtime/transports/http/index.js +55 -0
  163. package/dist/runtime/transports/http/setup-cors.d.ts +4 -0
  164. package/dist/runtime/transports/http/setup-cors.js +24 -0
  165. package/dist/runtime/transports/http/stateless-streamable-http.d.ts +39 -0
  166. package/dist/runtime/transports/http/stateless-streamable-http.js +331 -0
  167. package/dist/runtime/transports/stdio/index.d.ts +1 -0
  168. package/dist/runtime/transports/stdio/index.js +51 -0
  169. package/dist/runtime/utils/server.d.ts +42 -0
  170. package/dist/runtime/utils/server.js +39 -0
  171. package/dist/runtime/utils/tools.d.ts +8 -0
  172. package/dist/runtime/utils/tools.js +115 -0
  173. package/dist/runtime/verifier-middleware.d.ts +76 -0
  174. package/dist/runtime/verifier-middleware.js +322 -0
  175. package/dist/runtime/well-known.d.ts +151 -0
  176. package/dist/runtime/well-known.js +258 -0
  177. package/dist/storage/config.d.ts +28 -0
  178. package/dist/storage/config.js +79 -0
  179. package/dist/storage/delegation.d.ts +59 -0
  180. package/dist/storage/delegation.js +130 -0
  181. package/dist/storage/merkle-verifier.d.ts +84 -0
  182. package/dist/storage/merkle-verifier.js +261 -0
  183. package/dist/test/__tests__/nonce-cache-integration.test.d.ts +1 -0
  184. package/dist/test/__tests__/nonce-cache-integration.test.js +116 -0
  185. package/dist/test/__tests__/nonce-cache.test.d.ts +1 -0
  186. package/dist/test/__tests__/nonce-cache.test.js +122 -0
  187. package/dist/test/__tests__/runtime-integration.test.d.ts +4 -0
  188. package/dist/test/__tests__/runtime-integration.test.js +192 -0
  189. package/dist/test/__tests__/test-infrastructure.test.d.ts +4 -0
  190. package/dist/test/__tests__/test-infrastructure.test.js +178 -0
  191. package/dist/test/deterministic-keys.d.ts +31 -0
  192. package/dist/test/deterministic-keys.js +108 -0
  193. package/dist/test/examples/test-usage-example.d.ts +140 -0
  194. package/dist/test/examples/test-usage-example.js +175 -0
  195. package/dist/test/index.d.ts +11 -0
  196. package/dist/test/index.js +27 -0
  197. package/dist/test/local-verification.d.ts +28 -0
  198. package/dist/test/local-verification.js +342 -0
  199. package/dist/test/mock-identity-provider.d.ts +96 -0
  200. package/dist/test/mock-identity-provider.js +243 -0
  201. package/dist/test/runtime-integration.d.ts +63 -0
  202. package/dist/test/runtime-integration.js +140 -0
  203. package/dist/test/test-environment.d.ts +26 -0
  204. package/dist/test/test-environment.js +50 -0
  205. package/dist/types/declarations.d.ts +1 -0
  206. package/dist/types/declarations.js +6 -0
  207. package/dist/types/middleware.d.ts +2 -0
  208. package/dist/types/middleware.js +2 -0
  209. package/dist/types/tool.d.ts +80 -0
  210. package/dist/types/tool.js +2 -0
  211. package/dist/utils/cli-icons.d.ts +3 -0
  212. package/dist/utils/cli-icons.js +7 -0
  213. package/dist/utils/constants.d.ts +6 -0
  214. package/dist/utils/constants.js +13 -0
  215. package/dist/utils/context.d.ts +33 -0
  216. package/dist/utils/context.js +58 -0
  217. package/dist/utils/file-watcher.d.ts +19 -0
  218. package/dist/utils/file-watcher.js +49 -0
  219. package/dist/utils/fs-utils.d.ts +2 -0
  220. package/dist/utils/fs-utils.js +22 -0
  221. package/dist/utils/path-validation.d.ts +3 -0
  222. package/dist/utils/path-validation.js +56 -0
  223. package/dist/utils/spawn-process.d.ts +9 -0
  224. package/dist/utils/spawn-process.js +50 -0
  225. package/dist/utils/subscribable.d.ts +12 -0
  226. package/dist/utils/subscribable.js +44 -0
  227. package/package.json +99 -21
  228. package/dist/index.d.ts.map +0 -1
  229. package/dist/index.js.map +0 -1
@@ -0,0 +1,159 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ProxyOAuthServerProvider = void 0;
4
+ const memory_storage_1 = require("../storage/memory-storage");
5
+ class ProxyOAuthServerProvider {
6
+ config;
7
+ storage;
8
+ constructor(config) {
9
+ this.config = config;
10
+ // fallback to memory storage
11
+ // ideally this would be used in a development environment, could be set with a flag
12
+ // since we are providing storage on Redis (to do) we may want to fallback to this in dev
13
+ // does not scale to prod tho, so we should be validating that we prevent that from happening
14
+ this.storage = config.storage || new memory_storage_1.MemoryOAuthStorage();
15
+ }
16
+ // Expose config for router access
17
+ get endpoints() {
18
+ return this.config.endpoints;
19
+ }
20
+ async verifyAccessToken(token) {
21
+ if (this.config.verifyAccessToken) {
22
+ return await this.config.verifyAccessToken(token);
23
+ }
24
+ // Fallback to storage lookup or external verification
25
+ const storedToken = await this.storage.tokens.getToken(token);
26
+ if (storedToken) {
27
+ return storedToken;
28
+ }
29
+ // If not found in storage, verify with external provider
30
+ return await this.verifyTokenWithProvider(token);
31
+ }
32
+ async authorize(params) {
33
+ const { client_id, redirect_uri, response_type, scope, state } = params;
34
+ // Let Auth0 handle all client validation - just build the authorization URL
35
+ const authUrl = new URL(this.config.endpoints.authorizationUrl);
36
+ authUrl.searchParams.set("response_type", response_type);
37
+ authUrl.searchParams.set("client_id", client_id);
38
+ authUrl.searchParams.set("redirect_uri", redirect_uri);
39
+ if (scope) {
40
+ authUrl.searchParams.set("scope", scope);
41
+ }
42
+ else if (this.config.defaultScopes) {
43
+ authUrl.searchParams.set("scope", this.config.defaultScopes.join(" "));
44
+ }
45
+ if (state) {
46
+ authUrl.searchParams.set("state", state);
47
+ }
48
+ return authUrl.toString();
49
+ }
50
+ async token(params) {
51
+ const { grant_type, client_id, client_secret, code, redirect_uri, refresh_token, } = params;
52
+ try {
53
+ // Forward token request to external provider (let Auth0 handle client validation)
54
+ const response = await fetch(this.config.endpoints.tokenUrl, {
55
+ method: "POST",
56
+ headers: {
57
+ "Content-Type": "application/x-www-form-urlencoded",
58
+ Accept: "application/json",
59
+ },
60
+ body: new URLSearchParams({
61
+ grant_type,
62
+ client_id,
63
+ ...(client_secret && { client_secret }),
64
+ ...(code && { code }),
65
+ ...(redirect_uri && { redirect_uri }),
66
+ ...(refresh_token && { refresh_token }),
67
+ }),
68
+ });
69
+ const tokenData = await response.json();
70
+ if (!response.ok) {
71
+ throw this.createOAuthError(tokenData.error || "server_error", tokenData.error_description || "Token exchange failed");
72
+ }
73
+ // Store token in our storage if we have access_token
74
+ if (tokenData.access_token) {
75
+ const accessToken = {
76
+ token: tokenData.access_token,
77
+ clientId: client_id,
78
+ scopes: tokenData.scope ? tokenData.scope.split(" ") : [],
79
+ expiresAt: tokenData.expires_in
80
+ ? new Date(Date.now() + tokenData.expires_in * 1000)
81
+ : undefined,
82
+ refreshToken: tokenData.refresh_token,
83
+ };
84
+ await this.storage.tokens.saveToken(accessToken);
85
+ }
86
+ return tokenData;
87
+ }
88
+ catch (error) {
89
+ if (error instanceof Error && error.message.includes("invalid_")) {
90
+ throw error;
91
+ }
92
+ throw this.createOAuthError("server_error", "Failed to exchange token");
93
+ }
94
+ }
95
+ async revoke(params) {
96
+ const { token, token_type_hint, client_id, client_secret } = params;
97
+ // Remove from our storage first
98
+ await this.storage.tokens.deleteToken(token);
99
+ // If external provider supports revocation, forward the request
100
+ if (this.config.endpoints.revocationUrl) {
101
+ try {
102
+ const response = await fetch(this.config.endpoints.revocationUrl, {
103
+ method: "POST",
104
+ headers: {
105
+ "Content-Type": "application/x-www-form-urlencoded",
106
+ Accept: "application/json",
107
+ },
108
+ body: new URLSearchParams({
109
+ token,
110
+ ...(token_type_hint && { token_type_hint }),
111
+ ...(client_id && { client_id }),
112
+ ...(client_secret && { client_secret }),
113
+ }),
114
+ });
115
+ if (!response.ok) {
116
+ console.warn("Failed to revoke token with external provider:", response.statusText);
117
+ }
118
+ }
119
+ catch (error) {
120
+ console.warn("Error revoking token with external provider:", error);
121
+ }
122
+ }
123
+ }
124
+ async verifyTokenWithProvider(token) {
125
+ // If external provider has a userinfo endpoint, we can use it to verify the token
126
+ if (this.config.endpoints.userInfoUrl) {
127
+ try {
128
+ const response = await fetch(this.config.endpoints.userInfoUrl, {
129
+ headers: {
130
+ Authorization: `Bearer ${token}`,
131
+ Accept: "application/json",
132
+ },
133
+ });
134
+ if (response.ok) {
135
+ // Token is valid, create a basic AccessToken object
136
+ return {
137
+ token,
138
+ clientId: "external", // We might not know the exact client ID
139
+ scopes: [], // We might not know the exact scopes
140
+ };
141
+ }
142
+ }
143
+ catch (error) {
144
+ console.warn("Error verifying token with external provider:", error);
145
+ }
146
+ }
147
+ throw this.createOAuthError("invalid_token", "Token verification failed");
148
+ }
149
+ createOAuthError(error, description) {
150
+ const oauthError = {
151
+ error,
152
+ error_description: description,
153
+ };
154
+ const errorObj = new Error(description || error);
155
+ errorObj.oauth = oauthError;
156
+ return errorObj;
157
+ }
158
+ }
159
+ exports.ProxyOAuthServerProvider = ProxyOAuthServerProvider;
@@ -0,0 +1,4 @@
1
+ import { Router, Request, Response, NextFunction } from "express";
2
+ import { OAuthRouterConfig } from "./types";
3
+ export declare function createOAuthRouter(config: OAuthRouterConfig): Router;
4
+ export declare function createOAuthMiddleware(provider: any): (req: Request, res: Response, next: NextFunction) => Promise<void>;
@@ -0,0 +1,294 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createOAuthRouter = createOAuthRouter;
4
+ exports.createOAuthMiddleware = createOAuthMiddleware;
5
+ const express_1 = require("express");
6
+ function createOAuthRouter(config) {
7
+ const router = (0, express_1.Router)();
8
+ const { provider, issuerUrl, baseUrl, serviceDocumentationUrl, pathPrefix = "/oauth2", } = config;
9
+ router.use((req, res, next) => {
10
+ // to do check: cors config from ts file is overriding and failing to add headers
11
+ res.setHeader("Access-Control-Allow-Origin", "*");
12
+ res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
13
+ res.setHeader("Access-Control-Allow-Headers",
14
+ // should also pass the mcp-protocol-version header
15
+ // probably only that one since the rest can be set from the config side
16
+ "Content-Type, Authorization, Accept, mcp-protocol-version");
17
+ res.setHeader("Access-Control-Expose-Headers", "Content-Type");
18
+ if (req.method === "OPTIONS") {
19
+ res.sendStatus(200);
20
+ return;
21
+ }
22
+ next();
23
+ });
24
+ // OAuth 2.0 Protected Resource Metadata (RFC 9728)
25
+ router.get("/.well-known/oauth-protected-resource", async (req, res) => {
26
+ try {
27
+ const baseUrlStr = baseUrl.toString().replace(/\/$/, ""); // Remove trailing slash - formatting issues lol
28
+ // thinking of maybe removing the path prefix?
29
+ const metadata = {
30
+ resource: baseUrlStr,
31
+ authorization_servers: [issuerUrl.toString()],
32
+ bearer_methods_supported: ["header", "body"],
33
+ resource_documentation: serviceDocumentationUrl?.toString(),
34
+ introspection_endpoint: `${baseUrlStr}${pathPrefix}/introspect`,
35
+ revocation_endpoint: `${baseUrlStr}${pathPrefix}/revoke`,
36
+ };
37
+ res.json(metadata);
38
+ }
39
+ catch (error) {
40
+ console.error("Error in protected resource metadata endpoint:", error);
41
+ res.status(500).json({
42
+ error: "server_error",
43
+ error_description: "Internal server error",
44
+ });
45
+ }
46
+ });
47
+ // OAuth 2.0 Discovery endpoint - authorization server metadata RFC 8414
48
+ // maybe serve all the config as customizable? for example for scopes etc
49
+ router.get("/.well-known/oauth-authorization-server", async (req, res) => {
50
+ try {
51
+ const discovery = {
52
+ issuer: issuerUrl.toString(),
53
+ authorization_endpoint: provider.endpoints.authorizationUrl,
54
+ token_endpoint: provider.endpoints.tokenUrl,
55
+ revocation_endpoint: provider.endpoints.revocationUrl,
56
+ response_types_supported: ["code"],
57
+ grant_types_supported: ["authorization_code", "refresh_token"],
58
+ token_endpoint_auth_methods_supported: [
59
+ "client_secret_post",
60
+ "client_secret_basic",
61
+ ],
62
+ scopes_supported: ["openid", "profile", "email"],
63
+ // PKCE support (RFC 7636) - S256 mandatory for security
64
+ code_challenge_methods_supported: ["S256"],
65
+ // DCR is mandatory - all clients must register
66
+ // this is what MCP recommends doing to handle the entire OAuth flow
67
+ // cause we're not supporting manually setting up the client
68
+ registration_endpoint: `${baseUrl.toString().replace(/\/$/, "")}${pathPrefix}/register`,
69
+ ...(serviceDocumentationUrl && {
70
+ service_documentation: serviceDocumentationUrl.toString(),
71
+ }),
72
+ };
73
+ res.json(discovery);
74
+ }
75
+ catch (error) {
76
+ console.error("Error in discovery endpoint:", error);
77
+ res.status(500).json({
78
+ error: "server_error",
79
+ error_description: "Internal server error",
80
+ });
81
+ }
82
+ });
83
+ // following endpoints we're redirecting to the external authorization server
84
+ router.get(`${pathPrefix}/authorize`, async (req, res) => {
85
+ try {
86
+ const params = {
87
+ response_type: req.query.response_type,
88
+ client_id: req.query.client_id,
89
+ redirect_uri: req.query.redirect_uri,
90
+ scope: req.query.scope,
91
+ state: req.query.state,
92
+ // PKCE parameters (RFC 7636)
93
+ code_challenge: req.query.code_challenge,
94
+ code_challenge_method: req.query.code_challenge_method,
95
+ };
96
+ if (!params.response_type || !params.client_id || !params.redirect_uri) {
97
+ res.status(400).json({
98
+ error: "invalid_request",
99
+ error_description: "Missing required parameters",
100
+ });
101
+ return;
102
+ }
103
+ // PKCE params validation
104
+ if (!params.code_challenge || !params.code_challenge_method) {
105
+ res.status(400).json({
106
+ error: "invalid_request",
107
+ error_description: "PKCE parameters (code_challenge, code_challenge_method) are required",
108
+ });
109
+ return;
110
+ }
111
+ // only allow S256 method
112
+ if (params.code_challenge_method !== "S256") {
113
+ res.status(400).json({
114
+ error: "invalid_request",
115
+ error_description: "Only S256 code challenge method is supported",
116
+ });
117
+ return;
118
+ }
119
+ // let the provider handle the authorization
120
+ const authUrl = await provider.authorize(params);
121
+ res.redirect(authUrl);
122
+ }
123
+ catch (error) {
124
+ console.error("Error in authorize endpoint:", error);
125
+ const oauthError = extractOAuthError(error);
126
+ res.status(400).json(oauthError);
127
+ }
128
+ });
129
+ router.post(`${pathPrefix}/token`, async (req, res) => {
130
+ try {
131
+ const params = {
132
+ grant_type: req.body.grant_type,
133
+ client_id: req.body.client_id,
134
+ client_secret: req.body.client_secret,
135
+ code: req.body.code,
136
+ redirect_uri: req.body.redirect_uri,
137
+ refresh_token: req.body.refresh_token,
138
+ // PKCE parameter (RFC 7636)
139
+ code_verifier: req.body.code_verifier,
140
+ };
141
+ if (!params.grant_type || !params.client_id) {
142
+ res.status(400).json({
143
+ error: "invalid_request",
144
+ error_description: "Missing required parameters",
145
+ });
146
+ return;
147
+ }
148
+ // PKCE is mandatory for authorization_code grant
149
+ if (params.grant_type === "authorization_code" && !params.code_verifier) {
150
+ res.status(400).json({
151
+ error: "invalid_request",
152
+ error_description: "code_verifier is required for authorization_code grant (PKCE)",
153
+ });
154
+ return;
155
+ }
156
+ const tokenResponse = await provider.token(params);
157
+ res.json(tokenResponse);
158
+ }
159
+ catch (error) {
160
+ console.error("Error in token endpoint:", error);
161
+ const oauthError = extractOAuthError(error);
162
+ res.status(400).json(oauthError);
163
+ }
164
+ });
165
+ router.post(`${pathPrefix}/revoke`, async (req, res) => {
166
+ try {
167
+ const params = {
168
+ token: req.body.token,
169
+ token_type_hint: req.body.token_type_hint,
170
+ client_id: req.body.client_id,
171
+ client_secret: req.body.client_secret,
172
+ };
173
+ if (!params.token) {
174
+ res.status(400).json({
175
+ error: "invalid_request",
176
+ error_description: "Missing token parameter",
177
+ });
178
+ return;
179
+ }
180
+ await provider.revoke(params);
181
+ res.status(200).end();
182
+ }
183
+ catch (error) {
184
+ console.error("Error in revoke endpoint:", error);
185
+ const oauthError = extractOAuthError(error);
186
+ res.status(400).json(oauthError);
187
+ }
188
+ });
189
+ router.post(`${pathPrefix}/introspect`, async (req, res) => {
190
+ try {
191
+ const token = req.body.token;
192
+ if (!token) {
193
+ res.status(400).json({
194
+ error: "invalid_request",
195
+ error_description: "Missing token parameter",
196
+ });
197
+ return;
198
+ }
199
+ const accessToken = await provider.verifyAccessToken(token);
200
+ res.json({
201
+ active: true,
202
+ client_id: accessToken.clientId,
203
+ scope: accessToken.scopes.join(" "),
204
+ exp: accessToken.expiresAt
205
+ ? Math.floor(accessToken.expiresAt.getTime() / 1000)
206
+ : undefined,
207
+ });
208
+ }
209
+ catch (error) {
210
+ console.error("Error in introspect endpoint:", error);
211
+ // return active: false for invalid tokens
212
+ res.json({ active: false });
213
+ }
214
+ });
215
+ router.all(`${pathPrefix}/register`, async (req, res) => {
216
+ try {
217
+ if (req.method === "GET") {
218
+ // redirect to the external provider's registration page
219
+ res.redirect(provider.endpoints.registerUrl);
220
+ return;
221
+ }
222
+ // proxy to the external provider's registration page
223
+ const response = await fetch(provider.endpoints.registerUrl, {
224
+ method: req.method,
225
+ headers: {
226
+ "Content-Type": "application/json",
227
+ Accept: "application/json",
228
+ ...(req.headers["user-agent"] && {
229
+ "User-Agent": req.headers["user-agent"],
230
+ }),
231
+ },
232
+ body: JSON.stringify(req.body),
233
+ });
234
+ const registrationData = await response.json();
235
+ res.status(response.status).json(registrationData);
236
+ }
237
+ catch (error) {
238
+ console.error("Error in registration endpoint:", error);
239
+ res.status(500).json({
240
+ error: "server_error",
241
+ error_description: "Failed to register client",
242
+ });
243
+ }
244
+ });
245
+ return router;
246
+ }
247
+ // create middleware for protecting routes
248
+ function createOAuthMiddleware(provider) {
249
+ return async (req, res, next) => {
250
+ try {
251
+ const authHeader = req.header("Authorization");
252
+ if (!authHeader || !authHeader.startsWith("Bearer ")) {
253
+ res.status(401).json({
254
+ error: "invalid_token",
255
+ error_description: "Missing or malformed Authorization header",
256
+ });
257
+ return;
258
+ }
259
+ const token = authHeader.slice("Bearer ".length).trim();
260
+ if (!token) {
261
+ res.status(401).json({
262
+ error: "invalid_token",
263
+ error_description: "Missing access token",
264
+ });
265
+ return;
266
+ }
267
+ const accessToken = await provider.verifyAccessToken(token);
268
+ req.oauth = {
269
+ token: accessToken.token,
270
+ clientId: accessToken.clientId,
271
+ scopes: accessToken.scopes,
272
+ expiresAt: accessToken.expiresAt,
273
+ };
274
+ next();
275
+ }
276
+ catch (error) {
277
+ console.error("Error in OAuth middleware:", error);
278
+ res.status(401).json({
279
+ error: "invalid_token",
280
+ error_description: "Invalid or expired token",
281
+ });
282
+ }
283
+ };
284
+ }
285
+ // helper function to extract OAuth errors pretty self explanatory
286
+ function extractOAuthError(error) {
287
+ if (error && error.oauth) {
288
+ return error.oauth;
289
+ }
290
+ return {
291
+ error: "server_error",
292
+ error_description: error?.message || "Internal server error",
293
+ };
294
+ }
@@ -0,0 +1,12 @@
1
+ import { OAuthStorage, TokenStorage, AccessToken } from "../types";
2
+ export declare class MemoryTokenStorage implements TokenStorage {
3
+ tokens: Map<string, AccessToken>;
4
+ getToken(token: string): Promise<AccessToken | null>;
5
+ saveToken(token: AccessToken): Promise<void>;
6
+ deleteToken(token: string): Promise<void>;
7
+ deleteTokensByClient(clientId: string): Promise<void>;
8
+ }
9
+ export declare class MemoryOAuthStorage implements OAuthStorage {
10
+ tokens: TokenStorage;
11
+ constructor();
12
+ }
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MemoryOAuthStorage = exports.MemoryTokenStorage = void 0;
4
+ // Module-level storage map that persists across instances (SINGLETON PATTERN)
5
+ const tokensMap = new Map();
6
+ class MemoryTokenStorage {
7
+ // same, module level map
8
+ tokens = tokensMap;
9
+ async getToken(token) {
10
+ const accessToken = tokensMap.get(token);
11
+ if (accessToken &&
12
+ accessToken.expiresAt &&
13
+ accessToken.expiresAt < new Date()) {
14
+ tokensMap.delete(token);
15
+ return null;
16
+ }
17
+ return accessToken || null;
18
+ }
19
+ async saveToken(token) {
20
+ tokensMap.set(token.token, token);
21
+ }
22
+ async deleteToken(token) {
23
+ tokensMap.delete(token);
24
+ }
25
+ async deleteTokensByClient(clientId) {
26
+ for (const [tokenValue, tokenData] of tokensMap.entries()) {
27
+ if (tokenData.clientId === clientId) {
28
+ tokensMap.delete(tokenValue);
29
+ }
30
+ }
31
+ }
32
+ }
33
+ exports.MemoryTokenStorage = MemoryTokenStorage;
34
+ class MemoryOAuthStorage {
35
+ tokens;
36
+ constructor() {
37
+ this.tokens = new MemoryTokenStorage();
38
+ }
39
+ }
40
+ exports.MemoryOAuthStorage = MemoryOAuthStorage;
@@ -0,0 +1,112 @@
1
+ export interface OAuthClient {
2
+ client_id: string;
3
+ client_secret?: string;
4
+ redirect_uris: string[];
5
+ grant_types?: string[];
6
+ response_types?: string[];
7
+ scopes?: string[];
8
+ }
9
+ export interface AccessToken {
10
+ token: string;
11
+ clientId: string;
12
+ scopes: string[];
13
+ expiresAt?: Date;
14
+ refreshToken?: string;
15
+ }
16
+ export interface OAuthEndpoints {
17
+ authorizationUrl: string;
18
+ tokenUrl: string;
19
+ revocationUrl?: string;
20
+ userInfoUrl?: string;
21
+ registerUrl: string;
22
+ }
23
+ export interface OAuthError {
24
+ error: string;
25
+ error_description?: string;
26
+ error_uri?: string;
27
+ }
28
+ export interface TokenResponse {
29
+ access_token: string;
30
+ token_type: string;
31
+ expires_in?: number;
32
+ refresh_token?: string;
33
+ scope?: string;
34
+ }
35
+ export interface OAuthStorage {
36
+ tokens: TokenStorage;
37
+ }
38
+ export interface TokenStorage {
39
+ getToken(token: string): Promise<AccessToken | null>;
40
+ saveToken(token: AccessToken): Promise<void>;
41
+ deleteToken(token: string): Promise<void>;
42
+ deleteTokensByClient(clientId: string): Promise<void>;
43
+ }
44
+ export interface ProxyOAuthProviderConfig {
45
+ endpoints: OAuthEndpoints;
46
+ storage?: OAuthStorage;
47
+ verifyAccessToken?: (token: string) => Promise<AccessToken>;
48
+ issuerUrl?: string;
49
+ defaultScopes?: string[];
50
+ }
51
+ export interface OAuthRouterConfig {
52
+ provider: ProxyOAuthServerProvider;
53
+ issuerUrl: URL;
54
+ baseUrl: URL;
55
+ serviceDocumentationUrl?: URL;
56
+ pathPrefix?: string;
57
+ }
58
+ export interface ProxyOAuthServerProvider {
59
+ verifyAccessToken(token: string): Promise<AccessToken>;
60
+ authorize(params: AuthorizeParams): Promise<string>;
61
+ token(params: TokenParams): Promise<TokenResponse>;
62
+ revoke(params: RevokeParams): Promise<void>;
63
+ readonly endpoints: OAuthEndpoints;
64
+ }
65
+ export interface AuthorizeParams {
66
+ response_type: string;
67
+ client_id: string;
68
+ redirect_uri: string;
69
+ scope?: string;
70
+ state?: string;
71
+ code_challenge: string;
72
+ code_challenge_method: string;
73
+ }
74
+ export interface TokenParams {
75
+ grant_type: string;
76
+ client_id: string;
77
+ client_secret?: string;
78
+ code?: string;
79
+ redirect_uri?: string;
80
+ refresh_token?: string;
81
+ code_verifier?: string;
82
+ }
83
+ export interface RevokeParams {
84
+ token: string;
85
+ token_type_hint?: string;
86
+ client_id?: string;
87
+ client_secret?: string;
88
+ }
89
+ export interface OAuthConfigOptions {
90
+ endpoints: {
91
+ authorizationUrl: string;
92
+ tokenUrl: string;
93
+ revocationUrl?: string;
94
+ userInfoUrl?: string;
95
+ registerUrl: string;
96
+ };
97
+ issuerUrl: string;
98
+ baseUrl: string;
99
+ serviceDocumentationUrl?: string;
100
+ pathPrefix?: string;
101
+ defaultScopes?: string[];
102
+ }
103
+ export interface OAuthProxyConfig {
104
+ endpoints: OAuthEndpoints;
105
+ issuerUrl: string;
106
+ baseUrl: string;
107
+ serviceDocumentationUrl?: string;
108
+ pathPrefix?: string;
109
+ storage?: OAuthStorage;
110
+ verifyAccessToken?: (token: string) => Promise<any>;
111
+ defaultScopes?: string[];
112
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Tests for Cloudflare KV Nonce Cache
3
+ */
4
+ export {};