astro-tokenkit 1.0.11 → 1.0.13
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 +47 -6
- package/dist/auth/detector.js +14 -0
- package/dist/auth/manager.d.ts +6 -1
- package/dist/auth/manager.js +70 -32
- 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 +12 -8
- package/dist/client/client.js +23 -11
- package/dist/config.js +34 -14
- package/dist/index.cjs +170 -64
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +28 -10
- package/dist/index.js +170 -64
- package/dist/index.js.map +1 -1
- package/dist/integration.js +14 -2
- package/dist/middleware.js +2 -2
- package/dist/types.d.ts +11 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -112,6 +112,13 @@ const EXPIRES_IN_FIELDS = [
|
|
|
112
112
|
'expiresIn',
|
|
113
113
|
'ttl',
|
|
114
114
|
];
|
|
115
|
+
/**
|
|
116
|
+
* Common field names for token type
|
|
117
|
+
*/
|
|
118
|
+
const TOKEN_TYPE_FIELDS = [
|
|
119
|
+
'token_type',
|
|
120
|
+
'tokenType',
|
|
121
|
+
];
|
|
115
122
|
/**
|
|
116
123
|
* Common field names for session payload
|
|
117
124
|
*/
|
|
@@ -174,10 +181,13 @@ function autoDetectFields(body, fieldMapping) {
|
|
|
174
181
|
}
|
|
175
182
|
// Detect session payload (optional)
|
|
176
183
|
const sessionPayload = findField(SESSION_PAYLOAD_FIELDS, fieldMapping === null || fieldMapping === void 0 ? void 0 : fieldMapping.sessionPayload);
|
|
184
|
+
// Detect token type (optional)
|
|
185
|
+
const tokenType = findField(TOKEN_TYPE_FIELDS, fieldMapping === null || fieldMapping === void 0 ? void 0 : fieldMapping.tokenType);
|
|
177
186
|
return {
|
|
178
187
|
accessToken,
|
|
179
188
|
refreshToken,
|
|
180
189
|
accessExpiresAt,
|
|
190
|
+
tokenType: tokenType || undefined,
|
|
181
191
|
sessionPayload: sessionPayload || undefined,
|
|
182
192
|
};
|
|
183
193
|
}
|
|
@@ -191,6 +201,10 @@ function parseJWTPayload(token) {
|
|
|
191
201
|
return null;
|
|
192
202
|
}
|
|
193
203
|
const payload = parts[1];
|
|
204
|
+
// Better UTF-8 support for environments with Buffer (like Node.js/Astro)
|
|
205
|
+
if (typeof Buffer !== 'undefined') {
|
|
206
|
+
return JSON.parse(Buffer.from(payload, 'base64').toString('utf8'));
|
|
207
|
+
}
|
|
194
208
|
const decoded = atob(payload.replace(/-/g, '+').replace(/_/g, '/'));
|
|
195
209
|
return JSON.parse(decoded);
|
|
196
210
|
}
|
|
@@ -210,6 +224,7 @@ function getCookieNames(prefix) {
|
|
|
210
224
|
refreshToken: `${p}refresh_token`,
|
|
211
225
|
expiresAt: `${p}access_expires_at`,
|
|
212
226
|
lastRefreshAt: `${p}last_refresh_at`,
|
|
227
|
+
tokenType: `${p}token_type`,
|
|
213
228
|
};
|
|
214
229
|
}
|
|
215
230
|
/**
|
|
@@ -245,20 +260,25 @@ function storeTokens(ctx, bundle, cookieConfig = {}) {
|
|
|
245
260
|
ctx.cookies.set(names.expiresAt, bundle.accessExpiresAt.toString(), Object.assign(Object.assign({}, options), { maxAge: accessMaxAge, path: '/' }));
|
|
246
261
|
// Set last refresh timestamp
|
|
247
262
|
ctx.cookies.set(names.lastRefreshAt, now.toString(), Object.assign(Object.assign({}, options), { maxAge: accessMaxAge, path: '/' }));
|
|
263
|
+
// Set token type if available
|
|
264
|
+
if (bundle.tokenType) {
|
|
265
|
+
ctx.cookies.set(names.tokenType, bundle.tokenType, Object.assign(Object.assign({}, options), { maxAge: accessMaxAge, path: '/' }));
|
|
266
|
+
}
|
|
248
267
|
}
|
|
249
268
|
/**
|
|
250
269
|
* Retrieve tokens from cookies
|
|
251
270
|
*/
|
|
252
271
|
function retrieveTokens(ctx, cookieConfig = {}) {
|
|
253
|
-
var _a, _b, _c, _d;
|
|
272
|
+
var _a, _b, _c, _d, _e;
|
|
254
273
|
const names = getCookieNames(cookieConfig.prefix);
|
|
255
274
|
const accessToken = ((_a = ctx.cookies.get(names.accessToken)) === null || _a === void 0 ? void 0 : _a.value) || null;
|
|
256
275
|
const refreshToken = ((_b = ctx.cookies.get(names.refreshToken)) === null || _b === void 0 ? void 0 : _b.value) || null;
|
|
257
|
-
const
|
|
276
|
+
const tokenType = ((_c = ctx.cookies.get(names.tokenType)) === null || _c === void 0 ? void 0 : _c.value) || null;
|
|
277
|
+
const expiresAtStr = (_d = ctx.cookies.get(names.expiresAt)) === null || _d === void 0 ? void 0 : _d.value;
|
|
258
278
|
const expiresAt = expiresAtStr ? parseInt(expiresAtStr, 10) : null;
|
|
259
|
-
const lastRefreshAtStr = (
|
|
279
|
+
const lastRefreshAtStr = (_e = ctx.cookies.get(names.lastRefreshAt)) === null || _e === void 0 ? void 0 : _e.value;
|
|
260
280
|
const lastRefreshAt = lastRefreshAtStr ? parseInt(lastRefreshAtStr, 10) : null;
|
|
261
|
-
return { accessToken, refreshToken, expiresAt, lastRefreshAt };
|
|
281
|
+
return { accessToken, refreshToken, expiresAt, lastRefreshAt, tokenType };
|
|
262
282
|
}
|
|
263
283
|
/**
|
|
264
284
|
* Clear all auth cookies
|
|
@@ -270,6 +290,7 @@ function clearTokens(ctx, cookieConfig = {}) {
|
|
|
270
290
|
ctx.cookies.delete(names.refreshToken, Object.assign(Object.assign({}, options), { path: '/' }));
|
|
271
291
|
ctx.cookies.delete(names.expiresAt, Object.assign(Object.assign({}, options), { path: '/' }));
|
|
272
292
|
ctx.cookies.delete(names.lastRefreshAt, Object.assign(Object.assign({}, options), { path: '/' }));
|
|
293
|
+
ctx.cookies.delete(names.tokenType, Object.assign(Object.assign({}, options), { path: '/' }));
|
|
273
294
|
}
|
|
274
295
|
|
|
275
296
|
// packages/astro-tokenkit/src/utils/time.ts
|
|
@@ -371,7 +392,8 @@ function isExpired(expiresAt, now, policy = {}) {
|
|
|
371
392
|
const clockSkew = typeof normalized.clockSkew === 'number'
|
|
372
393
|
? normalized.clockSkew
|
|
373
394
|
: parseTime(normalized.clockSkew);
|
|
374
|
-
|
|
395
|
+
// Pessimistic: consider it expired if current time + skew is past expiration
|
|
396
|
+
return now + clockSkew > expiresAt;
|
|
375
397
|
}
|
|
376
398
|
|
|
377
399
|
// packages/astro-tokenkit/src/auth/manager.ts
|
|
@@ -417,7 +439,7 @@ class TokenManager {
|
|
|
417
439
|
*/
|
|
418
440
|
login(ctx, credentials, options) {
|
|
419
441
|
return __awaiter(this, void 0, void 0, function* () {
|
|
420
|
-
const url = this.baseURL
|
|
442
|
+
const url = this.joinURL(this.baseURL, this.config.login);
|
|
421
443
|
const contentType = this.config.contentType || 'application/json';
|
|
422
444
|
const headers = Object.assign(Object.assign({ 'Content-Type': contentType }, this.config.headers), options === null || options === void 0 ? void 0 : options.headers);
|
|
423
445
|
const data = Object.assign(Object.assign(Object.assign({}, this.config.loginData), options === null || options === void 0 ? void 0 : options.data), credentials);
|
|
@@ -428,15 +450,25 @@ class TokenManager {
|
|
|
428
450
|
else {
|
|
429
451
|
requestBody = JSON.stringify(data);
|
|
430
452
|
}
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
453
|
+
let response;
|
|
454
|
+
try {
|
|
455
|
+
response = yield fetch(url, {
|
|
456
|
+
method: 'POST',
|
|
457
|
+
headers,
|
|
458
|
+
body: requestBody,
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
catch (error) {
|
|
462
|
+
const authError = new AuthError(`Login request failed: ${error.message}`);
|
|
463
|
+
if (options === null || options === void 0 ? void 0 : options.onError)
|
|
464
|
+
yield options.onError(authError, ctx);
|
|
465
|
+
throw authError;
|
|
466
|
+
}
|
|
438
467
|
if (!response.ok) {
|
|
439
|
-
|
|
468
|
+
const authError = new AuthError(`Login failed: ${response.status} ${response.statusText}`, response.status, response);
|
|
469
|
+
if (options === null || options === void 0 ? void 0 : options.onError)
|
|
470
|
+
yield options.onError(authError, ctx);
|
|
471
|
+
throw authError;
|
|
440
472
|
}
|
|
441
473
|
const body = yield response.json().catch(() => ({}));
|
|
442
474
|
// Parse response
|
|
@@ -447,7 +479,10 @@ class TokenManager {
|
|
|
447
479
|
: autoDetectFields(body, this.config.fields);
|
|
448
480
|
}
|
|
449
481
|
catch (error) {
|
|
450
|
-
|
|
482
|
+
const authError = new AuthError(`Invalid login response: ${error.message}`, response.status, response);
|
|
483
|
+
if (options === null || options === void 0 ? void 0 : options.onError)
|
|
484
|
+
yield options.onError(authError, ctx);
|
|
485
|
+
throw authError;
|
|
451
486
|
}
|
|
452
487
|
// Store in cookies
|
|
453
488
|
storeTokens(ctx, bundle, this.config.cookies);
|
|
@@ -455,7 +490,14 @@ class TokenManager {
|
|
|
455
490
|
if (options === null || options === void 0 ? void 0 : options.onLogin) {
|
|
456
491
|
yield options.onLogin(bundle, body, ctx);
|
|
457
492
|
}
|
|
458
|
-
return
|
|
493
|
+
return {
|
|
494
|
+
data: bundle,
|
|
495
|
+
status: response.status,
|
|
496
|
+
statusText: response.statusText,
|
|
497
|
+
headers: response.headers,
|
|
498
|
+
url: response.url,
|
|
499
|
+
ok: response.ok,
|
|
500
|
+
};
|
|
459
501
|
});
|
|
460
502
|
}
|
|
461
503
|
/**
|
|
@@ -477,7 +519,7 @@ class TokenManager {
|
|
|
477
519
|
*/
|
|
478
520
|
performRefresh(ctx, refreshToken, options, extraHeaders) {
|
|
479
521
|
return __awaiter(this, void 0, void 0, function* () {
|
|
480
|
-
const url = this.baseURL
|
|
522
|
+
const url = this.joinURL(this.baseURL, this.config.refresh);
|
|
481
523
|
const contentType = this.config.contentType || 'application/json';
|
|
482
524
|
const headers = Object.assign(Object.assign({ 'Content-Type': contentType }, this.config.headers), extraHeaders);
|
|
483
525
|
const refreshField = this.config.refreshRequestField || 'refreshToken';
|
|
@@ -489,13 +531,17 @@ class TokenManager {
|
|
|
489
531
|
else {
|
|
490
532
|
requestBody = JSON.stringify(data);
|
|
491
533
|
}
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
534
|
+
let response;
|
|
535
|
+
try {
|
|
536
|
+
response = yield fetch(url, {
|
|
537
|
+
method: 'POST',
|
|
538
|
+
headers,
|
|
539
|
+
body: requestBody,
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
catch (error) {
|
|
497
543
|
throw new AuthError(`Refresh request failed: ${error.message}`);
|
|
498
|
-
}
|
|
544
|
+
}
|
|
499
545
|
if (!response.ok) {
|
|
500
546
|
// 401/403 = invalid refresh token
|
|
501
547
|
if (response.status === 401 || response.status === 403) {
|
|
@@ -529,7 +575,7 @@ class TokenManager {
|
|
|
529
575
|
*/
|
|
530
576
|
ensure(ctx, options, headers) {
|
|
531
577
|
return __awaiter(this, void 0, void 0, function* () {
|
|
532
|
-
var _a, _b, _c, _d, _e;
|
|
578
|
+
var _a, _b, _c, _d, _e, _f;
|
|
533
579
|
const now = Math.floor(Date.now() / 1000);
|
|
534
580
|
const tokens = retrieveTokens(ctx, this.config.cookies);
|
|
535
581
|
// No tokens
|
|
@@ -545,6 +591,7 @@ class TokenManager {
|
|
|
545
591
|
return {
|
|
546
592
|
accessToken: bundle.accessToken,
|
|
547
593
|
expiresAt: bundle.accessExpiresAt,
|
|
594
|
+
tokenType: bundle.tokenType,
|
|
548
595
|
payload: (_b = (_a = bundle.sessionPayload) !== null && _a !== void 0 ? _a : parseJWTPayload(bundle.accessToken)) !== null && _b !== void 0 ? _b : undefined,
|
|
549
596
|
};
|
|
550
597
|
}
|
|
@@ -556,6 +603,7 @@ class TokenManager {
|
|
|
556
603
|
return {
|
|
557
604
|
accessToken: bundle.accessToken,
|
|
558
605
|
expiresAt: bundle.accessExpiresAt,
|
|
606
|
+
tokenType: bundle.tokenType,
|
|
559
607
|
payload: (_d = (_c = bundle.sessionPayload) !== null && _c !== void 0 ? _c : parseJWTPayload(bundle.accessToken)) !== null && _d !== void 0 ? _d : undefined,
|
|
560
608
|
};
|
|
561
609
|
}
|
|
@@ -569,7 +617,8 @@ class TokenManager {
|
|
|
569
617
|
return {
|
|
570
618
|
accessToken: tokens.accessToken,
|
|
571
619
|
expiresAt: tokens.expiresAt,
|
|
572
|
-
|
|
620
|
+
tokenType: (_e = tokens.tokenType) !== null && _e !== void 0 ? _e : undefined,
|
|
621
|
+
payload: (_f = parseJWTPayload(tokens.accessToken)) !== null && _f !== void 0 ? _f : undefined,
|
|
573
622
|
};
|
|
574
623
|
});
|
|
575
624
|
}
|
|
@@ -578,15 +627,22 @@ class TokenManager {
|
|
|
578
627
|
*/
|
|
579
628
|
logout(ctx) {
|
|
580
629
|
return __awaiter(this, void 0, void 0, function* () {
|
|
630
|
+
var _a;
|
|
581
631
|
// Optionally call logout endpoint
|
|
582
632
|
if (this.config.logout) {
|
|
583
633
|
try {
|
|
584
|
-
const url = this.baseURL
|
|
585
|
-
|
|
634
|
+
const url = this.joinURL(this.baseURL, this.config.logout);
|
|
635
|
+
const session = this.getSession(ctx);
|
|
636
|
+
const headers = {};
|
|
637
|
+
if (session === null || session === void 0 ? void 0 : session.accessToken) {
|
|
638
|
+
const injectFn = (_a = this.config.injectToken) !== null && _a !== void 0 ? _a : ((token, type) => `${type !== null && type !== void 0 ? type : 'Bearer'} ${token}`);
|
|
639
|
+
headers['Authorization'] = injectFn(session.accessToken, session.tokenType);
|
|
640
|
+
}
|
|
641
|
+
yield fetch(url, { method: 'POST', headers });
|
|
586
642
|
}
|
|
587
643
|
catch (error) {
|
|
588
644
|
// Ignore logout endpoint errors
|
|
589
|
-
console.warn('Logout endpoint failed:', error);
|
|
645
|
+
console.warn('[TokenKit] Logout endpoint failed:', error);
|
|
590
646
|
}
|
|
591
647
|
}
|
|
592
648
|
clearTokens(ctx, this.config.cookies);
|
|
@@ -596,7 +652,7 @@ class TokenManager {
|
|
|
596
652
|
* Get current session (no refresh)
|
|
597
653
|
*/
|
|
598
654
|
getSession(ctx) {
|
|
599
|
-
var _a;
|
|
655
|
+
var _a, _b;
|
|
600
656
|
const tokens = retrieveTokens(ctx, this.config.cookies);
|
|
601
657
|
if (!tokens.accessToken || !tokens.expiresAt) {
|
|
602
658
|
return null;
|
|
@@ -604,7 +660,8 @@ class TokenManager {
|
|
|
604
660
|
return {
|
|
605
661
|
accessToken: tokens.accessToken,
|
|
606
662
|
expiresAt: tokens.expiresAt,
|
|
607
|
-
|
|
663
|
+
tokenType: (_a = tokens.tokenType) !== null && _a !== void 0 ? _a : undefined,
|
|
664
|
+
payload: (_b = parseJWTPayload(tokens.accessToken)) !== null && _b !== void 0 ? _b : undefined,
|
|
608
665
|
};
|
|
609
666
|
}
|
|
610
667
|
/**
|
|
@@ -618,57 +675,80 @@ class TokenManager {
|
|
|
618
675
|
* Create flight key for single-flight deduplication
|
|
619
676
|
*/
|
|
620
677
|
createFlightKey(token) {
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
678
|
+
// Avoid weak hashing of sensitive tokens
|
|
679
|
+
return `refresh_${token}`;
|
|
680
|
+
}
|
|
681
|
+
/**
|
|
682
|
+
* Join base URL and path safely
|
|
683
|
+
*/
|
|
684
|
+
joinURL(base, path) {
|
|
685
|
+
const b = base.endsWith('/') ? base : base + '/';
|
|
686
|
+
const p = path.startsWith('/') ? path.slice(1) : path;
|
|
687
|
+
return b + p;
|
|
628
688
|
}
|
|
629
689
|
}
|
|
630
690
|
|
|
631
691
|
// packages/astro-tokenkit/src/config.ts
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
692
|
+
const CONFIG_KEY = Symbol.for('astro-tokenkit.config');
|
|
693
|
+
const MANAGER_KEY = Symbol.for('astro-tokenkit.manager');
|
|
694
|
+
const globalStorage = globalThis;
|
|
695
|
+
// Initialize global storage if not present
|
|
696
|
+
if (!globalStorage[CONFIG_KEY]) {
|
|
697
|
+
globalStorage[CONFIG_KEY] = {
|
|
698
|
+
runWithContext: undefined,
|
|
699
|
+
getContextStore: undefined,
|
|
700
|
+
setContextStore: undefined,
|
|
701
|
+
baseURL: "",
|
|
702
|
+
};
|
|
703
|
+
}
|
|
639
704
|
/**
|
|
640
705
|
* Set configuration
|
|
641
706
|
*/
|
|
642
707
|
function setConfig(userConfig) {
|
|
643
|
-
const
|
|
708
|
+
const currentConfig = globalStorage[CONFIG_KEY];
|
|
709
|
+
const finalConfig = Object.assign(Object.assign({}, currentConfig), userConfig);
|
|
644
710
|
// Validate that getter and setter are defined together
|
|
645
711
|
if ((finalConfig.getContextStore && !finalConfig.setContextStore) ||
|
|
646
712
|
(!finalConfig.getContextStore && finalConfig.setContextStore)) {
|
|
647
713
|
throw new Error("[TokenKit] getContextStore and setContextStore must be defined together.");
|
|
648
714
|
}
|
|
649
|
-
|
|
715
|
+
globalStorage[CONFIG_KEY] = finalConfig;
|
|
650
716
|
// Re-initialize global token manager if auth changed
|
|
651
|
-
if (
|
|
652
|
-
|
|
717
|
+
if (finalConfig.auth) {
|
|
718
|
+
globalStorage[MANAGER_KEY] = new TokenManager(finalConfig.auth, finalConfig.baseURL);
|
|
719
|
+
}
|
|
720
|
+
else {
|
|
721
|
+
globalStorage[MANAGER_KEY] = undefined;
|
|
653
722
|
}
|
|
654
723
|
}
|
|
655
724
|
/**
|
|
656
725
|
* Get current configuration
|
|
657
726
|
*/
|
|
658
727
|
function getConfig() {
|
|
659
|
-
return
|
|
728
|
+
return globalStorage[CONFIG_KEY];
|
|
660
729
|
}
|
|
661
730
|
/**
|
|
662
731
|
* Get global token manager
|
|
663
732
|
*/
|
|
664
733
|
function getTokenManager() {
|
|
665
|
-
return
|
|
734
|
+
return globalStorage[MANAGER_KEY];
|
|
666
735
|
}
|
|
667
736
|
/**
|
|
668
737
|
* Set global token manager (mainly for testing)
|
|
669
738
|
*/
|
|
670
739
|
function setTokenManager(manager) {
|
|
671
|
-
|
|
740
|
+
globalStorage[MANAGER_KEY] = manager;
|
|
741
|
+
}
|
|
742
|
+
// Handle injected configuration from Astro integration
|
|
743
|
+
try {
|
|
744
|
+
// @ts-ignore
|
|
745
|
+
const injectedConfig = typeof __TOKENKIT_CONFIG__ !== 'undefined' ? __TOKENKIT_CONFIG__ : undefined;
|
|
746
|
+
if (injectedConfig) {
|
|
747
|
+
setConfig(injectedConfig);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
catch (e) {
|
|
751
|
+
// Ignore errors in environments where __TOKENKIT_CONFIG__ might be restricted
|
|
672
752
|
}
|
|
673
753
|
|
|
674
754
|
// packages/astro-tokenkit/src/client/context.ts
|
|
@@ -762,8 +842,8 @@ function createMiddleware() {
|
|
|
762
842
|
yield tokenManager.ensure(ctx);
|
|
763
843
|
}
|
|
764
844
|
catch (error) {
|
|
765
|
-
// Log
|
|
766
|
-
console.error('[TokenKit] Automatic token rotation failed:', error);
|
|
845
|
+
// Log only the message to avoid leaking sensitive data in the error object
|
|
846
|
+
console.error('[TokenKit] Automatic token rotation failed:', error.message || error);
|
|
767
847
|
}
|
|
768
848
|
}
|
|
769
849
|
return next();
|
|
@@ -892,8 +972,7 @@ class APIClient {
|
|
|
892
972
|
while (true) {
|
|
893
973
|
attempt++;
|
|
894
974
|
try {
|
|
895
|
-
|
|
896
|
-
return response.data;
|
|
975
|
+
return yield this.executeRequest(config, ctx, attempt);
|
|
897
976
|
}
|
|
898
977
|
catch (error) {
|
|
899
978
|
// Check if we should retry
|
|
@@ -921,7 +1000,7 @@ class APIClient {
|
|
|
921
1000
|
// Build full URL
|
|
922
1001
|
const fullURL = this.buildURL(config.url, config.params);
|
|
923
1002
|
// Build headers
|
|
924
|
-
const headers = this.buildHeaders(config, ctx);
|
|
1003
|
+
const headers = this.buildHeaders(config, ctx, fullURL);
|
|
925
1004
|
// Build request init
|
|
926
1005
|
const init = {
|
|
927
1006
|
method: config.method,
|
|
@@ -1018,6 +1097,7 @@ class APIClient {
|
|
|
1018
1097
|
statusText: response.statusText,
|
|
1019
1098
|
headers: response.headers,
|
|
1020
1099
|
url,
|
|
1100
|
+
ok: response.ok,
|
|
1021
1101
|
};
|
|
1022
1102
|
});
|
|
1023
1103
|
}
|
|
@@ -1039,19 +1119,33 @@ class APIClient {
|
|
|
1039
1119
|
/**
|
|
1040
1120
|
* Build request headers
|
|
1041
1121
|
*/
|
|
1042
|
-
buildHeaders(config, ctx) {
|
|
1122
|
+
buildHeaders(config, ctx, targetURL) {
|
|
1043
1123
|
var _a, _b;
|
|
1044
1124
|
const headers = Object.assign(Object.assign({ 'Content-Type': 'application/json' }, this.config.headers), config.headers);
|
|
1045
|
-
// Add auth token if available
|
|
1046
|
-
if (this.tokenManager && !config.skipAuth) {
|
|
1125
|
+
// Add auth token if available (only for safe URLs)
|
|
1126
|
+
if (this.tokenManager && !config.skipAuth && this.isSafeURL(targetURL)) {
|
|
1047
1127
|
const session = this.tokenManager.getSession(ctx);
|
|
1048
1128
|
if (session === null || session === void 0 ? void 0 : session.accessToken) {
|
|
1049
|
-
const injectFn = (_b = (_a = this.config.auth) === null || _a === void 0 ? void 0 : _a.injectToken) !== null && _b !== void 0 ? _b : ((token) =>
|
|
1050
|
-
headers['Authorization'] = injectFn(session.accessToken);
|
|
1129
|
+
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}`);
|
|
1130
|
+
headers['Authorization'] = injectFn(session.accessToken, session.tokenType);
|
|
1051
1131
|
}
|
|
1052
1132
|
}
|
|
1053
1133
|
return headers;
|
|
1054
1134
|
}
|
|
1135
|
+
/**
|
|
1136
|
+
* Check if a URL is safe for token injection (same origin as baseURL)
|
|
1137
|
+
*/
|
|
1138
|
+
isSafeURL(url) {
|
|
1139
|
+
try {
|
|
1140
|
+
const requestUrl = new URL(url, this.config.baseURL);
|
|
1141
|
+
const baseUrl = new URL(this.config.baseURL || 'http://localhost');
|
|
1142
|
+
return requestUrl.origin === baseUrl.origin;
|
|
1143
|
+
}
|
|
1144
|
+
catch (_a) {
|
|
1145
|
+
// Only allow relative paths if baseURL is missing or invalid
|
|
1146
|
+
return !url.startsWith('http') && !url.startsWith('//');
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1055
1149
|
/**
|
|
1056
1150
|
* Login
|
|
1057
1151
|
*/
|
|
@@ -1061,7 +1155,7 @@ class APIClient {
|
|
|
1061
1155
|
throw new Error('Auth is not configured for this client');
|
|
1062
1156
|
}
|
|
1063
1157
|
const context = getContextStore();
|
|
1064
|
-
|
|
1158
|
+
return this.tokenManager.login(context, credentials, options);
|
|
1065
1159
|
});
|
|
1066
1160
|
}
|
|
1067
1161
|
/**
|
|
@@ -1139,11 +1233,23 @@ function createClient(config) {
|
|
|
1139
1233
|
*/
|
|
1140
1234
|
function tokenKit(config) {
|
|
1141
1235
|
setConfig(config);
|
|
1236
|
+
// Create a serializable version of the config for the runtime
|
|
1237
|
+
const serializableConfig = JSON.parse(JSON.stringify(config, (key, value) => {
|
|
1238
|
+
if (typeof value === 'function')
|
|
1239
|
+
return undefined;
|
|
1240
|
+
return value;
|
|
1241
|
+
}));
|
|
1142
1242
|
return {
|
|
1143
1243
|
name: 'astro-tokenkit',
|
|
1144
1244
|
hooks: {
|
|
1145
|
-
'astro:config:setup': () => {
|
|
1146
|
-
|
|
1245
|
+
'astro:config:setup': ({ updateConfig }) => {
|
|
1246
|
+
updateConfig({
|
|
1247
|
+
vite: {
|
|
1248
|
+
define: {
|
|
1249
|
+
'__TOKENKIT_CONFIG__': JSON.stringify(serializableConfig)
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
});
|
|
1147
1253
|
console.log('[TokenKit] Integration initialized');
|
|
1148
1254
|
},
|
|
1149
1255
|
},
|