astro-tokenkit 1.0.10 → 1.0.12
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 -8
- package/dist/auth/detector.js +14 -0
- package/dist/auth/manager.d.ts +9 -4
- package/dist/auth/manager.js +100 -41
- package/dist/auth/policy.js +2 -1
- package/dist/auth/storage.d.ts +2 -0
- package/dist/auth/storage.js +11 -4
- package/dist/client/client.d.ts +15 -11
- package/dist/client/client.js +33 -31
- package/dist/client/context-shared.d.ts +1 -1
- package/dist/client/context-shared.js +2 -8
- package/dist/client/context.d.ts +2 -2
- package/dist/client/context.js +6 -9
- package/dist/index.cjs +168 -84
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +65 -21
- package/dist/index.js +167 -85
- package/dist/index.js.map +1 -1
- package/dist/middleware.js +2 -2
- package/dist/types.d.ts +33 -6
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -110,6 +110,13 @@ const EXPIRES_IN_FIELDS = [
|
|
|
110
110
|
'expiresIn',
|
|
111
111
|
'ttl',
|
|
112
112
|
];
|
|
113
|
+
/**
|
|
114
|
+
* Common field names for token type
|
|
115
|
+
*/
|
|
116
|
+
const TOKEN_TYPE_FIELDS = [
|
|
117
|
+
'token_type',
|
|
118
|
+
'tokenType',
|
|
119
|
+
];
|
|
113
120
|
/**
|
|
114
121
|
* Common field names for session payload
|
|
115
122
|
*/
|
|
@@ -172,10 +179,13 @@ function autoDetectFields(body, fieldMapping) {
|
|
|
172
179
|
}
|
|
173
180
|
// Detect session payload (optional)
|
|
174
181
|
const sessionPayload = findField(SESSION_PAYLOAD_FIELDS, fieldMapping === null || fieldMapping === void 0 ? void 0 : fieldMapping.sessionPayload);
|
|
182
|
+
// Detect token type (optional)
|
|
183
|
+
const tokenType = findField(TOKEN_TYPE_FIELDS, fieldMapping === null || fieldMapping === void 0 ? void 0 : fieldMapping.tokenType);
|
|
175
184
|
return {
|
|
176
185
|
accessToken,
|
|
177
186
|
refreshToken,
|
|
178
187
|
accessExpiresAt,
|
|
188
|
+
tokenType: tokenType || undefined,
|
|
179
189
|
sessionPayload: sessionPayload || undefined,
|
|
180
190
|
};
|
|
181
191
|
}
|
|
@@ -189,6 +199,10 @@ function parseJWTPayload(token) {
|
|
|
189
199
|
return null;
|
|
190
200
|
}
|
|
191
201
|
const payload = parts[1];
|
|
202
|
+
// Better UTF-8 support for environments with Buffer (like Node.js/Astro)
|
|
203
|
+
if (typeof Buffer !== 'undefined') {
|
|
204
|
+
return JSON.parse(Buffer.from(payload, 'base64').toString('utf8'));
|
|
205
|
+
}
|
|
192
206
|
const decoded = atob(payload.replace(/-/g, '+').replace(/_/g, '/'));
|
|
193
207
|
return JSON.parse(decoded);
|
|
194
208
|
}
|
|
@@ -208,6 +222,7 @@ function getCookieNames(prefix) {
|
|
|
208
222
|
refreshToken: `${p}refresh_token`,
|
|
209
223
|
expiresAt: `${p}access_expires_at`,
|
|
210
224
|
lastRefreshAt: `${p}last_refresh_at`,
|
|
225
|
+
tokenType: `${p}token_type`,
|
|
211
226
|
};
|
|
212
227
|
}
|
|
213
228
|
/**
|
|
@@ -243,20 +258,25 @@ function storeTokens(ctx, bundle, cookieConfig = {}) {
|
|
|
243
258
|
ctx.cookies.set(names.expiresAt, bundle.accessExpiresAt.toString(), Object.assign(Object.assign({}, options), { maxAge: accessMaxAge, path: '/' }));
|
|
244
259
|
// Set last refresh timestamp
|
|
245
260
|
ctx.cookies.set(names.lastRefreshAt, now.toString(), Object.assign(Object.assign({}, options), { maxAge: accessMaxAge, path: '/' }));
|
|
261
|
+
// Set token type if available
|
|
262
|
+
if (bundle.tokenType) {
|
|
263
|
+
ctx.cookies.set(names.tokenType, bundle.tokenType, Object.assign(Object.assign({}, options), { maxAge: accessMaxAge, path: '/' }));
|
|
264
|
+
}
|
|
246
265
|
}
|
|
247
266
|
/**
|
|
248
267
|
* Retrieve tokens from cookies
|
|
249
268
|
*/
|
|
250
269
|
function retrieveTokens(ctx, cookieConfig = {}) {
|
|
251
|
-
var _a, _b, _c, _d;
|
|
270
|
+
var _a, _b, _c, _d, _e;
|
|
252
271
|
const names = getCookieNames(cookieConfig.prefix);
|
|
253
272
|
const accessToken = ((_a = ctx.cookies.get(names.accessToken)) === null || _a === void 0 ? void 0 : _a.value) || null;
|
|
254
273
|
const refreshToken = ((_b = ctx.cookies.get(names.refreshToken)) === null || _b === void 0 ? void 0 : _b.value) || null;
|
|
255
|
-
const
|
|
274
|
+
const tokenType = ((_c = ctx.cookies.get(names.tokenType)) === null || _c === void 0 ? void 0 : _c.value) || null;
|
|
275
|
+
const expiresAtStr = (_d = ctx.cookies.get(names.expiresAt)) === null || _d === void 0 ? void 0 : _d.value;
|
|
256
276
|
const expiresAt = expiresAtStr ? parseInt(expiresAtStr, 10) : null;
|
|
257
|
-
const lastRefreshAtStr = (
|
|
277
|
+
const lastRefreshAtStr = (_e = ctx.cookies.get(names.lastRefreshAt)) === null || _e === void 0 ? void 0 : _e.value;
|
|
258
278
|
const lastRefreshAt = lastRefreshAtStr ? parseInt(lastRefreshAtStr, 10) : null;
|
|
259
|
-
return { accessToken, refreshToken, expiresAt, lastRefreshAt };
|
|
279
|
+
return { accessToken, refreshToken, expiresAt, lastRefreshAt, tokenType };
|
|
260
280
|
}
|
|
261
281
|
/**
|
|
262
282
|
* Clear all auth cookies
|
|
@@ -268,6 +288,7 @@ function clearTokens(ctx, cookieConfig = {}) {
|
|
|
268
288
|
ctx.cookies.delete(names.refreshToken, Object.assign(Object.assign({}, options), { path: '/' }));
|
|
269
289
|
ctx.cookies.delete(names.expiresAt, Object.assign(Object.assign({}, options), { path: '/' }));
|
|
270
290
|
ctx.cookies.delete(names.lastRefreshAt, Object.assign(Object.assign({}, options), { path: '/' }));
|
|
291
|
+
ctx.cookies.delete(names.tokenType, Object.assign(Object.assign({}, options), { path: '/' }));
|
|
271
292
|
}
|
|
272
293
|
|
|
273
294
|
// packages/astro-tokenkit/src/utils/time.ts
|
|
@@ -369,7 +390,8 @@ function isExpired(expiresAt, now, policy = {}) {
|
|
|
369
390
|
const clockSkew = typeof normalized.clockSkew === 'number'
|
|
370
391
|
? normalized.clockSkew
|
|
371
392
|
: parseTime(normalized.clockSkew);
|
|
372
|
-
|
|
393
|
+
// Pessimistic: consider it expired if current time + skew is past expiration
|
|
394
|
+
return now + clockSkew > expiresAt;
|
|
373
395
|
}
|
|
374
396
|
|
|
375
397
|
// packages/astro-tokenkit/src/auth/manager.ts
|
|
@@ -413,18 +435,38 @@ class TokenManager {
|
|
|
413
435
|
/**
|
|
414
436
|
* Perform login
|
|
415
437
|
*/
|
|
416
|
-
login(ctx, credentials,
|
|
438
|
+
login(ctx, credentials, options) {
|
|
417
439
|
return __awaiter(this, void 0, void 0, function* () {
|
|
418
|
-
const url = this.baseURL
|
|
419
|
-
const
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
}
|
|
440
|
+
const url = this.joinURL(this.baseURL, this.config.login);
|
|
441
|
+
const contentType = this.config.contentType || 'application/json';
|
|
442
|
+
const headers = Object.assign(Object.assign({ 'Content-Type': contentType }, this.config.headers), options === null || options === void 0 ? void 0 : options.headers);
|
|
443
|
+
const data = Object.assign(Object.assign(Object.assign({}, this.config.loginData), options === null || options === void 0 ? void 0 : options.data), credentials);
|
|
444
|
+
let requestBody;
|
|
445
|
+
if (contentType === 'application/x-www-form-urlencoded') {
|
|
446
|
+
requestBody = new URLSearchParams(data).toString();
|
|
447
|
+
}
|
|
448
|
+
else {
|
|
449
|
+
requestBody = JSON.stringify(data);
|
|
450
|
+
}
|
|
451
|
+
let response;
|
|
452
|
+
try {
|
|
453
|
+
response = yield fetch(url, {
|
|
454
|
+
method: 'POST',
|
|
455
|
+
headers,
|
|
456
|
+
body: requestBody,
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
catch (error) {
|
|
460
|
+
const authError = new AuthError(`Login request failed: ${error.message}`);
|
|
461
|
+
if (options === null || options === void 0 ? void 0 : options.onError)
|
|
462
|
+
yield options.onError(authError, ctx);
|
|
463
|
+
throw authError;
|
|
464
|
+
}
|
|
426
465
|
if (!response.ok) {
|
|
427
|
-
|
|
466
|
+
const authError = new AuthError(`Login failed: ${response.status} ${response.statusText}`, response.status, response);
|
|
467
|
+
if (options === null || options === void 0 ? void 0 : options.onError)
|
|
468
|
+
yield options.onError(authError, ctx);
|
|
469
|
+
throw authError;
|
|
428
470
|
}
|
|
429
471
|
const body = yield response.json().catch(() => ({}));
|
|
430
472
|
// Parse response
|
|
@@ -435,24 +477,34 @@ class TokenManager {
|
|
|
435
477
|
: autoDetectFields(body, this.config.fields);
|
|
436
478
|
}
|
|
437
479
|
catch (error) {
|
|
438
|
-
|
|
480
|
+
const authError = new AuthError(`Invalid login response: ${error.message}`, response.status, response);
|
|
481
|
+
if (options === null || options === void 0 ? void 0 : options.onError)
|
|
482
|
+
yield options.onError(authError, ctx);
|
|
483
|
+
throw authError;
|
|
439
484
|
}
|
|
440
485
|
// Store in cookies
|
|
441
486
|
storeTokens(ctx, bundle, this.config.cookies);
|
|
442
487
|
// Call onLogin callback if provided
|
|
443
|
-
if (onLogin) {
|
|
444
|
-
yield onLogin(bundle, body, ctx);
|
|
488
|
+
if (options === null || options === void 0 ? void 0 : options.onLogin) {
|
|
489
|
+
yield options.onLogin(bundle, body, ctx);
|
|
445
490
|
}
|
|
446
|
-
return
|
|
491
|
+
return {
|
|
492
|
+
data: bundle,
|
|
493
|
+
status: response.status,
|
|
494
|
+
statusText: response.statusText,
|
|
495
|
+
headers: response.headers,
|
|
496
|
+
url: response.url,
|
|
497
|
+
ok: response.ok,
|
|
498
|
+
};
|
|
447
499
|
});
|
|
448
500
|
}
|
|
449
501
|
/**
|
|
450
502
|
* Perform token refresh
|
|
451
503
|
*/
|
|
452
|
-
refresh(ctx, refreshToken) {
|
|
504
|
+
refresh(ctx, refreshToken, options, headers) {
|
|
453
505
|
return __awaiter(this, void 0, void 0, function* () {
|
|
454
506
|
try {
|
|
455
|
-
return yield this.performRefresh(ctx, refreshToken);
|
|
507
|
+
return yield this.performRefresh(ctx, refreshToken, options, headers);
|
|
456
508
|
}
|
|
457
509
|
catch (error) {
|
|
458
510
|
clearTokens(ctx, this.config.cookies);
|
|
@@ -463,16 +515,31 @@ class TokenManager {
|
|
|
463
515
|
/**
|
|
464
516
|
* Internal refresh implementation
|
|
465
517
|
*/
|
|
466
|
-
performRefresh(ctx, refreshToken) {
|
|
518
|
+
performRefresh(ctx, refreshToken, options, extraHeaders) {
|
|
467
519
|
return __awaiter(this, void 0, void 0, function* () {
|
|
468
|
-
const url = this.baseURL
|
|
469
|
-
const
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
520
|
+
const url = this.joinURL(this.baseURL, this.config.refresh);
|
|
521
|
+
const contentType = this.config.contentType || 'application/json';
|
|
522
|
+
const headers = Object.assign(Object.assign({ 'Content-Type': contentType }, this.config.headers), extraHeaders);
|
|
523
|
+
const refreshField = this.config.refreshRequestField || 'refreshToken';
|
|
524
|
+
const data = Object.assign(Object.assign(Object.assign({}, this.config.refreshData), options === null || options === void 0 ? void 0 : options.data), { [refreshField]: refreshToken });
|
|
525
|
+
let requestBody;
|
|
526
|
+
if (contentType === 'application/x-www-form-urlencoded') {
|
|
527
|
+
requestBody = new URLSearchParams(data).toString();
|
|
528
|
+
}
|
|
529
|
+
else {
|
|
530
|
+
requestBody = JSON.stringify(data);
|
|
531
|
+
}
|
|
532
|
+
let response;
|
|
533
|
+
try {
|
|
534
|
+
response = yield fetch(url, {
|
|
535
|
+
method: 'POST',
|
|
536
|
+
headers,
|
|
537
|
+
body: requestBody,
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
catch (error) {
|
|
474
541
|
throw new AuthError(`Refresh request failed: ${error.message}`);
|
|
475
|
-
}
|
|
542
|
+
}
|
|
476
543
|
if (!response.ok) {
|
|
477
544
|
// 401/403 = invalid refresh token
|
|
478
545
|
if (response.status === 401 || response.status === 403) {
|
|
@@ -504,9 +571,9 @@ class TokenManager {
|
|
|
504
571
|
/**
|
|
505
572
|
* Ensure valid tokens (with automatic refresh)
|
|
506
573
|
*/
|
|
507
|
-
ensure(ctx) {
|
|
574
|
+
ensure(ctx, options, headers) {
|
|
508
575
|
return __awaiter(this, void 0, void 0, function* () {
|
|
509
|
-
var _a, _b, _c, _d, _e;
|
|
576
|
+
var _a, _b, _c, _d, _e, _f;
|
|
510
577
|
const now = Math.floor(Date.now() / 1000);
|
|
511
578
|
const tokens = retrieveTokens(ctx, this.config.cookies);
|
|
512
579
|
// No tokens
|
|
@@ -516,23 +583,25 @@ class TokenManager {
|
|
|
516
583
|
// Token expired
|
|
517
584
|
if (isExpired(tokens.expiresAt, now, this.config.policy)) {
|
|
518
585
|
const flightKey = this.createFlightKey(tokens.refreshToken);
|
|
519
|
-
const bundle = yield this.singleFlight.execute(flightKey, () => this.refresh(ctx, tokens.refreshToken));
|
|
586
|
+
const bundle = yield this.singleFlight.execute(flightKey, () => this.refresh(ctx, tokens.refreshToken, options, headers));
|
|
520
587
|
if (!bundle)
|
|
521
588
|
return null;
|
|
522
589
|
return {
|
|
523
590
|
accessToken: bundle.accessToken,
|
|
524
591
|
expiresAt: bundle.accessExpiresAt,
|
|
592
|
+
tokenType: bundle.tokenType,
|
|
525
593
|
payload: (_b = (_a = bundle.sessionPayload) !== null && _a !== void 0 ? _a : parseJWTPayload(bundle.accessToken)) !== null && _b !== void 0 ? _b : undefined,
|
|
526
594
|
};
|
|
527
595
|
}
|
|
528
596
|
// Proactive refresh
|
|
529
597
|
if (shouldRefresh(tokens.expiresAt, now, tokens.lastRefreshAt, this.config.policy)) {
|
|
530
598
|
const flightKey = this.createFlightKey(tokens.refreshToken);
|
|
531
|
-
const bundle = yield this.singleFlight.execute(flightKey, () => this.refresh(ctx, tokens.refreshToken));
|
|
599
|
+
const bundle = yield this.singleFlight.execute(flightKey, () => this.refresh(ctx, tokens.refreshToken, options, headers));
|
|
532
600
|
if (bundle) {
|
|
533
601
|
return {
|
|
534
602
|
accessToken: bundle.accessToken,
|
|
535
603
|
expiresAt: bundle.accessExpiresAt,
|
|
604
|
+
tokenType: bundle.tokenType,
|
|
536
605
|
payload: (_d = (_c = bundle.sessionPayload) !== null && _c !== void 0 ? _c : parseJWTPayload(bundle.accessToken)) !== null && _d !== void 0 ? _d : undefined,
|
|
537
606
|
};
|
|
538
607
|
}
|
|
@@ -546,7 +615,8 @@ class TokenManager {
|
|
|
546
615
|
return {
|
|
547
616
|
accessToken: tokens.accessToken,
|
|
548
617
|
expiresAt: tokens.expiresAt,
|
|
549
|
-
|
|
618
|
+
tokenType: (_e = tokens.tokenType) !== null && _e !== void 0 ? _e : undefined,
|
|
619
|
+
payload: (_f = parseJWTPayload(tokens.accessToken)) !== null && _f !== void 0 ? _f : undefined,
|
|
550
620
|
};
|
|
551
621
|
});
|
|
552
622
|
}
|
|
@@ -555,15 +625,22 @@ class TokenManager {
|
|
|
555
625
|
*/
|
|
556
626
|
logout(ctx) {
|
|
557
627
|
return __awaiter(this, void 0, void 0, function* () {
|
|
628
|
+
var _a;
|
|
558
629
|
// Optionally call logout endpoint
|
|
559
630
|
if (this.config.logout) {
|
|
560
631
|
try {
|
|
561
|
-
const url = this.baseURL
|
|
562
|
-
|
|
632
|
+
const url = this.joinURL(this.baseURL, this.config.logout);
|
|
633
|
+
const session = this.getSession(ctx);
|
|
634
|
+
const headers = {};
|
|
635
|
+
if (session === null || session === void 0 ? void 0 : session.accessToken) {
|
|
636
|
+
const injectFn = (_a = this.config.injectToken) !== null && _a !== void 0 ? _a : ((token, type) => `${type !== null && type !== void 0 ? type : 'Bearer'} ${token}`);
|
|
637
|
+
headers['Authorization'] = injectFn(session.accessToken, session.tokenType);
|
|
638
|
+
}
|
|
639
|
+
yield fetch(url, { method: 'POST', headers });
|
|
563
640
|
}
|
|
564
641
|
catch (error) {
|
|
565
642
|
// Ignore logout endpoint errors
|
|
566
|
-
console.warn('Logout endpoint failed:', error);
|
|
643
|
+
console.warn('[TokenKit] Logout endpoint failed:', error);
|
|
567
644
|
}
|
|
568
645
|
}
|
|
569
646
|
clearTokens(ctx, this.config.cookies);
|
|
@@ -573,7 +650,7 @@ class TokenManager {
|
|
|
573
650
|
* Get current session (no refresh)
|
|
574
651
|
*/
|
|
575
652
|
getSession(ctx) {
|
|
576
|
-
var _a;
|
|
653
|
+
var _a, _b;
|
|
577
654
|
const tokens = retrieveTokens(ctx, this.config.cookies);
|
|
578
655
|
if (!tokens.accessToken || !tokens.expiresAt) {
|
|
579
656
|
return null;
|
|
@@ -581,7 +658,8 @@ class TokenManager {
|
|
|
581
658
|
return {
|
|
582
659
|
accessToken: tokens.accessToken,
|
|
583
660
|
expiresAt: tokens.expiresAt,
|
|
584
|
-
|
|
661
|
+
tokenType: (_a = tokens.tokenType) !== null && _a !== void 0 ? _a : undefined,
|
|
662
|
+
payload: (_b = parseJWTPayload(tokens.accessToken)) !== null && _b !== void 0 ? _b : undefined,
|
|
585
663
|
};
|
|
586
664
|
}
|
|
587
665
|
/**
|
|
@@ -595,13 +673,16 @@ class TokenManager {
|
|
|
595
673
|
* Create flight key for single-flight deduplication
|
|
596
674
|
*/
|
|
597
675
|
createFlightKey(token) {
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
676
|
+
// Avoid weak hashing of sensitive tokens
|
|
677
|
+
return `refresh_${token}`;
|
|
678
|
+
}
|
|
679
|
+
/**
|
|
680
|
+
* Join base URL and path safely
|
|
681
|
+
*/
|
|
682
|
+
joinURL(base, path) {
|
|
683
|
+
const b = base.endsWith('/') ? base : base + '/';
|
|
684
|
+
const p = path.startsWith('/') ? path.slice(1) : path;
|
|
685
|
+
return b + p;
|
|
605
686
|
}
|
|
606
687
|
}
|
|
607
688
|
|
|
@@ -667,20 +748,17 @@ function runWithContext(ctx, fn) {
|
|
|
667
748
|
/**
|
|
668
749
|
* Get current Astro context (from middleware binding or explicit)
|
|
669
750
|
*/
|
|
670
|
-
function getContextStore(
|
|
751
|
+
function getContextStore() {
|
|
671
752
|
const config = getConfig();
|
|
672
753
|
const getStore = config.getContextStore;
|
|
673
754
|
const context = config.context || als;
|
|
674
755
|
const store = getStore
|
|
675
756
|
? getStore()
|
|
676
757
|
: context.getStore();
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
throw new Error('Astro context not found. Either:\n' +
|
|
680
|
-
'1. Use api.middleware() to bind context automatically, or\n' +
|
|
681
|
-
'2. Pass context explicitly: api.get("/path", { ctx: Astro })');
|
|
758
|
+
if (!store) {
|
|
759
|
+
throw new Error('Astro context not found. Make sure to use api.middleware() to bind context automatically.');
|
|
682
760
|
}
|
|
683
|
-
return
|
|
761
|
+
return store;
|
|
684
762
|
}
|
|
685
763
|
|
|
686
764
|
// packages/astro-tokenkit/src/utils/retry.ts
|
|
@@ -742,8 +820,8 @@ function createMiddleware() {
|
|
|
742
820
|
yield tokenManager.ensure(ctx);
|
|
743
821
|
}
|
|
744
822
|
catch (error) {
|
|
745
|
-
// Log
|
|
746
|
-
console.error('[TokenKit] Automatic token rotation failed:', error);
|
|
823
|
+
// Log only the message to avoid leaking sensitive data in the error object
|
|
824
|
+
console.error('[TokenKit] Automatic token rotation failed:', error.message || error);
|
|
747
825
|
}
|
|
748
826
|
}
|
|
749
827
|
return next();
|
|
@@ -867,13 +945,12 @@ class APIClient {
|
|
|
867
945
|
*/
|
|
868
946
|
request(config) {
|
|
869
947
|
return __awaiter(this, void 0, void 0, function* () {
|
|
870
|
-
const ctx = getContextStore(
|
|
948
|
+
const ctx = getContextStore();
|
|
871
949
|
let attempt = 0;
|
|
872
950
|
while (true) {
|
|
873
951
|
attempt++;
|
|
874
952
|
try {
|
|
875
|
-
|
|
876
|
-
return response.data;
|
|
953
|
+
return yield this.executeRequest(config, ctx, attempt);
|
|
877
954
|
}
|
|
878
955
|
catch (error) {
|
|
879
956
|
// Check if we should retry
|
|
@@ -896,12 +973,12 @@ class APIClient {
|
|
|
896
973
|
var _a, _b, _c, _d, _e;
|
|
897
974
|
// Ensure valid session (if auth is enabled)
|
|
898
975
|
if (this.tokenManager && !config.skipAuth) {
|
|
899
|
-
yield this.tokenManager.ensure(ctx);
|
|
976
|
+
yield this.tokenManager.ensure(ctx, config.auth, config.headers);
|
|
900
977
|
}
|
|
901
978
|
// Build full URL
|
|
902
979
|
const fullURL = this.buildURL(config.url, config.params);
|
|
903
980
|
// Build headers
|
|
904
|
-
const headers = this.buildHeaders(config, ctx);
|
|
981
|
+
const headers = this.buildHeaders(config, ctx, fullURL);
|
|
905
982
|
// Build request init
|
|
906
983
|
const init = {
|
|
907
984
|
method: config.method,
|
|
@@ -929,7 +1006,7 @@ class APIClient {
|
|
|
929
1006
|
// Handle 401 (try refresh and retry once)
|
|
930
1007
|
if (response.status === 401 && this.tokenManager && !config.skipAuth && attempt === 1) {
|
|
931
1008
|
// Clear and try fresh session
|
|
932
|
-
const session = yield this.tokenManager.ensure(ctx);
|
|
1009
|
+
const session = yield this.tokenManager.ensure(ctx, config.auth, config.headers);
|
|
933
1010
|
if (session) {
|
|
934
1011
|
// Retry with new token
|
|
935
1012
|
return this.executeRequest(config, ctx, attempt + 1);
|
|
@@ -998,6 +1075,7 @@ class APIClient {
|
|
|
998
1075
|
statusText: response.statusText,
|
|
999
1076
|
headers: response.headers,
|
|
1000
1077
|
url,
|
|
1078
|
+
ok: response.ok,
|
|
1001
1079
|
};
|
|
1002
1080
|
});
|
|
1003
1081
|
}
|
|
@@ -1019,19 +1097,33 @@ class APIClient {
|
|
|
1019
1097
|
/**
|
|
1020
1098
|
* Build request headers
|
|
1021
1099
|
*/
|
|
1022
|
-
buildHeaders(config, ctx) {
|
|
1100
|
+
buildHeaders(config, ctx, targetURL) {
|
|
1023
1101
|
var _a, _b;
|
|
1024
1102
|
const headers = Object.assign(Object.assign({ 'Content-Type': 'application/json' }, this.config.headers), config.headers);
|
|
1025
|
-
// Add auth token if available
|
|
1026
|
-
if (this.tokenManager && !config.skipAuth) {
|
|
1103
|
+
// Add auth token if available (only for safe URLs)
|
|
1104
|
+
if (this.tokenManager && !config.skipAuth && this.isSafeURL(targetURL)) {
|
|
1027
1105
|
const session = this.tokenManager.getSession(ctx);
|
|
1028
1106
|
if (session === null || session === void 0 ? void 0 : session.accessToken) {
|
|
1029
|
-
const injectFn = (_b = (_a = this.config.auth) === null || _a === void 0 ? void 0 : _a.injectToken) !== null && _b !== void 0 ? _b : ((token) =>
|
|
1030
|
-
headers['Authorization'] = injectFn(session.accessToken);
|
|
1107
|
+
const injectFn = (_b = (_a = this.config.auth) === null || _a === void 0 ? void 0 : _a.injectToken) !== null && _b !== void 0 ? _b : ((token, type) => `${type !== null && type !== void 0 ? type : 'Bearer'} ${token}`);
|
|
1108
|
+
headers['Authorization'] = injectFn(session.accessToken, session.tokenType);
|
|
1031
1109
|
}
|
|
1032
1110
|
}
|
|
1033
1111
|
return headers;
|
|
1034
1112
|
}
|
|
1113
|
+
/**
|
|
1114
|
+
* Check if a URL is safe for token injection (same origin as baseURL)
|
|
1115
|
+
*/
|
|
1116
|
+
isSafeURL(url) {
|
|
1117
|
+
try {
|
|
1118
|
+
const requestUrl = new URL(url, this.config.baseURL);
|
|
1119
|
+
const baseUrl = new URL(this.config.baseURL || 'http://localhost');
|
|
1120
|
+
return requestUrl.origin === baseUrl.origin;
|
|
1121
|
+
}
|
|
1122
|
+
catch (_a) {
|
|
1123
|
+
// Only allow relative paths if baseURL is missing or invalid
|
|
1124
|
+
return !url.startsWith('http') && !url.startsWith('//');
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1035
1127
|
/**
|
|
1036
1128
|
* Login
|
|
1037
1129
|
*/
|
|
@@ -1040,48 +1132,38 @@ class APIClient {
|
|
|
1040
1132
|
if (!this.tokenManager) {
|
|
1041
1133
|
throw new Error('Auth is not configured for this client');
|
|
1042
1134
|
}
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
if (options && 'cookies' in options) {
|
|
1046
|
-
ctx = options;
|
|
1047
|
-
}
|
|
1048
|
-
else if (options) {
|
|
1049
|
-
const opt = options;
|
|
1050
|
-
ctx = opt.ctx;
|
|
1051
|
-
onLogin = opt.onLogin;
|
|
1052
|
-
}
|
|
1053
|
-
const context = getContextStore(ctx);
|
|
1054
|
-
yield this.tokenManager.login(context, credentials, onLogin);
|
|
1135
|
+
const context = getContextStore();
|
|
1136
|
+
return this.tokenManager.login(context, credentials, options);
|
|
1055
1137
|
});
|
|
1056
1138
|
}
|
|
1057
1139
|
/**
|
|
1058
1140
|
* Logout
|
|
1059
1141
|
*/
|
|
1060
|
-
logout(
|
|
1142
|
+
logout() {
|
|
1061
1143
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1062
1144
|
if (!this.tokenManager) {
|
|
1063
1145
|
throw new Error('Auth is not configured for this client');
|
|
1064
1146
|
}
|
|
1065
|
-
const context = getContextStore(
|
|
1147
|
+
const context = getContextStore();
|
|
1066
1148
|
yield this.tokenManager.logout(context);
|
|
1067
1149
|
});
|
|
1068
1150
|
}
|
|
1069
1151
|
/**
|
|
1070
1152
|
* Check if authenticated
|
|
1071
1153
|
*/
|
|
1072
|
-
isAuthenticated(
|
|
1154
|
+
isAuthenticated() {
|
|
1073
1155
|
if (!this.tokenManager)
|
|
1074
1156
|
return false;
|
|
1075
|
-
const context = getContextStore(
|
|
1157
|
+
const context = getContextStore();
|
|
1076
1158
|
return this.tokenManager.isAuthenticated(context);
|
|
1077
1159
|
}
|
|
1078
1160
|
/**
|
|
1079
1161
|
* Get current session
|
|
1080
1162
|
*/
|
|
1081
|
-
getSession(
|
|
1163
|
+
getSession() {
|
|
1082
1164
|
if (!this.tokenManager)
|
|
1083
1165
|
return null;
|
|
1084
|
-
const context = getContextStore(
|
|
1166
|
+
const context = getContextStore();
|
|
1085
1167
|
return this.tokenManager.getSession(context);
|
|
1086
1168
|
}
|
|
1087
1169
|
}
|
|
@@ -1144,5 +1226,5 @@ function tokenKit(config) {
|
|
|
1144
1226
|
*/
|
|
1145
1227
|
const defineMiddleware = () => createMiddleware();
|
|
1146
1228
|
|
|
1147
|
-
export { APIClient, APIError, AuthError, NetworkError, TimeoutError, api, createClient, createMiddleware, defineMiddleware, formatTime, getConfig, getTokenManager, parseTime, setConfig, setTokenManager, tokenKit };
|
|
1229
|
+
export { APIClient, APIError, AuthError, NetworkError, TimeoutError, api, createClient, createMiddleware, defineMiddleware, formatTime, getConfig, getContextStore, getTokenManager, parseTime, runWithContext, setConfig, setTokenManager, tokenKit };
|
|
1148
1230
|
//# sourceMappingURL=index.js.map
|