@kontext-dev/js-sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +70 -0
- package/dist/adapters/ai/index.cjs +175 -0
- package/dist/adapters/ai/index.cjs.map +1 -0
- package/dist/adapters/ai/index.d.cts +51 -0
- package/dist/adapters/ai/index.d.ts +51 -0
- package/dist/adapters/ai/index.js +173 -0
- package/dist/adapters/ai/index.js.map +1 -0
- package/dist/adapters/cloudflare/index.cjs +598 -0
- package/dist/adapters/cloudflare/index.cjs.map +1 -0
- package/dist/adapters/cloudflare/index.d.cts +214 -0
- package/dist/adapters/cloudflare/index.d.ts +214 -0
- package/dist/adapters/cloudflare/index.js +594 -0
- package/dist/adapters/cloudflare/index.js.map +1 -0
- package/dist/adapters/cloudflare/react.cjs +156 -0
- package/dist/adapters/cloudflare/react.cjs.map +1 -0
- package/dist/adapters/cloudflare/react.d.cts +68 -0
- package/dist/adapters/cloudflare/react.d.ts +68 -0
- package/dist/adapters/cloudflare/react.js +152 -0
- package/dist/adapters/cloudflare/react.js.map +1 -0
- package/dist/adapters/react/index.cjs +146 -0
- package/dist/adapters/react/index.cjs.map +1 -0
- package/dist/adapters/react/index.d.cts +103 -0
- package/dist/adapters/react/index.d.ts +103 -0
- package/dist/adapters/react/index.js +142 -0
- package/dist/adapters/react/index.js.map +1 -0
- package/dist/client/index.cjs +2415 -0
- package/dist/client/index.cjs.map +1 -0
- package/dist/client/index.d.cts +125 -0
- package/dist/client/index.d.ts +125 -0
- package/dist/client/index.js +2412 -0
- package/dist/client/index.js.map +1 -0
- package/dist/errors.cjs +213 -0
- package/dist/errors.cjs.map +1 -0
- package/dist/errors.d.cts +161 -0
- package/dist/errors.d.ts +161 -0
- package/dist/errors.js +201 -0
- package/dist/errors.js.map +1 -0
- package/dist/index-D5hS5PGn.d.ts +54 -0
- package/dist/index-DcL4a5Vq.d.cts +54 -0
- package/dist/index.cjs +4046 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +15 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +4029 -0
- package/dist/index.js.map +1 -0
- package/dist/kontext-CgIBANFo.d.cts +308 -0
- package/dist/kontext-CgIBANFo.d.ts +308 -0
- package/dist/management/index.cjs +867 -0
- package/dist/management/index.cjs.map +1 -0
- package/dist/management/index.d.cts +467 -0
- package/dist/management/index.d.ts +467 -0
- package/dist/management/index.js +855 -0
- package/dist/management/index.js.map +1 -0
- package/dist/mcp/index.cjs +799 -0
- package/dist/mcp/index.cjs.map +1 -0
- package/dist/mcp/index.d.cts +231 -0
- package/dist/mcp/index.d.ts +231 -0
- package/dist/mcp/index.js +797 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/oauth/index.cjs +418 -0
- package/dist/oauth/index.cjs.map +1 -0
- package/dist/oauth/index.d.cts +235 -0
- package/dist/oauth/index.d.ts +235 -0
- package/dist/oauth/index.js +414 -0
- package/dist/oauth/index.js.map +1 -0
- package/dist/server/index.cjs +1634 -0
- package/dist/server/index.cjs.map +1 -0
- package/dist/server/index.d.cts +10 -0
- package/dist/server/index.d.ts +10 -0
- package/dist/server/index.js +1629 -0
- package/dist/server/index.js.map +1 -0
- package/dist/types-CzhnlJHW.d.cts +397 -0
- package/dist/types-CzhnlJHW.d.ts +397 -0
- package/dist/types-RIzHnRpk.d.cts +23 -0
- package/dist/types-RIzHnRpk.d.ts +23 -0
- package/dist/verifier-CoJmYiw3.d.cts +109 -0
- package/dist/verifier-CoJmYiw3.d.ts +109 -0
- package/dist/verify/index.cjs +319 -0
- package/dist/verify/index.cjs.map +1 -0
- package/dist/verify/index.d.cts +63 -0
- package/dist/verify/index.d.ts +63 -0
- package/dist/verify/index.js +315 -0
- package/dist/verify/index.js.map +1 -0
- package/package.json +221 -0
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import { createRemoteJWKSet, jwtVerify, errors, decodeProtectedHeader } from 'jose';
|
|
2
|
+
|
|
3
|
+
// src/verify/verifier.ts
|
|
4
|
+
|
|
5
|
+
// src/verify/errors.ts
|
|
6
|
+
var TokenVerificationError = class _TokenVerificationError extends Error {
|
|
7
|
+
code;
|
|
8
|
+
constructor(code, message) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.name = "TokenVerificationError";
|
|
11
|
+
this.code = code;
|
|
12
|
+
Object.setPrototypeOf(this, _TokenVerificationError.prototype);
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
// src/verify/jwks-client.ts
|
|
17
|
+
var DEFAULT_CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
18
|
+
var DEFAULT_REFETCH_COOLDOWN_MS = 30 * 1e3;
|
|
19
|
+
var JwksClient = class {
|
|
20
|
+
jwksUrl;
|
|
21
|
+
cacheTtlMs;
|
|
22
|
+
refetchCooldownMs;
|
|
23
|
+
customFetch;
|
|
24
|
+
jwks = null;
|
|
25
|
+
lastFetchAt = 0;
|
|
26
|
+
lastRefreshAt = 0;
|
|
27
|
+
constructor(options) {
|
|
28
|
+
this.jwksUrl = new URL(options.jwksUrl);
|
|
29
|
+
this.cacheTtlMs = options.cacheTtlMs ?? DEFAULT_CACHE_TTL_MS;
|
|
30
|
+
this.refetchCooldownMs = options.refetchCooldownMs ?? DEFAULT_REFETCH_COOLDOWN_MS;
|
|
31
|
+
this.customFetch = options.fetch;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Get the JWKS key resolver for use with jose's jwtVerify.
|
|
35
|
+
*
|
|
36
|
+
* Creates the remote JWKS on first call and caches it.
|
|
37
|
+
* The jose library handles internal caching and key lookup.
|
|
38
|
+
*/
|
|
39
|
+
getKeyResolver() {
|
|
40
|
+
const now = Date.now();
|
|
41
|
+
if (this.jwks && now - this.lastFetchAt > this.cacheTtlMs) {
|
|
42
|
+
this.jwks = null;
|
|
43
|
+
}
|
|
44
|
+
if (!this.jwks) {
|
|
45
|
+
this.jwks = createRemoteJWKSet(this.jwksUrl, {
|
|
46
|
+
// jose handles caching internally, we just track our own refresh timing
|
|
47
|
+
...this.customFetch && {
|
|
48
|
+
[/* @__PURE__ */ Symbol.for("fetch")]: this.customFetch
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
this.lastFetchAt = now;
|
|
52
|
+
}
|
|
53
|
+
return this.jwks;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Force refresh the JWKS cache.
|
|
57
|
+
*
|
|
58
|
+
* Respects the refetch cooldown to prevent rapid refetching.
|
|
59
|
+
* Returns true if refresh was performed, false if cooldown not elapsed.
|
|
60
|
+
*/
|
|
61
|
+
refresh() {
|
|
62
|
+
const now = Date.now();
|
|
63
|
+
if (!this.canRefresh()) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
this.jwks = null;
|
|
67
|
+
this.lastRefreshAt = now;
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Check if a refresh is allowed (cooldown elapsed).
|
|
72
|
+
*/
|
|
73
|
+
canRefresh() {
|
|
74
|
+
return Date.now() - this.lastRefreshAt >= this.refetchCooldownMs;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Handle unknown kid errors by attempting refresh.
|
|
78
|
+
*
|
|
79
|
+
* @returns TokenVerificationError if refresh not allowed or already attempted
|
|
80
|
+
*/
|
|
81
|
+
handleUnknownKid(kid) {
|
|
82
|
+
if (this.refresh()) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
return new TokenVerificationError(
|
|
86
|
+
"UNKNOWN_KID",
|
|
87
|
+
`Unknown key ID: ${kid}. JWKS refresh on cooldown.`
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Clear the cache, forcing a fresh fetch on next access.
|
|
92
|
+
*/
|
|
93
|
+
clearCache() {
|
|
94
|
+
this.jwks = null;
|
|
95
|
+
this.lastFetchAt = 0;
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
// src/verify/verifier.ts
|
|
100
|
+
var DEFAULT_CLOCK_TOLERANCE_SEC = 30;
|
|
101
|
+
var SUPPORTED_ALGORITHMS = ["ES256", "RS256"];
|
|
102
|
+
var KontextTokenVerifier = class {
|
|
103
|
+
config;
|
|
104
|
+
jwksClient;
|
|
105
|
+
audiences;
|
|
106
|
+
constructor(config) {
|
|
107
|
+
this.config = {
|
|
108
|
+
jwksUrl: config.jwksUrl,
|
|
109
|
+
issuer: config.issuer,
|
|
110
|
+
audience: config.audience,
|
|
111
|
+
requiredScopes: config.requiredScopes ?? [],
|
|
112
|
+
cacheTtlMs: config.cacheTtlMs ?? 5 * 60 * 1e3,
|
|
113
|
+
refetchCooldownMs: config.refetchCooldownMs ?? 30 * 1e3,
|
|
114
|
+
clockToleranceSec: config.clockToleranceSec ?? DEFAULT_CLOCK_TOLERANCE_SEC,
|
|
115
|
+
fetch: config.fetch
|
|
116
|
+
};
|
|
117
|
+
this.audiences = Array.isArray(config.audience) ? config.audience : [config.audience];
|
|
118
|
+
this.jwksClient = new JwksClient({
|
|
119
|
+
jwksUrl: config.jwksUrl,
|
|
120
|
+
cacheTtlMs: this.config.cacheTtlMs,
|
|
121
|
+
refetchCooldownMs: this.config.refetchCooldownMs,
|
|
122
|
+
fetch: config.fetch
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Verify a JWT token.
|
|
127
|
+
*
|
|
128
|
+
* @param token - The JWT token string (without "Bearer " prefix)
|
|
129
|
+
* @returns VerifyResult with success=true and claims, or success=false and error
|
|
130
|
+
*/
|
|
131
|
+
async verify(token) {
|
|
132
|
+
try {
|
|
133
|
+
return await this.verifyInternal(token, false);
|
|
134
|
+
} catch (error) {
|
|
135
|
+
if (error instanceof TokenVerificationError) {
|
|
136
|
+
return { success: false, error };
|
|
137
|
+
}
|
|
138
|
+
return {
|
|
139
|
+
success: false,
|
|
140
|
+
error: new TokenVerificationError(
|
|
141
|
+
"INVALID_TOKEN_FORMAT",
|
|
142
|
+
`Unexpected verification error: ${error.message}`
|
|
143
|
+
)
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Verify a JWT token and return claims or null.
|
|
149
|
+
* Simpler API for cases where you don't need error details.
|
|
150
|
+
*
|
|
151
|
+
* @param token - The JWT token string (without "Bearer " prefix)
|
|
152
|
+
* @returns VerifiedTokenClaims if valid, null if invalid
|
|
153
|
+
*/
|
|
154
|
+
async verifyOrNull(token) {
|
|
155
|
+
const result = await this.verify(token);
|
|
156
|
+
return result.success ? result.claims : null;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Clear the JWKS cache, forcing a fresh fetch on next verification.
|
|
160
|
+
*/
|
|
161
|
+
clearCache() {
|
|
162
|
+
this.jwksClient.clearCache();
|
|
163
|
+
}
|
|
164
|
+
async verifyInternal(token, isRetry) {
|
|
165
|
+
const JWKS = this.jwksClient.getKeyResolver();
|
|
166
|
+
try {
|
|
167
|
+
const { payload, protectedHeader } = await jwtVerify(token, JWKS, {
|
|
168
|
+
issuer: this.config.issuer,
|
|
169
|
+
audience: this.audiences,
|
|
170
|
+
clockTolerance: this.config.clockToleranceSec,
|
|
171
|
+
algorithms: SUPPORTED_ALGORITHMS
|
|
172
|
+
});
|
|
173
|
+
const alg = protectedHeader.alg;
|
|
174
|
+
if (!SUPPORTED_ALGORITHMS.includes(alg)) {
|
|
175
|
+
throw new TokenVerificationError(
|
|
176
|
+
"UNSUPPORTED_ALGORITHM",
|
|
177
|
+
`Unsupported algorithm: ${alg}. Expected one of: ${SUPPORTED_ALGORITHMS.join(", ")}`
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
const jwtPayload = payload;
|
|
181
|
+
if (typeof jwtPayload.exp !== "number" || !Number.isFinite(jwtPayload.exp) || jwtPayload.exp <= 0) {
|
|
182
|
+
throw new TokenVerificationError(
|
|
183
|
+
"MISSING_CLAIMS",
|
|
184
|
+
"Token missing required exp claim"
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
const scopes = this.parseScopes(jwtPayload.scope);
|
|
188
|
+
for (const required of this.config.requiredScopes) {
|
|
189
|
+
if (!scopes.includes(required)) {
|
|
190
|
+
throw new TokenVerificationError(
|
|
191
|
+
"MISSING_SCOPE",
|
|
192
|
+
`Missing required scope: ${required}`
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
const clientId = jwtPayload.client_id || jwtPayload.sub;
|
|
197
|
+
if (!clientId) {
|
|
198
|
+
throw new TokenVerificationError(
|
|
199
|
+
"MISSING_CLAIMS",
|
|
200
|
+
"Token missing client_id and sub claims"
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
const claims = {
|
|
204
|
+
sub: jwtPayload.sub || "",
|
|
205
|
+
clientId,
|
|
206
|
+
scopes,
|
|
207
|
+
expiresAt: new Date(jwtPayload.exp * 1e3),
|
|
208
|
+
jti: jwtPayload.jti,
|
|
209
|
+
payload: jwtPayload
|
|
210
|
+
};
|
|
211
|
+
return { success: true, claims };
|
|
212
|
+
} catch (error) {
|
|
213
|
+
if (error instanceof errors.JWKSNoMatchingKey) {
|
|
214
|
+
if (!isRetry) {
|
|
215
|
+
const kid = this.extractKid(token);
|
|
216
|
+
const refreshError = this.jwksClient.handleUnknownKid(
|
|
217
|
+
kid || "unknown"
|
|
218
|
+
);
|
|
219
|
+
if (!refreshError) {
|
|
220
|
+
return this.verifyInternal(token, true);
|
|
221
|
+
}
|
|
222
|
+
return { success: false, error: refreshError };
|
|
223
|
+
}
|
|
224
|
+
return {
|
|
225
|
+
success: false,
|
|
226
|
+
error: new TokenVerificationError(
|
|
227
|
+
"UNKNOWN_KID",
|
|
228
|
+
"No matching key found in JWKS"
|
|
229
|
+
)
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
if (error instanceof errors.JWTExpired) {
|
|
233
|
+
return {
|
|
234
|
+
success: false,
|
|
235
|
+
error: new TokenVerificationError(
|
|
236
|
+
"TOKEN_EXPIRED",
|
|
237
|
+
"Token has expired"
|
|
238
|
+
)
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
if (error instanceof errors.JWTClaimValidationFailed) {
|
|
242
|
+
const message = error.message;
|
|
243
|
+
if (message.includes("iss")) {
|
|
244
|
+
const expected = Array.isArray(this.config.issuer) ? this.config.issuer.join(" or ") : this.config.issuer;
|
|
245
|
+
return {
|
|
246
|
+
success: false,
|
|
247
|
+
error: new TokenVerificationError(
|
|
248
|
+
"INVALID_ISSUER",
|
|
249
|
+
`Invalid issuer: expected ${expected}`
|
|
250
|
+
)
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
if (message.includes("aud")) {
|
|
254
|
+
return {
|
|
255
|
+
success: false,
|
|
256
|
+
error: new TokenVerificationError(
|
|
257
|
+
"INVALID_AUDIENCE",
|
|
258
|
+
`Invalid audience: expected one of ${this.audiences.join(", ")}`
|
|
259
|
+
)
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
if (message.includes("nbf")) {
|
|
263
|
+
return {
|
|
264
|
+
success: false,
|
|
265
|
+
error: new TokenVerificationError(
|
|
266
|
+
"TOKEN_NOT_YET_VALID",
|
|
267
|
+
"Token is not yet valid (nbf claim)"
|
|
268
|
+
)
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
if (error instanceof errors.JWSSignatureVerificationFailed) {
|
|
273
|
+
return {
|
|
274
|
+
success: false,
|
|
275
|
+
error: new TokenVerificationError(
|
|
276
|
+
"INVALID_SIGNATURE",
|
|
277
|
+
"Signature verification failed"
|
|
278
|
+
)
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
if (error instanceof errors.JWSInvalid) {
|
|
282
|
+
return {
|
|
283
|
+
success: false,
|
|
284
|
+
error: new TokenVerificationError(
|
|
285
|
+
"INVALID_TOKEN_FORMAT",
|
|
286
|
+
`Invalid JWS: ${error.message}`
|
|
287
|
+
)
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
if (error instanceof TokenVerificationError) {
|
|
291
|
+
throw error;
|
|
292
|
+
}
|
|
293
|
+
throw new TokenVerificationError(
|
|
294
|
+
"INVALID_TOKEN_FORMAT",
|
|
295
|
+
`Verification failed: ${error.message}`
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
parseScopes(scope) {
|
|
300
|
+
if (!scope) return [];
|
|
301
|
+
return scope.split(" ").map((s) => s.trim()).filter(Boolean);
|
|
302
|
+
}
|
|
303
|
+
extractKid(token) {
|
|
304
|
+
try {
|
|
305
|
+
const header = decodeProtectedHeader(token);
|
|
306
|
+
return header.kid ?? null;
|
|
307
|
+
} catch {
|
|
308
|
+
return null;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
export { JwksClient, KontextTokenVerifier, TokenVerificationError };
|
|
314
|
+
//# sourceMappingURL=index.js.map
|
|
315
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/verify/errors.ts","../../src/verify/jwks-client.ts","../../src/verify/verifier.ts"],"names":["joseErrors"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Token verification error codes.\n * These provide structured error information for debugging and error handling.\n */\nexport type TokenVerificationErrorCode =\n | \"INVALID_TOKEN_FORMAT\"\n | \"INVALID_SIGNATURE\"\n | \"TOKEN_EXPIRED\"\n | \"TOKEN_NOT_YET_VALID\"\n | \"INVALID_ISSUER\"\n | \"INVALID_AUDIENCE\"\n | \"MISSING_SCOPE\"\n | \"MISSING_CLAIMS\"\n | \"JWKS_FETCH_FAILED\"\n | \"UNKNOWN_KID\"\n | \"UNSUPPORTED_ALGORITHM\";\n\n/**\n * Error thrown when token verification fails.\n * Contains a structured error code for programmatic handling.\n */\nexport class TokenVerificationError extends Error {\n readonly code: TokenVerificationErrorCode;\n\n constructor(code: TokenVerificationErrorCode, message: string) {\n super(message);\n this.name = \"TokenVerificationError\";\n this.code = code;\n Object.setPrototypeOf(this, TokenVerificationError.prototype);\n }\n}\n","import { createRemoteJWKSet, type JWTVerifyGetKey } from \"jose\";\nimport { TokenVerificationError } from \"./errors.js\";\n\n/**\n * Options for the JWKS client.\n */\nexport interface JwksClientOptions {\n /** JWKS endpoint URL */\n jwksUrl: string;\n\n /** Cache TTL in milliseconds (default: 5 minutes) */\n cacheTtlMs?: number;\n\n /** Minimum time between refetches in milliseconds (default: 30 seconds) */\n refetchCooldownMs?: number;\n\n /** Custom fetch function for testing */\n fetch?: typeof globalThis.fetch;\n}\n\nconst DEFAULT_CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes\nconst DEFAULT_REFETCH_COOLDOWN_MS = 30 * 1000; // 30 seconds\n\n/**\n * JWKS client with caching and rate-limited refetching.\n *\n * Uses jose's createRemoteJWKSet for JWKS fetching and caching,\n * but adds rate limiting to prevent DoS via rapid refetch requests.\n */\nexport class JwksClient {\n private readonly jwksUrl: URL;\n private readonly cacheTtlMs: number;\n private readonly refetchCooldownMs: number;\n private readonly customFetch?: typeof globalThis.fetch;\n\n private jwks: JWTVerifyGetKey | null = null;\n private lastFetchAt = 0;\n private lastRefreshAt = 0;\n\n constructor(options: JwksClientOptions) {\n this.jwksUrl = new URL(options.jwksUrl);\n this.cacheTtlMs = options.cacheTtlMs ?? DEFAULT_CACHE_TTL_MS;\n this.refetchCooldownMs =\n options.refetchCooldownMs ?? DEFAULT_REFETCH_COOLDOWN_MS;\n this.customFetch = options.fetch;\n }\n\n /**\n * Get the JWKS key resolver for use with jose's jwtVerify.\n *\n * Creates the remote JWKS on first call and caches it.\n * The jose library handles internal caching and key lookup.\n */\n getKeyResolver(): JWTVerifyGetKey {\n const now = Date.now();\n\n // Check if we need to refresh (cache expired)\n if (this.jwks && now - this.lastFetchAt > this.cacheTtlMs) {\n this.jwks = null;\n }\n\n if (!this.jwks) {\n this.jwks = createRemoteJWKSet(this.jwksUrl, {\n // jose handles caching internally, we just track our own refresh timing\n ...(this.customFetch && {\n [Symbol.for(\"fetch\")]: this.customFetch,\n }),\n });\n this.lastFetchAt = now;\n }\n\n return this.jwks;\n }\n\n /**\n * Force refresh the JWKS cache.\n *\n * Respects the refetch cooldown to prevent rapid refetching.\n * Returns true if refresh was performed, false if cooldown not elapsed.\n */\n refresh(): boolean {\n const now = Date.now();\n\n if (!this.canRefresh()) {\n return false;\n }\n\n this.jwks = null;\n this.lastRefreshAt = now;\n return true;\n }\n\n /**\n * Check if a refresh is allowed (cooldown elapsed).\n */\n canRefresh(): boolean {\n return Date.now() - this.lastRefreshAt >= this.refetchCooldownMs;\n }\n\n /**\n * Handle unknown kid errors by attempting refresh.\n *\n * @returns TokenVerificationError if refresh not allowed or already attempted\n */\n handleUnknownKid(kid: string): TokenVerificationError | null {\n if (this.refresh()) {\n // Refresh performed, caller should retry verification\n return null;\n }\n\n // Cooldown not elapsed, return error\n return new TokenVerificationError(\n \"UNKNOWN_KID\",\n `Unknown key ID: ${kid}. JWKS refresh on cooldown.`,\n );\n }\n\n /**\n * Clear the cache, forcing a fresh fetch on next access.\n */\n clearCache(): void {\n this.jwks = null;\n this.lastFetchAt = 0;\n // Don't reset lastRefreshAt to maintain cooldown protection\n }\n}\n","import { jwtVerify, decodeProtectedHeader, errors as joseErrors } from \"jose\";\nimport { JwksClient } from \"./jwks-client.js\";\nimport { TokenVerificationError } from \"./errors.js\";\nimport type {\n KontextTokenVerifierConfig,\n VerifiedTokenClaims,\n VerifyResult,\n JwtPayload,\n} from \"./types.js\";\n\nconst DEFAULT_CLOCK_TOLERANCE_SEC = 30;\nconst SUPPORTED_ALGORITHMS = [\"ES256\", \"RS256\"];\n\n/**\n * Token verifier for Kontext-issued JWTs using JWKS discovery.\n *\n * Uses the jose library for robust JWT verification with support for:\n * - ES256 and RS256 algorithms\n * - JWKS-based key discovery with caching\n * - Key rotation support with rate-limited refetching\n * - Configurable clock tolerance\n * - Typed error responses\n *\n * @example\n * ```typescript\n * import { KontextTokenVerifier } from '@kontext-dev/js-sdk';\n *\n * const verifier = new KontextTokenVerifier({\n * jwksUrl: 'https://api.kontext.dev/.well-known/jwks.json',\n * issuer: 'kontext-token-exchange',\n * audience: 'mcp-gateway',\n * requiredScopes: ['mcp:invoke'],\n * });\n *\n * const result = await verifier.verify(bearerToken);\n * if (result.success) {\n * console.log(`Verified token for client: ${result.claims.clientId}`);\n * } else {\n * console.error(`Verification failed: ${result.error.code}`);\n * }\n * ```\n */\ninterface ResolvedConfig {\n jwksUrl: string;\n issuer: string | string[];\n audience: string | string[];\n requiredScopes: string[];\n cacheTtlMs: number;\n refetchCooldownMs: number;\n clockToleranceSec: number;\n fetch?: typeof globalThis.fetch;\n}\n\nexport class KontextTokenVerifier {\n private readonly config: ResolvedConfig;\n private readonly jwksClient: JwksClient;\n private readonly audiences: string[];\n\n constructor(config: KontextTokenVerifierConfig) {\n this.config = {\n jwksUrl: config.jwksUrl,\n issuer: config.issuer,\n audience: config.audience,\n requiredScopes: config.requiredScopes ?? [],\n cacheTtlMs: config.cacheTtlMs ?? 5 * 60 * 1000,\n refetchCooldownMs: config.refetchCooldownMs ?? 30 * 1000,\n clockToleranceSec:\n config.clockToleranceSec ?? DEFAULT_CLOCK_TOLERANCE_SEC,\n fetch: config.fetch,\n };\n\n this.audiences = Array.isArray(config.audience)\n ? config.audience\n : [config.audience];\n\n this.jwksClient = new JwksClient({\n jwksUrl: config.jwksUrl,\n cacheTtlMs: this.config.cacheTtlMs,\n refetchCooldownMs: this.config.refetchCooldownMs,\n fetch: config.fetch,\n });\n }\n\n /**\n * Verify a JWT token.\n *\n * @param token - The JWT token string (without \"Bearer \" prefix)\n * @returns VerifyResult with success=true and claims, or success=false and error\n */\n async verify(token: string): Promise<VerifyResult> {\n try {\n return await this.verifyInternal(token, false);\n } catch (error) {\n if (error instanceof TokenVerificationError) {\n return { success: false, error };\n }\n\n // Unexpected error\n return {\n success: false,\n error: new TokenVerificationError(\n \"INVALID_TOKEN_FORMAT\",\n `Unexpected verification error: ${(error as Error).message}`,\n ),\n };\n }\n }\n\n /**\n * Verify a JWT token and return claims or null.\n * Simpler API for cases where you don't need error details.\n *\n * @param token - The JWT token string (without \"Bearer \" prefix)\n * @returns VerifiedTokenClaims if valid, null if invalid\n */\n async verifyOrNull(token: string): Promise<VerifiedTokenClaims | null> {\n const result = await this.verify(token);\n return result.success ? result.claims : null;\n }\n\n /**\n * Clear the JWKS cache, forcing a fresh fetch on next verification.\n */\n clearCache(): void {\n this.jwksClient.clearCache();\n }\n\n private async verifyInternal(\n token: string,\n isRetry: boolean,\n ): Promise<VerifyResult> {\n const JWKS = this.jwksClient.getKeyResolver();\n\n try {\n // Use jose's jwtVerify for robust verification\n const { payload, protectedHeader } = await jwtVerify(token, JWKS, {\n issuer: this.config.issuer,\n audience: this.audiences,\n clockTolerance: this.config.clockToleranceSec,\n algorithms: SUPPORTED_ALGORITHMS,\n });\n\n // Check algorithm is supported\n const alg = protectedHeader.alg;\n if (!SUPPORTED_ALGORITHMS.includes(alg)) {\n throw new TokenVerificationError(\n \"UNSUPPORTED_ALGORITHM\",\n `Unsupported algorithm: ${alg}. Expected one of: ${SUPPORTED_ALGORITHMS.join(\", \")}`,\n );\n }\n\n // Validate required claims\n const jwtPayload = payload as JwtPayload;\n if (\n typeof jwtPayload.exp !== \"number\" ||\n !Number.isFinite(jwtPayload.exp) ||\n jwtPayload.exp <= 0\n ) {\n throw new TokenVerificationError(\n \"MISSING_CLAIMS\",\n \"Token missing required exp claim\",\n );\n }\n\n // Extract and validate scopes\n const scopes = this.parseScopes(jwtPayload.scope);\n for (const required of this.config.requiredScopes) {\n if (!scopes.includes(required)) {\n throw new TokenVerificationError(\n \"MISSING_SCOPE\",\n `Missing required scope: ${required}`,\n );\n }\n }\n\n // Extract client ID\n const clientId = jwtPayload.client_id || jwtPayload.sub;\n if (!clientId) {\n throw new TokenVerificationError(\n \"MISSING_CLAIMS\",\n \"Token missing client_id and sub claims\",\n );\n }\n\n // Build verified claims\n const claims: VerifiedTokenClaims = {\n sub: jwtPayload.sub || \"\",\n clientId,\n scopes,\n expiresAt: new Date(jwtPayload.exp * 1000),\n jti: jwtPayload.jti,\n payload: jwtPayload,\n };\n\n return { success: true, claims };\n } catch (error) {\n // Handle jose-specific errors\n if (error instanceof joseErrors.JWKSNoMatchingKey) {\n // Unknown kid - try refreshing JWKS once\n if (!isRetry) {\n const kid = this.extractKid(token);\n const refreshError = this.jwksClient.handleUnknownKid(\n kid || \"unknown\",\n );\n if (!refreshError) {\n // Refresh performed, retry verification\n return this.verifyInternal(token, true);\n }\n return { success: false, error: refreshError };\n }\n\n return {\n success: false,\n error: new TokenVerificationError(\n \"UNKNOWN_KID\",\n \"No matching key found in JWKS\",\n ),\n };\n }\n\n if (error instanceof joseErrors.JWTExpired) {\n return {\n success: false,\n error: new TokenVerificationError(\n \"TOKEN_EXPIRED\",\n \"Token has expired\",\n ),\n };\n }\n\n if (error instanceof joseErrors.JWTClaimValidationFailed) {\n const message = error.message;\n if (message.includes(\"iss\")) {\n const expected = Array.isArray(this.config.issuer)\n ? this.config.issuer.join(\" or \")\n : this.config.issuer;\n return {\n success: false,\n error: new TokenVerificationError(\n \"INVALID_ISSUER\",\n `Invalid issuer: expected ${expected}`,\n ),\n };\n }\n if (message.includes(\"aud\")) {\n return {\n success: false,\n error: new TokenVerificationError(\n \"INVALID_AUDIENCE\",\n `Invalid audience: expected one of ${this.audiences.join(\", \")}`,\n ),\n };\n }\n if (message.includes(\"nbf\")) {\n return {\n success: false,\n error: new TokenVerificationError(\n \"TOKEN_NOT_YET_VALID\",\n \"Token is not yet valid (nbf claim)\",\n ),\n };\n }\n }\n\n if (error instanceof joseErrors.JWSSignatureVerificationFailed) {\n return {\n success: false,\n error: new TokenVerificationError(\n \"INVALID_SIGNATURE\",\n \"Signature verification failed\",\n ),\n };\n }\n\n if (error instanceof joseErrors.JWSInvalid) {\n return {\n success: false,\n error: new TokenVerificationError(\n \"INVALID_TOKEN_FORMAT\",\n `Invalid JWS: ${error.message}`,\n ),\n };\n }\n\n // Re-throw TokenVerificationError\n if (error instanceof TokenVerificationError) {\n throw error;\n }\n\n // Unknown error\n throw new TokenVerificationError(\n \"INVALID_TOKEN_FORMAT\",\n `Verification failed: ${(error as Error).message}`,\n );\n }\n }\n\n private parseScopes(scope: string | undefined): string[] {\n if (!scope) return [];\n return scope\n .split(\" \")\n .map((s) => s.trim())\n .filter(Boolean);\n }\n\n private extractKid(token: string): string | null {\n try {\n const header = decodeProtectedHeader(token);\n return header.kid ?? null;\n } catch {\n return null;\n }\n }\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kontext-dev/js-sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Broker credentials between your AI agents and integrations.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"typesVersions": {
|
|
10
|
+
"*": {
|
|
11
|
+
"ai": [
|
|
12
|
+
"./dist/adapters/ai/index.d.ts"
|
|
13
|
+
],
|
|
14
|
+
"server": [
|
|
15
|
+
"./dist/server/index.d.ts"
|
|
16
|
+
],
|
|
17
|
+
"errors": [
|
|
18
|
+
"./dist/errors.d.ts"
|
|
19
|
+
],
|
|
20
|
+
"management": [
|
|
21
|
+
"./dist/management/index.d.ts"
|
|
22
|
+
],
|
|
23
|
+
"mcp": [
|
|
24
|
+
"./dist/mcp/index.d.ts"
|
|
25
|
+
]
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"exports": {
|
|
29
|
+
".": {
|
|
30
|
+
"import": {
|
|
31
|
+
"types": "./dist/index.d.ts",
|
|
32
|
+
"default": "./dist/index.js"
|
|
33
|
+
},
|
|
34
|
+
"require": {
|
|
35
|
+
"types": "./dist/index.d.cts",
|
|
36
|
+
"default": "./dist/index.cjs"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"./client": {
|
|
40
|
+
"import": {
|
|
41
|
+
"types": "./dist/client/index.d.ts",
|
|
42
|
+
"default": "./dist/client/index.js"
|
|
43
|
+
},
|
|
44
|
+
"require": {
|
|
45
|
+
"types": "./dist/client/index.d.cts",
|
|
46
|
+
"default": "./dist/client/index.cjs"
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"./ai": {
|
|
50
|
+
"import": {
|
|
51
|
+
"types": "./dist/adapters/ai/index.d.ts",
|
|
52
|
+
"default": "./dist/adapters/ai/index.js"
|
|
53
|
+
},
|
|
54
|
+
"require": {
|
|
55
|
+
"types": "./dist/adapters/ai/index.d.cts",
|
|
56
|
+
"default": "./dist/adapters/ai/index.cjs"
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"./server": {
|
|
60
|
+
"import": {
|
|
61
|
+
"types": "./dist/server/index.d.ts",
|
|
62
|
+
"default": "./dist/server/index.js"
|
|
63
|
+
},
|
|
64
|
+
"require": {
|
|
65
|
+
"types": "./dist/server/index.d.cts",
|
|
66
|
+
"default": "./dist/server/index.cjs"
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
"./cloudflare": {
|
|
70
|
+
"import": {
|
|
71
|
+
"types": "./dist/adapters/cloudflare/index.d.ts",
|
|
72
|
+
"default": "./dist/adapters/cloudflare/index.js"
|
|
73
|
+
},
|
|
74
|
+
"require": {
|
|
75
|
+
"types": "./dist/adapters/cloudflare/index.d.cts",
|
|
76
|
+
"default": "./dist/adapters/cloudflare/index.cjs"
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
"./errors": {
|
|
80
|
+
"import": {
|
|
81
|
+
"types": "./dist/errors.d.ts",
|
|
82
|
+
"default": "./dist/errors.js"
|
|
83
|
+
},
|
|
84
|
+
"require": {
|
|
85
|
+
"types": "./dist/errors.d.cts",
|
|
86
|
+
"default": "./dist/errors.cjs"
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
"./verify": {
|
|
90
|
+
"import": {
|
|
91
|
+
"types": "./dist/verify/index.d.ts",
|
|
92
|
+
"default": "./dist/verify/index.js"
|
|
93
|
+
},
|
|
94
|
+
"require": {
|
|
95
|
+
"types": "./dist/verify/index.d.cts",
|
|
96
|
+
"default": "./dist/verify/index.cjs"
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
"./react": {
|
|
100
|
+
"import": {
|
|
101
|
+
"types": "./dist/adapters/react/index.d.ts",
|
|
102
|
+
"default": "./dist/adapters/react/index.js"
|
|
103
|
+
},
|
|
104
|
+
"require": {
|
|
105
|
+
"types": "./dist/adapters/react/index.d.cts",
|
|
106
|
+
"default": "./dist/adapters/react/index.cjs"
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
"./react/cloudflare": {
|
|
110
|
+
"import": {
|
|
111
|
+
"types": "./dist/adapters/cloudflare/react.d.ts",
|
|
112
|
+
"default": "./dist/adapters/cloudflare/react.js"
|
|
113
|
+
},
|
|
114
|
+
"require": {
|
|
115
|
+
"types": "./dist/adapters/cloudflare/react.d.cts",
|
|
116
|
+
"default": "./dist/adapters/cloudflare/react.cjs"
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
"./management": {
|
|
120
|
+
"import": {
|
|
121
|
+
"types": "./dist/management/index.d.ts",
|
|
122
|
+
"default": "./dist/management/index.js"
|
|
123
|
+
},
|
|
124
|
+
"require": {
|
|
125
|
+
"types": "./dist/management/index.d.cts",
|
|
126
|
+
"default": "./dist/management/index.cjs"
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
"./mcp": {
|
|
130
|
+
"import": {
|
|
131
|
+
"types": "./dist/mcp/index.d.ts",
|
|
132
|
+
"default": "./dist/mcp/index.js"
|
|
133
|
+
},
|
|
134
|
+
"require": {
|
|
135
|
+
"types": "./dist/mcp/index.d.cts",
|
|
136
|
+
"default": "./dist/mcp/index.cjs"
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
"./oauth": {
|
|
140
|
+
"import": {
|
|
141
|
+
"types": "./dist/oauth/index.d.ts",
|
|
142
|
+
"default": "./dist/oauth/index.js"
|
|
143
|
+
},
|
|
144
|
+
"require": {
|
|
145
|
+
"types": "./dist/oauth/index.d.cts",
|
|
146
|
+
"default": "./dist/oauth/index.cjs"
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
"files": [
|
|
151
|
+
"dist"
|
|
152
|
+
],
|
|
153
|
+
"scripts": {
|
|
154
|
+
"build": "tsup",
|
|
155
|
+
"dev": "tsup --watch",
|
|
156
|
+
"lint": "eslint . --max-warnings 0",
|
|
157
|
+
"lint:fix": "eslint . --fix --max-warnings 0",
|
|
158
|
+
"test": "vitest run",
|
|
159
|
+
"test:watch": "vitest",
|
|
160
|
+
"test:integration": "vitest run --config vitest.integration.config.ts",
|
|
161
|
+
"check-types": "tsc -p tsconfig.json --noEmit",
|
|
162
|
+
"clean": "rm -rf dist"
|
|
163
|
+
},
|
|
164
|
+
"dependencies": {
|
|
165
|
+
"jose": "^6.1.3",
|
|
166
|
+
"zod": "^3.24.1"
|
|
167
|
+
},
|
|
168
|
+
"peerDependencies": {
|
|
169
|
+
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
170
|
+
"agents": ">=0.4.0",
|
|
171
|
+
"ai": "^4.0.0",
|
|
172
|
+
"express": "^4.21.0 || ^5.0.0",
|
|
173
|
+
"react": "^18.0.0 || ^19.0.0"
|
|
174
|
+
},
|
|
175
|
+
"peerDependenciesMeta": {
|
|
176
|
+
"@modelcontextprotocol/sdk": {
|
|
177
|
+
"optional": true
|
|
178
|
+
},
|
|
179
|
+
"ai": {
|
|
180
|
+
"optional": true
|
|
181
|
+
},
|
|
182
|
+
"express": {
|
|
183
|
+
"optional": true
|
|
184
|
+
},
|
|
185
|
+
"agents": {
|
|
186
|
+
"optional": true
|
|
187
|
+
},
|
|
188
|
+
"react": {
|
|
189
|
+
"optional": true
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
"devDependencies": {
|
|
193
|
+
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
194
|
+
"@types/bcryptjs": "^3.0.0",
|
|
195
|
+
"@types/react": "^19.0.0",
|
|
196
|
+
"@types/express": "^5.0.0",
|
|
197
|
+
"@types/node": "^24.10.1",
|
|
198
|
+
"@workspace/eslint-config": "workspace:*",
|
|
199
|
+
"@workspace/typescript-config": "workspace:*",
|
|
200
|
+
"agents": "^0.4.0",
|
|
201
|
+
"ai": "^4.3.19",
|
|
202
|
+
"bcryptjs": "^3.0.3",
|
|
203
|
+
"dotenv": "^17.2.3",
|
|
204
|
+
"eslint": "^9.18.0",
|
|
205
|
+
"tsup": "^8.4.0",
|
|
206
|
+
"typescript": "^5.9.3",
|
|
207
|
+
"vitest": "^3.0.7"
|
|
208
|
+
},
|
|
209
|
+
"keywords": [
|
|
210
|
+
"kontext",
|
|
211
|
+
"mcp",
|
|
212
|
+
"model-context-protocol",
|
|
213
|
+
"sdk",
|
|
214
|
+
"oauth"
|
|
215
|
+
],
|
|
216
|
+
"license": "MIT",
|
|
217
|
+
"repository": {
|
|
218
|
+
"type": "git",
|
|
219
|
+
"url": "https://github.com/kontext-dev/spaces"
|
|
220
|
+
}
|
|
221
|
+
}
|