@kya-os/mcp-i-core 1.3.3 → 1.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-test$colon$coverage.log +2530 -2522
- package/coverage/coverage-final.json +1 -1
- package/dist/services/oauth-config.service.d.ts.map +1 -1
- package/dist/services/oauth-config.service.js +27 -5
- package/dist/services/oauth-config.service.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/services/cache-busting.test.ts +125 -0
- package/src/services/oauth-config.service.ts +28 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauth-config.service.d.ts","sourceRoot":"","sources":["../../src/services/oauth-config.service.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAiB,MAAM,0BAA0B,CAAC;AAC3E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAGvE,MAAM,WAAW,wBAAwB;IACvC,oEAAoE;IACpE,OAAO,EAAE,MAAM,CAAC;IAEhB,iCAAiC;IACjC,MAAM,EAAE,MAAM,CAAC;IAEf,8CAA8C;IAC9C,aAAa,EAAE,aAAa,CAAC;IAE7B,mDAAmD;IACnD,KAAK,CAAC,EAAE,gBAAgB,CAAC;IAEzB,qDAAqD;IACrD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,+CAA+C;IAC/C,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;CACpD;AAED;;GAEG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,MAAM,CAGZ;gBAEU,MAAM,EAAE,wBAAwB;IAW5C;;;;;;;;OAQG;IACG,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"oauth-config.service.d.ts","sourceRoot":"","sources":["../../src/services/oauth-config.service.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAiB,MAAM,0BAA0B,CAAC;AAC3E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAGvE,MAAM,WAAW,wBAAwB;IACvC,oEAAoE;IACpE,OAAO,EAAE,MAAM,CAAC;IAEhB,iCAAiC;IACjC,MAAM,EAAE,MAAM,CAAC;IAEf,8CAA8C;IAC9C,aAAa,EAAE,aAAa,CAAC;IAE7B,mDAAmD;IACnD,KAAK,CAAC,EAAE,gBAAgB,CAAC;IAEzB,qDAAqD;IACrD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,+CAA+C;IAC/C,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;CACpD;AAED;;GAEG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,MAAM,CAGZ;gBAEU,MAAM,EAAE,wBAAwB;IAW5C;;;;;;;;OAQG;IACG,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IA8G7D;;;;OAIG;IACG,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKlD;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;CAIrC"}
|
|
@@ -66,17 +66,39 @@ class OAuthConfigService {
|
|
|
66
66
|
if (!result.success || !result.data) {
|
|
67
67
|
throw new Error("Invalid API response: missing success or data field");
|
|
68
68
|
}
|
|
69
|
-
// Parse providers object
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
// Parse providers object with snake_case → camelCase mapping
|
|
70
|
+
// AgentShield API may return snake_case fields, but OAuthProvider interface uses camelCase
|
|
71
|
+
const rawProviders = result.data.providers || {};
|
|
72
|
+
if (typeof rawProviders !== "object" || Array.isArray(rawProviders)) {
|
|
72
73
|
throw new Error("Invalid API response: providers must be an object, not an array");
|
|
73
74
|
}
|
|
75
|
+
// Map each provider's fields from snake_case to camelCase
|
|
76
|
+
const providers = {};
|
|
77
|
+
for (const [name, raw] of Object.entries(rawProviders)) {
|
|
78
|
+
const p = raw;
|
|
79
|
+
providers[name] = {
|
|
80
|
+
clientId: (p.clientId ?? p.client_id ?? ""),
|
|
81
|
+
clientSecret: (p.clientSecret ?? p.client_secret ?? null),
|
|
82
|
+
authorizationUrl: (p.authorizationUrl ?? p.authorization_url ?? ""),
|
|
83
|
+
tokenUrl: (p.tokenUrl ?? p.token_url ?? ""),
|
|
84
|
+
userInfoUrl: (p.userInfoUrl ?? p.user_info_url),
|
|
85
|
+
supportsPKCE: (p.supportsPKCE ?? p.supports_pkce ?? false),
|
|
86
|
+
requiresClientSecret: (p.requiresClientSecret ?? p.requires_client_secret ?? true),
|
|
87
|
+
scopes: (p.scopes ?? []),
|
|
88
|
+
defaultScopes: (p.defaultScopes ?? p.default_scopes),
|
|
89
|
+
proxyMode: (p.proxyMode ?? p.proxy_mode ?? false),
|
|
90
|
+
customParams: (p.customParams ?? p.custom_params),
|
|
91
|
+
tokenEndpointAuthMethod: (p.tokenEndpointAuthMethod ?? p.token_endpoint_auth_method),
|
|
92
|
+
responseType: (p.responseType ?? p.response_type),
|
|
93
|
+
grantType: (p.grantType ?? p.grant_type),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
74
96
|
// Build OAuthConfig object
|
|
75
97
|
// Extract configuredProvider from API response - this indicates which provider
|
|
76
98
|
// the user has actually configured in AgentShield dashboard
|
|
77
|
-
const configuredProvider = result.data.configuredProvider
|
|
99
|
+
const configuredProvider = result.data.configuredProvider ?? result.data.configured_provider ?? null;
|
|
78
100
|
const config = {
|
|
79
|
-
providers
|
|
101
|
+
providers,
|
|
80
102
|
configuredProvider,
|
|
81
103
|
};
|
|
82
104
|
// Cache config
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauth-config.service.js","sourceRoot":"","sources":["../../src/services/oauth-config.service.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;;AAKH,0EAA0E;AAsB1E;;GAEG;AACH,MAAa,kBAAkB;IACrB,MAAM,CAGZ;IAEF,YAAY,MAAgC;QAC1C,IAAI,CAAC,MAAM,GAAG;YACZ,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,aAAa,EAAE,MAAM,CAAC,aAAa;YACnC,QAAQ,EAAE,MAAM,CAAC,QAAQ,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,EAAE,oBAAoB;YAChE,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC;YACnC,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,IAAI,gDAAwB,EAAE;SACtD,CAAC;IACJ,CAAC;IAED;;;;;;;;OAQG;IACH,KAAK,CAAC,cAAc,CAAC,SAAiB;QACpC,cAAc;QACd,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACtD,IAAI,MAAM,EAAE,CAAC;YACX,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,gCAAgC,EAAE;gBACnD,SAAS;aACV,CAAC,CAAC;YACH,OAAO,MAAM,CAAC;QAChB,CAAC;QAED,6BAA6B;QAC7B,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,4BAA4B,kBAAkB,CAAC,SAAS,CAAC,YAAY,CAAC;QAExG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,+CAA+C,EAAE;YAClE,SAAS;YACT,GAAG;SACJ,CAAC,CAAC;QAEH,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,GAAG,EAAE;gBAC1D,MAAM,EAAE,KAAK;gBACb,OAAO,EAAE;oBACP,aAAa,EAAE,UAAU,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE;oBAC7C,cAAc,EAAE,kBAAkB;iBACnC;aACF,CAAC,CAAC;YAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,eAAe,CAAC,CAAC;gBACrE,MAAM,IAAI,KAAK,CACb,iCAAiC,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,MAAM,SAAS,EAAE,CACzF,CAAC;YACJ,CAAC;YAED,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,IAAI,EAMjC,CAAC;YAEF,8BAA8B;YAC9B,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;gBACpC,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;YACzE,CAAC;YAED,
|
|
1
|
+
{"version":3,"file":"oauth-config.service.js","sourceRoot":"","sources":["../../src/services/oauth-config.service.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
+
import { ToolProtectionService } from "../../services/tool-protection.service";
|
|
3
|
+
import { InMemoryToolProtectionCache } from "../../cache/tool-protection-cache";
|
|
4
|
+
|
|
5
|
+
describe("Cache Busting in clearAndRefresh", () => {
|
|
6
|
+
let cache: InMemoryToolProtectionCache;
|
|
7
|
+
let service: ToolProtectionService;
|
|
8
|
+
const agentDid = "did:key:test-agent";
|
|
9
|
+
const projectId = "test-project-123";
|
|
10
|
+
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
cache = new InMemoryToolProtectionCache();
|
|
13
|
+
service = new ToolProtectionService(
|
|
14
|
+
{
|
|
15
|
+
apiUrl: "https://test.agentshield.com",
|
|
16
|
+
apiKey: "test-api-key",
|
|
17
|
+
projectId,
|
|
18
|
+
cacheTtl: 300000,
|
|
19
|
+
debug: true,
|
|
20
|
+
},
|
|
21
|
+
cache
|
|
22
|
+
);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("clearAndRefresh should call fetchFromApi with bypassCDNCache: true", async () => {
|
|
26
|
+
// Spy on the private fetchFromApi method
|
|
27
|
+
const fetchSpy = vi.spyOn(service as any, "fetchFromApi").mockResolvedValue({
|
|
28
|
+
success: true,
|
|
29
|
+
data: {
|
|
30
|
+
toolProtections: {
|
|
31
|
+
greet: { requiresDelegation: true, requiredScopes: ["greet:execute"] },
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
await service.clearAndRefresh(agentDid);
|
|
37
|
+
|
|
38
|
+
// Verify fetchFromApi was called with bypassCDNCache: true
|
|
39
|
+
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
|
40
|
+
expect(fetchSpy).toHaveBeenCalledWith(agentDid, { bypassCDNCache: true });
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("fetchFromApi should add cache-busting query param when bypassCDNCache is true", async () => {
|
|
44
|
+
// Mock global fetch
|
|
45
|
+
const fetchMock = vi.fn().mockResolvedValue({
|
|
46
|
+
ok: true,
|
|
47
|
+
json: () => Promise.resolve({ success: true, data: { toolProtections: {} } }),
|
|
48
|
+
});
|
|
49
|
+
global.fetch = fetchMock;
|
|
50
|
+
|
|
51
|
+
// Call fetchFromApi directly with bypassCDNCache: true
|
|
52
|
+
await (service as any).fetchFromApi(agentDid, { bypassCDNCache: true });
|
|
53
|
+
|
|
54
|
+
// Verify the URL contains cache-busting timestamp
|
|
55
|
+
const calledUrl = fetchMock.mock.calls[0][0] as string;
|
|
56
|
+
expect(calledUrl).toContain("?_t=");
|
|
57
|
+
expect(calledUrl).toMatch(/\?_t=\d+$/); // Ends with ?_t=<timestamp>
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("fetchFromApi should add Cache-Control headers when bypassCDNCache is true", async () => {
|
|
61
|
+
// Mock global fetch
|
|
62
|
+
const fetchMock = vi.fn().mockResolvedValue({
|
|
63
|
+
ok: true,
|
|
64
|
+
json: () => Promise.resolve({ success: true, data: { toolProtections: {} } }),
|
|
65
|
+
});
|
|
66
|
+
global.fetch = fetchMock;
|
|
67
|
+
|
|
68
|
+
// Call fetchFromApi directly with bypassCDNCache: true
|
|
69
|
+
await (service as any).fetchFromApi(agentDid, { bypassCDNCache: true });
|
|
70
|
+
|
|
71
|
+
// Verify the headers contain cache-control
|
|
72
|
+
const calledOptions = fetchMock.mock.calls[0][1] as RequestInit;
|
|
73
|
+
const headers = calledOptions.headers as Record<string, string>;
|
|
74
|
+
|
|
75
|
+
expect(headers["Cache-Control"]).toBe("no-cache, no-store, must-revalidate");
|
|
76
|
+
expect(headers["Pragma"]).toBe("no-cache");
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("fetchFromApi should NOT add cache-busting when bypassCDNCache is false/undefined", async () => {
|
|
80
|
+
// Mock global fetch
|
|
81
|
+
const fetchMock = vi.fn().mockResolvedValue({
|
|
82
|
+
ok: true,
|
|
83
|
+
json: () => Promise.resolve({ success: true, data: { toolProtections: {} } }),
|
|
84
|
+
});
|
|
85
|
+
global.fetch = fetchMock;
|
|
86
|
+
|
|
87
|
+
// Call fetchFromApi without bypassCDNCache
|
|
88
|
+
await (service as any).fetchFromApi(agentDid);
|
|
89
|
+
|
|
90
|
+
// Verify the URL does NOT contain cache-busting timestamp
|
|
91
|
+
const calledUrl = fetchMock.mock.calls[0][0] as string;
|
|
92
|
+
expect(calledUrl).not.toContain("?_t=");
|
|
93
|
+
|
|
94
|
+
// Verify no cache-control header
|
|
95
|
+
const calledOptions = fetchMock.mock.calls[0][1] as RequestInit;
|
|
96
|
+
const headers = calledOptions.headers as Record<string, string>;
|
|
97
|
+
expect(headers["Cache-Control"]).toBeUndefined();
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("cache should be empty after clearAndRefresh deletes it", async () => {
|
|
101
|
+
const cacheKey = `config:tool-protections:${projectId}`;
|
|
102
|
+
|
|
103
|
+
// Pre-populate cache
|
|
104
|
+
await cache.set(cacheKey, { toolProtections: { old: { requiresDelegation: false, requiredScopes: [] } } }, 300000);
|
|
105
|
+
|
|
106
|
+
// Verify cache has data
|
|
107
|
+
const before = await cache.get(cacheKey);
|
|
108
|
+
expect(before).not.toBeNull();
|
|
109
|
+
|
|
110
|
+
// Mock fetchFromApi to avoid network call
|
|
111
|
+
vi.spyOn(service as any, "fetchFromApi").mockResolvedValue({
|
|
112
|
+
success: true,
|
|
113
|
+
data: { toolProtections: {} },
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// Clear and refresh
|
|
117
|
+
await service.clearAndRefresh(agentDid);
|
|
118
|
+
|
|
119
|
+
// Note: clearAndRefresh deletes then re-populates with fresh data
|
|
120
|
+
// But since our mock returns empty toolProtections, cache should have empty config
|
|
121
|
+
const after = await cache.get(cacheKey);
|
|
122
|
+
expect(after).toBeDefined();
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
@@ -108,20 +108,43 @@ export class OAuthConfigService {
|
|
|
108
108
|
throw new Error("Invalid API response: missing success or data field");
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
// Parse providers object
|
|
112
|
-
|
|
113
|
-
|
|
111
|
+
// Parse providers object with snake_case → camelCase mapping
|
|
112
|
+
// AgentShield API may return snake_case fields, but OAuthProvider interface uses camelCase
|
|
113
|
+
const rawProviders = result.data.providers || {};
|
|
114
|
+
if (typeof rawProviders !== "object" || Array.isArray(rawProviders)) {
|
|
114
115
|
throw new Error(
|
|
115
116
|
"Invalid API response: providers must be an object, not an array"
|
|
116
117
|
);
|
|
117
118
|
}
|
|
118
119
|
|
|
120
|
+
// Map each provider's fields from snake_case to camelCase
|
|
121
|
+
const providers: Record<string, OAuthProvider> = {};
|
|
122
|
+
for (const [name, raw] of Object.entries(rawProviders)) {
|
|
123
|
+
const p = raw as Record<string, unknown>;
|
|
124
|
+
providers[name] = {
|
|
125
|
+
clientId: (p.clientId ?? p.client_id ?? "") as string,
|
|
126
|
+
clientSecret: (p.clientSecret ?? p.client_secret ?? null) as string | null,
|
|
127
|
+
authorizationUrl: (p.authorizationUrl ?? p.authorization_url ?? "") as string,
|
|
128
|
+
tokenUrl: (p.tokenUrl ?? p.token_url ?? "") as string,
|
|
129
|
+
userInfoUrl: (p.userInfoUrl ?? p.user_info_url) as string | undefined,
|
|
130
|
+
supportsPKCE: (p.supportsPKCE ?? p.supports_pkce ?? false) as boolean,
|
|
131
|
+
requiresClientSecret: (p.requiresClientSecret ?? p.requires_client_secret ?? true) as boolean,
|
|
132
|
+
scopes: (p.scopes ?? []) as string[],
|
|
133
|
+
defaultScopes: (p.defaultScopes ?? p.default_scopes) as string[] | undefined,
|
|
134
|
+
proxyMode: (p.proxyMode ?? p.proxy_mode ?? false) as boolean,
|
|
135
|
+
customParams: (p.customParams ?? p.custom_params) as Record<string, string> | undefined,
|
|
136
|
+
tokenEndpointAuthMethod: (p.tokenEndpointAuthMethod ?? p.token_endpoint_auth_method) as "client_secret_post" | "client_secret_basic" | undefined,
|
|
137
|
+
responseType: (p.responseType ?? p.response_type) as string | undefined,
|
|
138
|
+
grantType: (p.grantType ?? p.grant_type) as string | undefined,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
119
142
|
// Build OAuthConfig object
|
|
120
143
|
// Extract configuredProvider from API response - this indicates which provider
|
|
121
144
|
// the user has actually configured in AgentShield dashboard
|
|
122
|
-
const configuredProvider = result.data.configuredProvider
|
|
145
|
+
const configuredProvider = result.data.configuredProvider ?? (result.data as any).configured_provider ?? null;
|
|
123
146
|
const config: OAuthConfig = {
|
|
124
|
-
providers
|
|
147
|
+
providers,
|
|
125
148
|
configuredProvider,
|
|
126
149
|
};
|
|
127
150
|
|