@wootsup/mcp 0.1.0-rc.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 (135) hide show
  1. package/CHANGELOG.md +91 -0
  2. package/LICENSE +21 -0
  3. package/README.md +179 -0
  4. package/SECURITY.md +163 -0
  5. package/dist/auth/keychain.d.ts +47 -0
  6. package/dist/auth/keychain.js +262 -0
  7. package/dist/auth/keychain.js.map +1 -0
  8. package/dist/auth/oauth-provider.d.ts +68 -0
  9. package/dist/auth/oauth-provider.js +232 -0
  10. package/dist/auth/oauth-provider.js.map +1 -0
  11. package/dist/auth/profiles.d.ts +52 -0
  12. package/dist/auth/profiles.js +200 -0
  13. package/dist/auth/profiles.js.map +1 -0
  14. package/dist/auth/token.d.ts +27 -0
  15. package/dist/auth/token.js +88 -0
  16. package/dist/auth/token.js.map +1 -0
  17. package/dist/index.d.ts +13 -0
  18. package/dist/index.js +137 -0
  19. package/dist/index.js.map +1 -0
  20. package/dist/install-skill.d.ts +23 -0
  21. package/dist/install-skill.js +73 -0
  22. package/dist/install-skill.js.map +1 -0
  23. package/dist/modules/apimapper/cache.d.ts +2 -0
  24. package/dist/modules/apimapper/cache.js +71 -0
  25. package/dist/modules/apimapper/cache.js.map +1 -0
  26. package/dist/modules/apimapper/client.d.ts +85 -0
  27. package/dist/modules/apimapper/client.js +523 -0
  28. package/dist/modules/apimapper/client.js.map +1 -0
  29. package/dist/modules/apimapper/connections.d.ts +2 -0
  30. package/dist/modules/apimapper/connections.js +406 -0
  31. package/dist/modules/apimapper/connections.js.map +1 -0
  32. package/dist/modules/apimapper/credential-sanitizer.d.ts +7 -0
  33. package/dist/modules/apimapper/credential-sanitizer.js +70 -0
  34. package/dist/modules/apimapper/credential-sanitizer.js.map +1 -0
  35. package/dist/modules/apimapper/credentials.d.ts +2 -0
  36. package/dist/modules/apimapper/credentials.js +258 -0
  37. package/dist/modules/apimapper/credentials.js.map +1 -0
  38. package/dist/modules/apimapper/diagnose.d.ts +18 -0
  39. package/dist/modules/apimapper/diagnose.js +305 -0
  40. package/dist/modules/apimapper/diagnose.js.map +1 -0
  41. package/dist/modules/apimapper/flows.d.ts +2 -0
  42. package/dist/modules/apimapper/flows.js +372 -0
  43. package/dist/modules/apimapper/flows.js.map +1 -0
  44. package/dist/modules/apimapper/get-skill.d.ts +4 -0
  45. package/dist/modules/apimapper/get-skill.js +88 -0
  46. package/dist/modules/apimapper/get-skill.js.map +1 -0
  47. package/dist/modules/apimapper/graph-builder.d.ts +47 -0
  48. package/dist/modules/apimapper/graph-builder.js +117 -0
  49. package/dist/modules/apimapper/graph-builder.js.map +1 -0
  50. package/dist/modules/apimapper/graph.d.ts +2 -0
  51. package/dist/modules/apimapper/graph.js +117 -0
  52. package/dist/modules/apimapper/graph.js.map +1 -0
  53. package/dist/modules/apimapper/index.d.ts +2 -0
  54. package/dist/modules/apimapper/index.js +43 -0
  55. package/dist/modules/apimapper/index.js.map +1 -0
  56. package/dist/modules/apimapper/inspect.d.ts +20 -0
  57. package/dist/modules/apimapper/inspect.js +86 -0
  58. package/dist/modules/apimapper/inspect.js.map +1 -0
  59. package/dist/modules/apimapper/library.d.ts +2 -0
  60. package/dist/modules/apimapper/library.js +237 -0
  61. package/dist/modules/apimapper/library.js.map +1 -0
  62. package/dist/modules/apimapper/license.d.ts +2 -0
  63. package/dist/modules/apimapper/license.js +142 -0
  64. package/dist/modules/apimapper/license.js.map +1 -0
  65. package/dist/modules/apimapper/local-sources.d.ts +2 -0
  66. package/dist/modules/apimapper/local-sources.js +123 -0
  67. package/dist/modules/apimapper/local-sources.js.map +1 -0
  68. package/dist/modules/apimapper/misc.d.ts +2 -0
  69. package/dist/modules/apimapper/misc.js +149 -0
  70. package/dist/modules/apimapper/misc.js.map +1 -0
  71. package/dist/modules/apimapper/node-schema.d.ts +217 -0
  72. package/dist/modules/apimapper/node-schema.js +218 -0
  73. package/dist/modules/apimapper/node-schema.js.map +1 -0
  74. package/dist/modules/apimapper/normalizers.d.ts +13 -0
  75. package/dist/modules/apimapper/normalizers.js +37 -0
  76. package/dist/modules/apimapper/normalizers.js.map +1 -0
  77. package/dist/modules/apimapper/onboarding.d.ts +51 -0
  78. package/dist/modules/apimapper/onboarding.js +201 -0
  79. package/dist/modules/apimapper/onboarding.js.map +1 -0
  80. package/dist/modules/apimapper/schema.d.ts +2 -0
  81. package/dist/modules/apimapper/schema.js +84 -0
  82. package/dist/modules/apimapper/schema.js.map +1 -0
  83. package/dist/modules/apimapper/settings.d.ts +2 -0
  84. package/dist/modules/apimapper/settings.js +157 -0
  85. package/dist/modules/apimapper/settings.js.map +1 -0
  86. package/dist/modules/apimapper/skill-resources.d.ts +4 -0
  87. package/dist/modules/apimapper/skill-resources.js +85 -0
  88. package/dist/modules/apimapper/skill-resources.js.map +1 -0
  89. package/dist/modules/apimapper/types.d.ts +111 -0
  90. package/dist/modules/apimapper/types.js +14 -0
  91. package/dist/modules/apimapper/types.js.map +1 -0
  92. package/dist/modules/apimapper/use-profile.d.ts +34 -0
  93. package/dist/modules/apimapper/use-profile.js +176 -0
  94. package/dist/modules/apimapper/use-profile.js.map +1 -0
  95. package/dist/modules/apimapper/workflows.d.ts +2 -0
  96. package/dist/modules/apimapper/workflows.js +301 -0
  97. package/dist/modules/apimapper/workflows.js.map +1 -0
  98. package/dist/platform/index.d.ts +71 -0
  99. package/dist/platform/index.js +377 -0
  100. package/dist/platform/index.js.map +1 -0
  101. package/dist/server-http.d.ts +22 -0
  102. package/dist/server-http.js +159 -0
  103. package/dist/server-http.js.map +1 -0
  104. package/dist/setup/detect-clients.d.ts +39 -0
  105. package/dist/setup/detect-clients.js +152 -0
  106. package/dist/setup/detect-clients.js.map +1 -0
  107. package/dist/setup/probe-handshake.d.ts +26 -0
  108. package/dist/setup/probe-handshake.js +159 -0
  109. package/dist/setup/probe-handshake.js.map +1 -0
  110. package/dist/setup/write-config.d.ts +25 -0
  111. package/dist/setup/write-config.js +247 -0
  112. package/dist/setup/write-config.js.map +1 -0
  113. package/dist/setup-cli.d.ts +49 -0
  114. package/dist/setup-cli.js +292 -0
  115. package/dist/setup-cli.js.map +1 -0
  116. package/dist/skill-instructions.d.ts +10 -0
  117. package/dist/skill-instructions.js +68 -0
  118. package/dist/skill-instructions.js.map +1 -0
  119. package/dist/transports/http.d.ts +29 -0
  120. package/dist/transports/http.js +267 -0
  121. package/dist/transports/http.js.map +1 -0
  122. package/dist/transports/stdio.d.ts +9 -0
  123. package/dist/transports/stdio.js +19 -0
  124. package/dist/transports/stdio.js.map +1 -0
  125. package/docs/architecture.md +140 -0
  126. package/docs/customgraph-internal-migration.md +210 -0
  127. package/docs/security.md +126 -0
  128. package/docs/tools.md +230 -0
  129. package/manifest.json +76 -0
  130. package/package.json +61 -0
  131. package/skills/apimapper/SKILL.md +57 -0
  132. package/skills/apimapper/reference/joomla.md +85 -0
  133. package/skills/apimapper/reference/oauth.md +94 -0
  134. package/skills/apimapper/reference/troubleshooting.md +123 -0
  135. package/skills/apimapper/reference/yootheme.md +96 -0
@@ -0,0 +1,262 @@
1
+ // src/auth/keychain.ts — Phase 5.1.
2
+ //
3
+ // Two storage backends behind a single Keychain interface:
4
+ //
5
+ // - SystemKeychain: thin wrapper around @napi-rs/keyring 1.3.x (`Entry`).
6
+ // Uses the OS keychain — macOS Keychain, Linux Secret Service / DBus,
7
+ // Windows DPAPI.
8
+ //
9
+ // - EncryptedFileKeychain: AES-256-GCM encrypted JSON file at
10
+ // `<configDir>/keychain.enc.json`. Used as fallback when the OS keychain
11
+ // isn't accessible (locked-down macOS sessions, minimal Linux without
12
+ // DBus, etc.).
13
+ //
14
+ // `createKeychain()` tries SystemKeychain first and falls back to the file
15
+ // impl on any throw. The encryption key is derived via PBKDF2 from a constant
16
+ // in-binary phrase + a per-install salt (random 32 bytes) stored beside the
17
+ // vault at `<configDir>/.salt`. This is NOT a strong defense against a local
18
+ // attacker with filesystem access, but it (a) prevents accidental leakage
19
+ // through casual file inspection, and (b) is no worse than the alternative
20
+ // (raw plaintext) for environments that already failed the system-keychain
21
+ // probe.
22
+ import { randomBytes, pbkdf2Sync, createCipheriv, createDecipheriv, } from "node:crypto";
23
+ import { mkdirSync, existsSync, readFileSync, writeFileSync, } from "node:fs";
24
+ import { createRequire } from "node:module";
25
+ import { join } from "node:path";
26
+ // ── SystemKeychain ───────────────────────────────────────────────────────
27
+ const SERVICE_NAME = "@wootsup/mcp";
28
+ // Defer the import error to construction time; tests on environments without
29
+ // the native binary (rare — package ships prebuilts for all major OSes) can
30
+ // fall through to the file impl via `createKeychain()`.
31
+ //
32
+ // We dynamic-require here because the napi binding initialises native code on
33
+ // load; deferring keeps unrelated tests fast and lets `createKeychain()` catch
34
+ // failures.
35
+ export class SystemKeychain {
36
+ service;
37
+ constructor(service = SERVICE_NAME) {
38
+ this.service = service;
39
+ // Touch the binding eagerly so ctor throws on broken envs — that's the
40
+ // signal `createKeychain()` uses to fall back.
41
+ //
42
+ // The binding's `new Entry(service, username)` does no I/O itself but
43
+ // requires the native lib to be loadable.
44
+ loadKeyringModule();
45
+ }
46
+ async set(ref, value) {
47
+ const keyring = loadKeyringModule();
48
+ const entry = new keyring.Entry(this.service, ref);
49
+ entry.setPassword(value);
50
+ }
51
+ async get(ref) {
52
+ const keyring = loadKeyringModule();
53
+ const entry = new keyring.Entry(this.service, ref);
54
+ try {
55
+ const v = entry.getPassword();
56
+ // The 1.3.x binding returns null when there's no entry. Other failure
57
+ // modes throw (e.g. Ambiguous on Linux with duplicate secrets).
58
+ return v ?? null;
59
+ }
60
+ catch (e) {
61
+ // Treat "no entry" as a normal miss; rethrow any other failure.
62
+ if (isNoEntryError(e))
63
+ return null;
64
+ throw e;
65
+ }
66
+ }
67
+ async delete(ref) {
68
+ const keyring = loadKeyringModule();
69
+ const entry = new keyring.Entry(this.service, ref);
70
+ try {
71
+ entry.deletePassword();
72
+ }
73
+ catch (e) {
74
+ // Idempotent: missing entry is fine.
75
+ if (isNoEntryError(e))
76
+ return;
77
+ throw e;
78
+ }
79
+ }
80
+ }
81
+ let _keyringMod;
82
+ function loadKeyringModule() {
83
+ if (_keyringMod)
84
+ return _keyringMod;
85
+ // The napi binding ships CJS and we want a synchronous throw at
86
+ // SystemKeychain ctor time, so we use createRequire (statically
87
+ // imported above) to load the CJS bridge from an ESM module.
88
+ //
89
+ // 2026-05-18 fix: the previous implementation used `require("node:module")`
90
+ // inside this function — but `require` is not defined globally in ESM,
91
+ // which made the SystemKeychain path fail with "require is not defined"
92
+ // before it could even try the keyring binding. The customer-facing
93
+ // symptom was every fresh install falling back to the file keychain.
94
+ const req = createRequire(import.meta.url);
95
+ _keyringMod = req("@napi-rs/keyring");
96
+ return _keyringMod;
97
+ }
98
+ function isNoEntryError(e) {
99
+ if (!(e instanceof Error))
100
+ return false;
101
+ // The 1.3.x bindings surface NoEntry as an Error whose message contains
102
+ // "NoEntry" or "no entry" depending on platform. Be liberal.
103
+ return /no\s*entry/i.test(e.message);
104
+ }
105
+ // ── EncryptedFileKeychain ────────────────────────────────────────────────
106
+ // AES-GCM parameters: 256-bit key, 96-bit IV (NIST-recommended), 128-bit tag.
107
+ const KEY_LEN = 32;
108
+ const IV_LEN = 12;
109
+ const TAG_LEN = 16;
110
+ const SALT_LEN = 32;
111
+ const PBKDF2_ITERS = 200_000;
112
+ const PBKDF2_DIGEST = "sha256";
113
+ // Constant in-binary phrase mixed with the per-install salt. Not a secret in
114
+ // the cryptographic sense (it's checked into the repo), but ensures the
115
+ // derived key is unique to this product even if two unrelated tools share the
116
+ // same salt file path by accident.
117
+ const KDF_PHRASE = "apimapper-mcp/v1/keychain";
118
+ export class EncryptedFileKeychain {
119
+ dir;
120
+ vaultPath;
121
+ saltPath;
122
+ cachedKey;
123
+ // In-memory mutex to serialise writes — vitest hits concurrent set() in the
124
+ // "concurrent writes" test and we don't want a torn JSON file.
125
+ writeChain = Promise.resolve();
126
+ constructor(configDir) {
127
+ this.dir = configDir;
128
+ this.vaultPath = join(configDir, "keychain.enc.json");
129
+ this.saltPath = join(configDir, ".salt");
130
+ if (!existsSync(this.dir)) {
131
+ mkdirSync(this.dir, { recursive: true });
132
+ }
133
+ }
134
+ deriveKey() {
135
+ if (this.cachedKey)
136
+ return this.cachedKey;
137
+ let salt;
138
+ if (existsSync(this.saltPath)) {
139
+ salt = readFileSync(this.saltPath);
140
+ if (salt.length !== SALT_LEN) {
141
+ // Corrupt salt — regenerate. Old entries become unreadable; that's
142
+ // an acceptable failure mode for a recovery flow.
143
+ salt = randomBytes(SALT_LEN);
144
+ writeFileSync(this.saltPath, salt, { mode: 0o600 });
145
+ }
146
+ }
147
+ else {
148
+ salt = randomBytes(SALT_LEN);
149
+ writeFileSync(this.saltPath, salt, { mode: 0o600 });
150
+ }
151
+ this.cachedKey = pbkdf2Sync(KDF_PHRASE, salt, PBKDF2_ITERS, KEY_LEN, PBKDF2_DIGEST);
152
+ return this.cachedKey;
153
+ }
154
+ readVault() {
155
+ if (!existsSync(this.vaultPath))
156
+ return {};
157
+ try {
158
+ const raw = readFileSync(this.vaultPath, "utf8");
159
+ if (!raw)
160
+ return {};
161
+ const parsed = JSON.parse(raw);
162
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
163
+ return {};
164
+ }
165
+ return parsed;
166
+ }
167
+ catch {
168
+ // Corrupt vault file: surface as empty rather than crashing — the
169
+ // surrounding flow can re-prompt for the token and overwrite.
170
+ return {};
171
+ }
172
+ }
173
+ writeVault(vault) {
174
+ writeFileSync(this.vaultPath, JSON.stringify(vault, null, 2), {
175
+ mode: 0o600,
176
+ });
177
+ }
178
+ encrypt(plaintext) {
179
+ const key = this.deriveKey();
180
+ const iv = randomBytes(IV_LEN);
181
+ const cipher = createCipheriv("aes-256-gcm", key, iv);
182
+ const ct = Buffer.concat([
183
+ cipher.update(plaintext, "utf8"),
184
+ cipher.final(),
185
+ ]);
186
+ const tag = cipher.getAuthTag();
187
+ return {
188
+ iv: iv.toString("base64"),
189
+ ciphertext: ct.toString("base64"),
190
+ tag: tag.toString("base64"),
191
+ };
192
+ }
193
+ decrypt(rec) {
194
+ try {
195
+ const key = this.deriveKey();
196
+ const iv = Buffer.from(rec.iv, "base64");
197
+ const ct = Buffer.from(rec.ciphertext, "base64");
198
+ const tag = Buffer.from(rec.tag, "base64");
199
+ if (iv.length !== IV_LEN || tag.length !== TAG_LEN)
200
+ return null;
201
+ const decipher = createDecipheriv("aes-256-gcm", key, iv);
202
+ decipher.setAuthTag(tag);
203
+ const pt = Buffer.concat([decipher.update(ct), decipher.final()]);
204
+ return pt.toString("utf8");
205
+ }
206
+ catch {
207
+ // Auth-tag mismatch or any other crypto error → return null so callers
208
+ // see "not found" rather than a hard error.
209
+ return null;
210
+ }
211
+ }
212
+ set(ref, value) {
213
+ // Serialise on the per-instance write chain to avoid torn JSON. Callers
214
+ // can still hit us concurrently — each set() waits for the prior one.
215
+ const next = this.writeChain.then(() => {
216
+ const vault = this.readVault();
217
+ vault[ref] = this.encrypt(value);
218
+ this.writeVault(vault);
219
+ });
220
+ this.writeChain = next.catch(() => undefined);
221
+ return next;
222
+ }
223
+ async get(ref) {
224
+ const vault = this.readVault();
225
+ const rec = vault[ref];
226
+ if (!rec)
227
+ return null;
228
+ return this.decrypt(rec);
229
+ }
230
+ delete(ref) {
231
+ const next = this.writeChain.then(() => {
232
+ const vault = this.readVault();
233
+ if (!(ref in vault))
234
+ return;
235
+ delete vault[ref];
236
+ this.writeVault(vault);
237
+ });
238
+ this.writeChain = next.catch(() => undefined);
239
+ return next;
240
+ }
241
+ }
242
+ export async function createKeychain(opts) {
243
+ const Ctor = opts.systemKeychainCtor ?? SystemKeychain;
244
+ try {
245
+ // Some envs throw on ctor; others throw on first read/write. We can only
246
+ // catch ctor failures synchronously here — read/write failures bubble up
247
+ // to the caller and are not auto-fallback (by design: silent fallback
248
+ // mid-session would mask data-loss).
249
+ const kc = new Ctor(opts.service ?? SERVICE_NAME);
250
+ return kc;
251
+ }
252
+ catch (e) {
253
+ if (!opts.silent) {
254
+ const msg = e instanceof Error ? e.message : String(e);
255
+ // eslint-disable-next-line no-console -- intentional one-shot stderr warning
256
+ console.warn(`[apimapper-mcp] System keychain unavailable (${msg}); ` +
257
+ `falling back to AES-GCM-encrypted file at ${opts.configDir}/keychain.enc.json`);
258
+ }
259
+ return new EncryptedFileKeychain(opts.configDir);
260
+ }
261
+ }
262
+ //# sourceMappingURL=keychain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keychain.js","sourceRoot":"","sources":["../../src/auth/keychain.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ export interface OAuthClientRegistration {
2
+ client_id: string;
3
+ client_id_issued_at: number;
4
+ redirect_uris: string[];
5
+ client_name?: string;
6
+ token_endpoint_auth_method: "none";
7
+ grant_types: ["authorization_code", "refresh_token"];
8
+ response_types: ["code"];
9
+ }
10
+ export interface IssueCodeArgs {
11
+ client_id: string;
12
+ redirect_uri: string;
13
+ code_challenge: string;
14
+ code_challenge_method: "S256" | "plain";
15
+ scope: string;
16
+ state?: string;
17
+ }
18
+ export interface ExchangeCodeArgs {
19
+ client_id: string;
20
+ code: string;
21
+ redirect_uri: string;
22
+ code_verifier: string;
23
+ }
24
+ export interface RefreshArgs {
25
+ client_id: string;
26
+ refresh_token: string;
27
+ }
28
+ export interface TokenResponse {
29
+ access_token: string;
30
+ token_type: "Bearer";
31
+ expires_in: number;
32
+ refresh_token: string;
33
+ scope: string;
34
+ }
35
+ export interface AccessTokenInfo {
36
+ client_id: string;
37
+ scope: string;
38
+ expires_at: number;
39
+ }
40
+ export interface OAuthProviderOptions {
41
+ /** Clock function returning unix seconds. Default: Date.now()/1000. */
42
+ now?: () => number;
43
+ /** Authorization code TTL (default 600s = 10 min). */
44
+ codeTtlSeconds?: number;
45
+ /** Access token TTL (default 86400s = 24 h). */
46
+ tokenTtlSeconds?: number;
47
+ /** Refresh token TTL (default 604800s = 7 d). */
48
+ refreshTtlSeconds?: number;
49
+ }
50
+ export interface OAuthProvider {
51
+ registerClient(args: {
52
+ redirect_uris: string[];
53
+ client_name?: string;
54
+ }): OAuthClientRegistration;
55
+ getClient(client_id: string): OAuthClientRegistration | null;
56
+ issueCode(args: IssueCodeArgs): string;
57
+ exchangeCode(args: ExchangeCodeArgs): TokenResponse;
58
+ refreshToken(args: RefreshArgs): TokenResponse;
59
+ verifyAccessToken(token: string): AccessTokenInfo | null;
60
+ sweep(): number;
61
+ stats(): {
62
+ clients: number;
63
+ codes: number;
64
+ tokens: number;
65
+ refreshes: number;
66
+ };
67
+ }
68
+ export declare function createOAuthProvider(options?: OAuthProviderOptions): OAuthProvider;
@@ -0,0 +1,232 @@
1
+ // src/auth/oauth-provider.ts — Phase 9.2.
2
+ //
3
+ // Minimal in-memory OAuth 2.0 Authorization-Code + PKCE + Dynamic Client
4
+ // Registration (DCR) provider for the remote HTTP MCP transport.
5
+ //
6
+ // References:
7
+ // - RFC 6749 (OAuth 2.0)
8
+ // - RFC 7636 (PKCE)
9
+ // - RFC 7591 (Dynamic Client Registration)
10
+ // - workers-oauth-provider (Cloudflare reference, in-memory variant)
11
+ //
12
+ // Storage backend is process-local Maps with TTL-based expiry — the
13
+ // hosted production service (mcp.wootsup.com) is expected to swap in a
14
+ // persistent backend, but for unit/integration tests + the local DXT
15
+ // bundle, in-memory is sufficient.
16
+ //
17
+ // Security notes:
18
+ // - Tokens are 32 random bytes hex-encoded (no JWT — keeps the surface
19
+ // tiny and prevents misuse of unverified payloads).
20
+ // - Authorization codes are 32 random bytes hex; single-use; bound to
21
+ // client_id + redirect_uri + code_challenge.
22
+ // - PKCE S256 is REQUIRED; "plain" is rejected.
23
+ // - On refresh, the old refresh token is invalidated (rotation).
24
+ import { randomBytes, createHash, timingSafeEqual } from "node:crypto";
25
+ // ── Helpers ────────────────────────────────────────────────────────────
26
+ function randomId(prefix) {
27
+ return `${prefix}${randomBytes(12).toString("hex")}`;
28
+ }
29
+ function randomOpaque() {
30
+ return randomBytes(32).toString("hex");
31
+ }
32
+ /** Constant-time compare to thwart timing oracles on token / verifier lookups. */
33
+ function safeEquals(a, b) {
34
+ const ba = Buffer.from(a);
35
+ const bb = Buffer.from(b);
36
+ if (ba.length !== bb.length)
37
+ return false;
38
+ return timingSafeEqual(ba, bb);
39
+ }
40
+ function s256(input) {
41
+ return createHash("sha256").update(input).digest("base64url");
42
+ }
43
+ // ── Factory ────────────────────────────────────────────────────────────
44
+ export function createOAuthProvider(options = {}) {
45
+ const now = options.now ?? (() => Math.floor(Date.now() / 1000));
46
+ const codeTtl = options.codeTtlSeconds ?? 600;
47
+ const tokenTtl = options.tokenTtlSeconds ?? 86_400;
48
+ const refreshTtl = options.refreshTtlSeconds ?? 7 * 86_400;
49
+ const clients = new Map();
50
+ const codes = new Map();
51
+ const tokens = new Map();
52
+ const refreshes = new Map();
53
+ function registerClient(args) {
54
+ if (!Array.isArray(args.redirect_uris) || args.redirect_uris.length === 0) {
55
+ throw new Error("invalid_redirect_uri: at least one redirect_uri required");
56
+ }
57
+ for (const uri of args.redirect_uris) {
58
+ if (typeof uri !== "string" || uri.length === 0) {
59
+ throw new Error("invalid_redirect_uri");
60
+ }
61
+ }
62
+ const reg = {
63
+ client_id: randomId("cli_"),
64
+ client_id_issued_at: now(),
65
+ redirect_uris: [...args.redirect_uris],
66
+ client_name: args.client_name,
67
+ token_endpoint_auth_method: "none", // public client (mobile/native/AI)
68
+ grant_types: ["authorization_code", "refresh_token"],
69
+ response_types: ["code"],
70
+ };
71
+ clients.set(reg.client_id, reg);
72
+ return reg;
73
+ }
74
+ function getClient(client_id) {
75
+ return clients.get(client_id) ?? null;
76
+ }
77
+ function issueCode(args) {
78
+ const client = clients.get(args.client_id);
79
+ if (!client)
80
+ throw new Error("unknown_client");
81
+ if (!client.redirect_uris.includes(args.redirect_uri)) {
82
+ throw new Error("invalid_redirect_uri");
83
+ }
84
+ if (!args.code_challenge || typeof args.code_challenge !== "string") {
85
+ throw new Error("invalid_request: code_challenge required");
86
+ }
87
+ if (args.code_challenge_method !== "S256") {
88
+ // Reject "plain" — S256 is mandatory for safety.
89
+ throw new Error("invalid_request: code_challenge_method must be S256");
90
+ }
91
+ const code = randomId("code_");
92
+ codes.set(code, {
93
+ client_id: args.client_id,
94
+ redirect_uri: args.redirect_uri,
95
+ code_challenge: args.code_challenge,
96
+ code_challenge_method: args.code_challenge_method,
97
+ scope: args.scope,
98
+ expires_at: now() + codeTtl,
99
+ used: false,
100
+ });
101
+ return code;
102
+ }
103
+ function exchangeCode(args) {
104
+ const rec = codes.get(args.code);
105
+ if (!rec)
106
+ throw new Error("invalid_grant: code not found");
107
+ if (rec.used)
108
+ throw new Error("invalid_grant: code already used");
109
+ if (rec.expires_at < now())
110
+ throw new Error("invalid_grant: code expired");
111
+ if (rec.client_id !== args.client_id) {
112
+ throw new Error("invalid_grant: client_id mismatch");
113
+ }
114
+ if (rec.redirect_uri !== args.redirect_uri) {
115
+ throw new Error("invalid_grant: redirect_uri mismatch");
116
+ }
117
+ const challengeFromVerifier = s256(args.code_verifier);
118
+ if (!safeEquals(challengeFromVerifier, rec.code_challenge)) {
119
+ throw new Error("invalid_grant: code_verifier mismatch");
120
+ }
121
+ rec.used = true;
122
+ const accessToken = randomOpaque();
123
+ const refreshToken = randomOpaque();
124
+ tokens.set(accessToken, {
125
+ client_id: rec.client_id,
126
+ scope: rec.scope,
127
+ expires_at: now() + tokenTtl,
128
+ });
129
+ refreshes.set(refreshToken, {
130
+ client_id: rec.client_id,
131
+ scope: rec.scope,
132
+ expires_at: now() + refreshTtl,
133
+ revoked: false,
134
+ });
135
+ return {
136
+ access_token: accessToken,
137
+ refresh_token: refreshToken,
138
+ token_type: "Bearer",
139
+ expires_in: tokenTtl,
140
+ scope: rec.scope,
141
+ };
142
+ }
143
+ function refreshToken(args) {
144
+ const rec = refreshes.get(args.refresh_token);
145
+ if (!rec)
146
+ throw new Error("invalid_grant: refresh_token not found");
147
+ if (rec.revoked)
148
+ throw new Error("invalid_grant: refresh_token revoked");
149
+ if (rec.expires_at < now()) {
150
+ throw new Error("invalid_grant: refresh_token expired");
151
+ }
152
+ if (rec.client_id !== args.client_id) {
153
+ throw new Error("invalid_grant: client_id mismatch");
154
+ }
155
+ // Rotate: revoke the old refresh token, mint a new pair.
156
+ rec.revoked = true;
157
+ const accessToken = randomOpaque();
158
+ const newRefresh = randomOpaque();
159
+ tokens.set(accessToken, {
160
+ client_id: rec.client_id,
161
+ scope: rec.scope,
162
+ expires_at: now() + tokenTtl,
163
+ });
164
+ refreshes.set(newRefresh, {
165
+ client_id: rec.client_id,
166
+ scope: rec.scope,
167
+ expires_at: now() + refreshTtl,
168
+ revoked: false,
169
+ });
170
+ return {
171
+ access_token: accessToken,
172
+ refresh_token: newRefresh,
173
+ token_type: "Bearer",
174
+ expires_in: tokenTtl,
175
+ scope: rec.scope,
176
+ };
177
+ }
178
+ function verifyAccessToken(token) {
179
+ const rec = tokens.get(token);
180
+ if (!rec)
181
+ return null;
182
+ if (rec.expires_at < now())
183
+ return null;
184
+ return {
185
+ client_id: rec.client_id,
186
+ scope: rec.scope,
187
+ expires_at: rec.expires_at,
188
+ };
189
+ }
190
+ function sweep() {
191
+ const t = now();
192
+ let evicted = 0;
193
+ for (const [k, v] of codes) {
194
+ if (v.expires_at < t || v.used) {
195
+ codes.delete(k);
196
+ evicted++;
197
+ }
198
+ }
199
+ for (const [k, v] of tokens) {
200
+ if (v.expires_at < t) {
201
+ tokens.delete(k);
202
+ evicted++;
203
+ }
204
+ }
205
+ for (const [k, v] of refreshes) {
206
+ if (v.expires_at < t || v.revoked) {
207
+ refreshes.delete(k);
208
+ evicted++;
209
+ }
210
+ }
211
+ return evicted;
212
+ }
213
+ function stats() {
214
+ return {
215
+ clients: clients.size,
216
+ codes: codes.size,
217
+ tokens: tokens.size,
218
+ refreshes: refreshes.size,
219
+ };
220
+ }
221
+ return {
222
+ registerClient,
223
+ getClient,
224
+ issueCode,
225
+ exchangeCode,
226
+ refreshToken,
227
+ verifyAccessToken,
228
+ sweep,
229
+ stats,
230
+ };
231
+ }
232
+ //# sourceMappingURL=oauth-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth-provider.js","sourceRoot":"","sources":["../../src/auth/oauth-provider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ export type ProfilePlatform = "wordpress" | "joomla" | "standalone";
2
+ export interface LastVerifiedIdentity {
3
+ plugin_version: string;
4
+ plugin_hash: string;
5
+ capabilities: string[];
6
+ }
7
+ export interface Profile {
8
+ /** Unique short name (e.g. "dev-wp", "client-x-prod"). */
9
+ name: string;
10
+ /** Base URL of the host. WordPress = wp-root, Joomla = site-root. */
11
+ siteUrl: string;
12
+ /** Platform kind — drives URL construction in the Platform abstraction. */
13
+ platform: ProfilePlatform;
14
+ /** Ref into the Keychain pointing at the raw amk_live_... token. */
15
+ keychainRef: string;
16
+ /**
17
+ * Snapshot of the last successful /identity probe — used to detect plugin
18
+ * drift on `apimapper_use_profile`. Optional because freshly added
19
+ * profiles haven't been probed yet.
20
+ */
21
+ lastVerifiedIdentity?: LastVerifiedIdentity;
22
+ /** ISO 8601 timestamp of when the profile was first added. */
23
+ addedAt: string;
24
+ }
25
+ /**
26
+ * Resolve the canonical config directory for apimapper-mcp:
27
+ *
28
+ * - macOS/Linux: $XDG_CONFIG_HOME/apimapper-mcp/ if set, else
29
+ * $HOME/.config/apimapper-mcp/
30
+ * - Windows: %APPDATA%/apimapper-mcp/
31
+ *
32
+ * Falls back to $HOME/.apimapper-mcp/ if none of the env vars are set
33
+ * (degenerate environment — better than failing).
34
+ */
35
+ export declare function resolveConfigDir(): string;
36
+ export declare class ProfileStore {
37
+ private readonly dir;
38
+ private readonly profilesPath;
39
+ private readonly activePath;
40
+ constructor(configDir?: string);
41
+ private readAll;
42
+ private writeAll;
43
+ private readActiveName;
44
+ private writeActiveName;
45
+ list(): Promise<Profile[]>;
46
+ get(name: string): Promise<Profile | null>;
47
+ add(profile: Profile): Promise<void>;
48
+ remove(name: string): Promise<boolean>;
49
+ update(name: string, partial: Partial<Profile>): Promise<void>;
50
+ setActive(name: string): Promise<void>;
51
+ getActive(): Promise<Profile | null>;
52
+ }