@explorins/pers-sdk 1.6.32 → 1.6.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +50 -3
  2. package/dist/chunks/{pers-sdk-BbflloQE.cjs → pers-sdk-BGDZS3-x.cjs} +393 -19
  3. package/dist/chunks/pers-sdk-BGDZS3-x.cjs.map +1 -0
  4. package/dist/chunks/{pers-sdk-Cb3gtslf.js → pers-sdk-Ca36Z7SO.js} +389 -20
  5. package/dist/chunks/pers-sdk-Ca36Z7SO.js.map +1 -0
  6. package/dist/core/auth/auth-provider.interface.d.ts +12 -0
  7. package/dist/core/auth/auth-provider.interface.d.ts.map +1 -1
  8. package/dist/core/auth/default-auth-provider.d.ts +2 -0
  9. package/dist/core/auth/default-auth-provider.d.ts.map +1 -1
  10. package/dist/core/auth/dpop/dpop-manager.d.ts +32 -0
  11. package/dist/core/auth/dpop/dpop-manager.d.ts.map +1 -0
  12. package/dist/core/auth/dpop/dpop.types.d.ts +30 -0
  13. package/dist/core/auth/dpop/dpop.types.d.ts.map +1 -0
  14. package/dist/core/auth/dpop/index.d.ts +4 -0
  15. package/dist/core/auth/dpop/index.d.ts.map +1 -0
  16. package/dist/core/auth/dpop/web-crypto-dpop-provider.d.ts +13 -0
  17. package/dist/core/auth/dpop/web-crypto-dpop-provider.d.ts.map +1 -0
  18. package/dist/core/auth/index.d.ts +1 -0
  19. package/dist/core/auth/index.d.ts.map +1 -1
  20. package/dist/core/auth/indexed-db-storage.d.ts +24 -0
  21. package/dist/core/auth/indexed-db-storage.d.ts.map +1 -0
  22. package/dist/core/auth/token-storage.d.ts +19 -5
  23. package/dist/core/auth/token-storage.d.ts.map +1 -1
  24. package/dist/core/index.d.ts +3 -1
  25. package/dist/core/index.d.ts.map +1 -1
  26. package/dist/core/pers-api-client.d.ts +2 -0
  27. package/dist/core/pers-api-client.d.ts.map +1 -1
  28. package/dist/core/pers-config.d.ts +12 -1
  29. package/dist/core/pers-config.d.ts.map +1 -1
  30. package/dist/core.cjs +6 -1
  31. package/dist/core.cjs.map +1 -1
  32. package/dist/core.js +1 -1
  33. package/dist/index.cjs +6 -1
  34. package/dist/index.cjs.map +1 -1
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +1 -1
  37. package/dist/package.json +2 -2
  38. package/package.json +2 -2
  39. package/dist/chunks/pers-sdk-BbflloQE.cjs.map +0 -1
  40. package/dist/chunks/pers-sdk-Cb3gtslf.js.map +0 -1
  41. package/dist/legacy-pers-sdk.d.ts +0 -79
  42. package/dist/legacy-pers-sdk.d.ts.map +0 -1
package/README.md CHANGED
@@ -120,9 +120,11 @@ sdk.campaigns.getCampaignService()
120
120
  ## Core Features
121
121
 
122
122
  ### Authentication
123
+ - **Secure DPoP (Demonstrating Proof-of-Possession)**: Enabled by default to bind tokens to the client, preventing replay attacks.
123
124
  - External JWT integration (Firebase, Auth0, etc.)
124
125
  - Native token validation and refresh
125
126
  - User and admin authentication flows
127
+ - Flexible storage strategies (LocalStorage, IndexedDB, Memory)
126
128
 
127
129
  ### Business Management
128
130
  - Business registration and profiles
@@ -152,16 +154,61 @@ sdk.campaigns.getCampaignService()
152
154
  ## Configuration
153
155
 
154
156
  ```typescript
157
+ import { IndexedDBTokenStorage } from '@explorins/pers-sdk/core';
158
+
155
159
  interface PersConfig {
156
160
  environment?: 'development' | 'staging' | 'production';
157
- apiVersion?: 'v2';
158
161
  apiProjectKey: string;
159
- timeout?: number;
160
- retries?: number;
162
+
163
+ // Storage Strategy (Recommended: IndexedDB)
164
+ authStorage?: TokenStorage; // Defaults to LocalStorage if omitted
165
+
166
+ // DPoP Security (Default: enabled)
167
+ dpop?: {
168
+ enabled: boolean;
169
+ };
170
+
171
+ // Advanced overrides
161
172
  authProvider?: PersAuthProvider;
162
173
  }
163
174
  ```
164
175
 
176
+ ## Advanced Authentication
177
+
178
+ ### Using IndexedDB (Recommended)
179
+
180
+ For better security and performance, use the built-in `IndexedDBTokenStorage` instead of the default LocalStorage.
181
+
182
+ ```typescript
183
+ import { PersSDK } from '@explorins/pers-sdk';
184
+ import { IndexedDBTokenStorage } from '@explorins/pers-sdk/core';
185
+
186
+ const sdk = new PersSDK(adapter, {
187
+ environment: 'production',
188
+ apiProjectKey: 'your-key',
189
+ // Activates IndexedDB for tokens and keys
190
+ authStorage: new IndexedDBTokenStorage()
191
+ });
192
+ ```
193
+
194
+ ### Custom Storage & Fallbacks
195
+
196
+ You can implement your own storage strategy (e.g., hybrid Fallback) by implementing the `TokenStorage` interface. The SDK provides type-safe key constants.
197
+
198
+ ```typescript
199
+ import { TokenStorage, AUTH_STORAGE_KEYS, DPOP_STORAGE_KEYS } from '@explorins/pers-sdk/core';
200
+
201
+ class HybridStorage implements TokenStorage {
202
+ // IMPORTANT: Set to 'false' if your storage backend only supports strings (like LocalStorage).
203
+ // This ensures the SDK generates exportable keys that can be serialized.
204
+ // Set to 'true' (like IndexedDB) to enable high-security non-extractable CryptoKey objects.
205
+ readonly supportsObjects = false;
206
+
207
+ // Your custom implementation...
208
+ // Use AUTH_STORAGE_KEYS.ACCESS_TOKEN, DPOP_STORAGE_KEYS.PRIVATE, etc.
209
+ }
210
+ ```
211
+
165
212
  ## Error Handling
166
213
 
167
214
  ```typescript
@@ -335,14 +335,19 @@ const AUTH_STORAGE_KEYS = {
335
335
  };
336
336
  /**
337
337
  * LocalStorage implementation
338
+ * Note: Forces string conversion for compatibility
338
339
  */
339
340
  class LocalStorageTokenStorage {
341
+ constructor() {
342
+ this.supportsObjects = false;
343
+ }
340
344
  async get(key) {
341
345
  return typeof localStorage !== 'undefined' ? localStorage.getItem(key) : null;
342
346
  }
343
347
  async set(key, value) {
344
348
  if (typeof localStorage !== 'undefined') {
345
- localStorage.setItem(key, value);
349
+ const stringValue = typeof value === 'string' ? value : JSON.stringify(value);
350
+ localStorage.setItem(key, stringValue);
346
351
  }
347
352
  }
348
353
  async remove(key) {
@@ -363,6 +368,7 @@ class LocalStorageTokenStorage {
363
368
  */
364
369
  class MemoryTokenStorage {
365
370
  constructor() {
371
+ this.supportsObjects = true;
366
372
  this.store = new Map();
367
373
  }
368
374
  async get(key) {
@@ -386,26 +392,30 @@ class AuthTokenManager {
386
392
  this.storage = storage;
387
393
  }
388
394
  async getAccessToken() {
389
- return this.storage.get(AUTH_STORAGE_KEYS.ACCESS_TOKEN);
395
+ const val = await this.storage.get(AUTH_STORAGE_KEYS.ACCESS_TOKEN);
396
+ // Ensure we return string for tokens
397
+ return typeof val === 'string' ? val : null;
390
398
  }
391
399
  async setAccessToken(token) {
392
400
  await this.storage.set(AUTH_STORAGE_KEYS.ACCESS_TOKEN, token);
393
401
  }
394
402
  async getRefreshToken() {
395
- return this.storage.get(AUTH_STORAGE_KEYS.REFRESH_TOKEN);
403
+ const val = await this.storage.get(AUTH_STORAGE_KEYS.REFRESH_TOKEN);
404
+ return typeof val === 'string' ? val : null;
396
405
  }
397
406
  async setRefreshToken(token) {
398
407
  await this.storage.set(AUTH_STORAGE_KEYS.REFRESH_TOKEN, token);
399
408
  }
400
409
  async getProviderToken() {
401
- return this.storage.get(AUTH_STORAGE_KEYS.PROVIDER_TOKEN);
410
+ const val = await this.storage.get(AUTH_STORAGE_KEYS.PROVIDER_TOKEN);
411
+ return typeof val === 'string' ? val : null;
402
412
  }
403
413
  async setProviderToken(token) {
404
414
  await this.storage.set(AUTH_STORAGE_KEYS.PROVIDER_TOKEN, token);
405
415
  }
406
416
  async getAuthType() {
407
417
  const authType = await this.storage.get(AUTH_STORAGE_KEYS.AUTH_TYPE);
408
- return authType;
418
+ return typeof authType === 'string' ? authType : null;
409
419
  }
410
420
  async setAuthType(authType) {
411
421
  await this.storage.set(AUTH_STORAGE_KEYS.AUTH_TYPE, authType);
@@ -430,6 +440,218 @@ class AuthTokenManager {
430
440
  }
431
441
  }
432
442
 
443
+ class WebDPoPCryptoProvider {
444
+ get crypto() {
445
+ // Basic environment check
446
+ if (typeof crypto !== 'undefined')
447
+ return crypto;
448
+ if (typeof window !== 'undefined' && window.crypto)
449
+ return window.crypto;
450
+ if (typeof globalThis !== 'undefined' && globalThis.crypto)
451
+ return globalThis.crypto;
452
+ throw new Error('WebCrypto API not found. Please polyfill crypto.subtle or use a different DPoPCryptoProvider.');
453
+ }
454
+ async generateKeyPair(options) {
455
+ const extractable = options?.extractable ?? true;
456
+ const keyPair = await this.crypto.subtle.generateKey({
457
+ name: 'ECDSA',
458
+ namedCurve: 'P-256',
459
+ }, extractable, // Configurable extractability
460
+ ['sign', 'verify']);
461
+ const publicKey = await this.crypto.subtle.exportKey('jwk', keyPair.publicKey);
462
+ // If extractable, export private key as JWK (plain object)
463
+ // If NOT extractable, keep it as CryptoKey (native handle)
464
+ // However, DPoPKeyPair interface currently expects JsonWebKey | CryptoKey
465
+ // We need to check DPoPKeyPair type definition.
466
+ // Wait, DPoPKeyPair definition in dpop.types.ts says:
467
+ // publicKey: JsonWebKey; privateKey: JsonWebKey;
468
+ // I need to change that to support CryptoKey.
469
+ // Let's re-read dpop.types.ts
470
+ // I assumed it might be compatible but let's verify.
471
+ // If I change privateKey to unknown or JsonWebKey | CryptoKey, it will be safer.
472
+ let privateKey;
473
+ if (extractable) {
474
+ privateKey = await this.crypto.subtle.exportKey('jwk', keyPair.privateKey);
475
+ }
476
+ else {
477
+ privateKey = keyPair.privateKey;
478
+ }
479
+ return { publicKey, privateKey };
480
+ }
481
+ async signProof(payload, keyPair) {
482
+ // 1. Prepare Header
483
+ const header = {
484
+ typ: 'dpop+jwt',
485
+ alg: 'ES256',
486
+ jwk: keyPair.publicKey
487
+ };
488
+ // 2. Prepare Payload
489
+ // Ensure jti and iat if not present (though Manager usually provides them)
490
+ const finalPayload = {
491
+ ...payload,
492
+ iat: payload.iat || Math.floor(Date.now() / 1000),
493
+ jti: payload.jti || this.generateUUID()
494
+ };
495
+ // 3. Encode segments
496
+ const encodedHeader = this.base64UrlEncode(JSON.stringify(header));
497
+ const encodedPayload = this.base64UrlEncode(JSON.stringify(finalPayload));
498
+ const signingInput = `${encodedHeader}.${encodedPayload}`;
499
+ // 4. Import private key for signing
500
+ let privateKey;
501
+ // Check if it's already a CryptoKey (non-extractable handle)
502
+ // We check for 'algorithm' property which signals a CryptoKey object
503
+ if (keyPair.privateKey.algorithm) {
504
+ privateKey = keyPair.privateKey;
505
+ }
506
+ else {
507
+ // Otherwise import from JWK
508
+ privateKey = await this.crypto.subtle.importKey('jwk', keyPair.privateKey, {
509
+ name: 'ECDSA',
510
+ namedCurve: 'P-256',
511
+ }, false, ['sign']);
512
+ }
513
+ // 5. Sign
514
+ const signatureBuffer = await this.crypto.subtle.sign({
515
+ name: 'ECDSA',
516
+ hash: { name: 'SHA-256' },
517
+ }, privateKey, new TextEncoder().encode(signingInput));
518
+ const encodedSignature = this.base64UrlEncodeBuffer(signatureBuffer);
519
+ return `${signingInput}.${encodedSignature}`;
520
+ }
521
+ async hashAccessToken(accessToken) {
522
+ const encoder = new TextEncoder();
523
+ const data = encoder.encode(accessToken);
524
+ const hashBuffer = await this.crypto.subtle.digest('SHA-256', data);
525
+ return this.base64UrlEncodeBuffer(hashBuffer);
526
+ }
527
+ // --- Helpers ---
528
+ generateUUID() {
529
+ if (typeof crypto !== 'undefined' && crypto.randomUUID) {
530
+ return crypto.randomUUID();
531
+ }
532
+ // Fallback for older envs
533
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
534
+ const r = (Math.random() * 16) | 0;
535
+ const v = c === 'x' ? r : (r & 0x3) | 0x8;
536
+ return v.toString(16);
537
+ });
538
+ }
539
+ base64UrlEncode(str) {
540
+ // Use TextEncoder to handle UTF-8 properly
541
+ const encoder = new TextEncoder();
542
+ return this.base64UrlEncodeBuffer(encoder.encode(str));
543
+ }
544
+ base64UrlEncodeBuffer(buffer) {
545
+ const bytes = new Uint8Array(buffer);
546
+ let binary = '';
547
+ const len = bytes.byteLength;
548
+ // Chunk processing for large buffers if needed, but proofs are small
549
+ for (let i = 0; i < len; i++) {
550
+ binary += String.fromCharCode(bytes[i]);
551
+ }
552
+ // btoa is widely available in browsers.
553
+ // In Node (non-globals), we might need Buffer.
554
+ // For universal 'platform agnostic' aiming at standard web-like envs:
555
+ let base64 = '';
556
+ if (typeof btoa === 'function') {
557
+ base64 = btoa(binary);
558
+ }
559
+ else if (typeof Buffer !== 'undefined') {
560
+ base64 = Buffer.from(binary, 'binary').toString('base64');
561
+ }
562
+ else {
563
+ throw new Error('Base64 encoding not supported in this environment');
564
+ }
565
+ return base64
566
+ .replace(/\+/g, '-')
567
+ .replace(/\//g, '_')
568
+ .replace(/=+$/, '');
569
+ }
570
+ }
571
+
572
+ const DPOP_STORAGE_KEYS = {
573
+ PUBLIC: 'pers_dpop_public_key',
574
+ PRIVATE: 'pers_dpop_private_key'
575
+ };
576
+ class DPoPManager {
577
+ constructor(storage, cryptoProvider) {
578
+ this.storage = storage;
579
+ this.memoryKeyPair = null;
580
+ this.cryptoProvider = cryptoProvider || new WebDPoPCryptoProvider();
581
+ }
582
+ /**
583
+ * Ensures a DPoP key pair exists, loading from storage or generating new one.
584
+ */
585
+ async ensureKeyPair() {
586
+ if (this.memoryKeyPair)
587
+ return this.memoryKeyPair;
588
+ const storedPublic = await this.storage.get(DPOP_STORAGE_KEYS.PUBLIC);
589
+ const storedPrivate = await this.storage.get(DPOP_STORAGE_KEYS.PRIVATE);
590
+ if (storedPublic && storedPrivate) {
591
+ try {
592
+ // Handle polymorphic storage: String (JSON) or Object (CryptoKey)
593
+ const publicKey = typeof storedPublic === 'string' ? JSON.parse(storedPublic) : storedPublic;
594
+ const privateKey = typeof storedPrivate === 'string' ? JSON.parse(storedPrivate) : storedPrivate;
595
+ this.memoryKeyPair = { publicKey, privateKey };
596
+ return this.memoryKeyPair;
597
+ }
598
+ catch (e) {
599
+ console.warn('Corrupted DPoP keys in storage, regenerating.');
600
+ }
601
+ }
602
+ // Generate new key pair
603
+ // Adaptation: If storage supports raw objects (like IndexedDB or Native Keychain),
604
+ // we can generate Non-Extractable keys for maximum security.
605
+ // If storage is text-only (LocalStorage), we must use Extractable keys to serialize them.
606
+ const useHighSecurity = !!this.storage.supportsObjects;
607
+ const keyPair = await this.cryptoProvider.generateKeyPair({
608
+ extractable: !useHighSecurity
609
+ });
610
+ // Save to storage
611
+ await this.storage.set(DPOP_STORAGE_KEYS.PUBLIC, keyPair.publicKey);
612
+ await this.storage.set(DPOP_STORAGE_KEYS.PRIVATE, keyPair.privateKey);
613
+ this.memoryKeyPair = keyPair;
614
+ return keyPair;
615
+ }
616
+ /**
617
+ * Generates the DPoP proof header value.
618
+ * @param method HTTP method
619
+ * @param url Request URL
620
+ * @param accessToken Optional access token to bind the proof to (ath claim)
621
+ */
622
+ async generateProofHeader(method, url, accessToken) {
623
+ const keyPair = await this.ensureKeyPair();
624
+ const payload = {
625
+ htm: method,
626
+ htu: url,
627
+ };
628
+ if (accessToken) {
629
+ payload.ath = await this.cryptoProvider.hashAccessToken(accessToken);
630
+ }
631
+ return this.cryptoProvider.signProof(payload, keyPair);
632
+ }
633
+ /**
634
+ * Clears DPoP keys (e.g. on logout)
635
+ */
636
+ async clearKeys() {
637
+ this.memoryKeyPair = null;
638
+ await this.storage.remove(DPOP_STORAGE_KEYS.PUBLIC);
639
+ await this.storage.remove(DPOP_STORAGE_KEYS.PRIVATE);
640
+ }
641
+ /**
642
+ * Gets the public key (for debugging/inspection)
643
+ */
644
+ async getPublicKey() {
645
+ try {
646
+ const kp = await this.ensureKeyPair();
647
+ return kp.publicKey;
648
+ }
649
+ catch {
650
+ return null;
651
+ }
652
+ }
653
+ }
654
+
433
655
  /**
434
656
  * Default Authentication Provider
435
657
  * Simple implementation of PERS authentication with token lifecycle management
@@ -443,6 +665,9 @@ class DefaultAuthProvider {
443
665
  this.authType = config.authType || 'user';
444
666
  const storage = config.storage || this.createStorage();
445
667
  this.tokenManager = new AuthTokenManager(storage);
668
+ if (config.dpop?.enabled) {
669
+ this.dpopManager = new DPoPManager(storage, config.dpop.cryptoProvider);
670
+ }
446
671
  }
447
672
  createStorage() {
448
673
  return typeof window === 'undefined' || typeof localStorage === 'undefined'
@@ -452,6 +677,26 @@ class DefaultAuthProvider {
452
677
  getToken() {
453
678
  return this.tokenManager.getAccessToken();
454
679
  }
680
+ async getAuthHeaders(token, method, url) {
681
+ const headers = {};
682
+ if (this.dpopManager && method && url) {
683
+ // Generate DPoP Proof
684
+ // Note: For 'bypassAuth' requests (token is null), we still generate proof (bound to public key, no ath)
685
+ // This covers Token Requests (Login) which need DPoP header to bind the issued token.
686
+ const proof = await this.dpopManager.generateProofHeader(method, url, token || undefined);
687
+ headers['DPoP'] = proof;
688
+ if (token) {
689
+ headers['Authorization'] = `DPoP ${token}`;
690
+ }
691
+ }
692
+ else {
693
+ // Standard Bearer
694
+ if (token) {
695
+ headers['Authorization'] = `Bearer ${token}`;
696
+ }
697
+ }
698
+ return headers;
699
+ }
455
700
  async getProjectKey() {
456
701
  return this.config.projectKey || null;
457
702
  }
@@ -486,6 +731,9 @@ class DefaultAuthProvider {
486
731
  }
487
732
  async clearTokens() {
488
733
  await this.tokenManager.clearAllTokens();
734
+ if (this.dpopManager) {
735
+ await this.dpopManager.clearKeys();
736
+ }
489
737
  }
490
738
  async setTokens(accessToken, refreshToken, providerToken) {
491
739
  await this.tokenManager.setTokens(accessToken, refreshToken, providerToken);
@@ -534,7 +782,11 @@ class PersApiClient {
534
782
  this.mergedConfig.authProvider = new DefaultAuthProvider({
535
783
  authType: this.mergedConfig.authType || 'user',
536
784
  projectKey: this.mergedConfig.apiProjectKey,
537
- storage: this.mergedConfig.authStorage // Support custom storage
785
+ storage: this.mergedConfig.authStorage, // Support custom storage
786
+ dpop: {
787
+ enabled: this.mergedConfig.dpop?.enabled ?? true, // Enable DPoP by default
788
+ cryptoProvider: this.mergedConfig.dpop?.cryptoProvider
789
+ }
538
790
  });
539
791
  }
540
792
  this.authService = new AuthService(this.authApi, this.mergedConfig.authProvider);
@@ -597,7 +849,7 @@ class PersApiClient {
597
849
  });
598
850
  }
599
851
  const requestOptions = {
600
- headers: await this.getHeaders(!bypassAuth),
852
+ headers: await this.getHeaders(!bypassAuth, method, url),
601
853
  timeout: this.mergedConfig.timeout,
602
854
  responseType
603
855
  };
@@ -692,26 +944,45 @@ class PersApiClient {
692
944
  * Get request headers with optional auth token and project key
693
945
  *
694
946
  * @param includeAuth - Whether to include the Authorization header (default: true)
947
+ * @param method - HTTP Method (required for DPoP)
948
+ * @param url - Full Request URL (required for DPoP)
695
949
  */
696
- async getHeaders(includeAuth = true) {
950
+ async getHeaders(includeAuth = true, method, url) {
697
951
  const headers = {
698
952
  'Content-Type': 'application/json',
699
953
  };
700
- if (includeAuth) {
701
- // Add authentication token
702
- if (this.mergedConfig.authProvider) {
703
- const token = await this.mergedConfig.authProvider.getToken();
704
- if (token) {
705
- headers['Authorization'] = `Bearer ${token}`;
954
+ if (this.mergedConfig.authProvider) {
955
+ let token = null;
956
+ if (includeAuth) {
957
+ token = await this.mergedConfig.authProvider.getToken();
958
+ }
959
+ // Handle Authentication Headers (Bearer or DPoP)
960
+ if (this.mergedConfig.authProvider.getAuthHeaders) {
961
+ // Provider supports advanced headers (DPoP)
962
+ // We pass token (null if includeAuth=false) so it can generate DPoP proofs for login requests too
963
+ try {
964
+ const authHeaders = await this.mergedConfig.authProvider.getAuthHeaders(token, method, url);
965
+ Object.assign(headers, authHeaders);
706
966
  }
707
- else {
708
- console.warn('[PersApiClient] No token available from auth provider');
967
+ catch (e) {
968
+ console.warn('[PersApiClient] Failed to generate auth headers:', e);
969
+ // Fallback if token exists but DPoP generation failed?
970
+ // Better to fail or let standardized fallback below handle it?
971
+ // If DPoP fails, falling back to Bearer might be insecure if server enforces DPoP.
972
+ // For now, valid token -> Bearer fallback logic mostly for non-DPoP legacy providers.
709
973
  }
710
974
  }
711
- else {
712
- console.warn('[PersApiClient] No auth provider configured');
975
+ // Fallback: If provider didn't give headers but we have token and NO Authorization header yet
976
+ if (includeAuth && token && !headers['Authorization']) {
977
+ headers['Authorization'] = `Bearer ${token}`;
978
+ }
979
+ else if (includeAuth && !token) {
980
+ console.warn('[PersApiClient] No token available from auth provider');
713
981
  }
714
982
  }
983
+ else if (includeAuth) {
984
+ console.warn('[PersApiClient] No auth provider configured');
985
+ }
715
986
  // Add project key
716
987
  if (this.mergedConfig.authProvider) {
717
988
  const projectKey = await this.mergedConfig.authProvider.getProjectKey();
@@ -747,6 +1018,104 @@ class PersApiClient {
747
1018
  }
748
1019
  }
749
1020
 
1021
+ class IndexedDBTokenStorage {
1022
+ constructor() {
1023
+ this.supportsObjects = true;
1024
+ this.dbPromise = null;
1025
+ if (typeof indexedDB === 'undefined') {
1026
+ console.warn('IndexedDB is not available in this environment');
1027
+ }
1028
+ }
1029
+ getDB() {
1030
+ if (this.dbPromise)
1031
+ return this.dbPromise;
1032
+ this.dbPromise = new Promise((resolve, reject) => {
1033
+ if (typeof indexedDB === 'undefined') {
1034
+ return reject(new Error('IndexedDB not supported'));
1035
+ }
1036
+ const request = indexedDB.open(IndexedDBTokenStorage.DB_NAME, IndexedDBTokenStorage.DB_VERSION);
1037
+ request.onupgradeneeded = (event) => {
1038
+ const db = event.target.result;
1039
+ if (!db.objectStoreNames.contains(IndexedDBTokenStorage.STORE_NAME)) {
1040
+ db.createObjectStore(IndexedDBTokenStorage.STORE_NAME);
1041
+ }
1042
+ };
1043
+ request.onsuccess = (event) => {
1044
+ resolve(event.target.result);
1045
+ };
1046
+ request.onerror = (event) => {
1047
+ reject(event.target.error);
1048
+ };
1049
+ });
1050
+ return this.dbPromise;
1051
+ }
1052
+ async get(key) {
1053
+ try {
1054
+ const db = await this.getDB();
1055
+ return new Promise((resolve, reject) => {
1056
+ const transaction = db.transaction(IndexedDBTokenStorage.STORE_NAME, 'readonly');
1057
+ const store = transaction.objectStore(IndexedDBTokenStorage.STORE_NAME);
1058
+ const request = store.get(key);
1059
+ request.onsuccess = () => resolve(request.result || null);
1060
+ request.onerror = () => reject(request.error);
1061
+ });
1062
+ }
1063
+ catch (e) {
1064
+ console.warn('[IndexedDBTokenStorage] Failed to get key', key, e);
1065
+ return null;
1066
+ }
1067
+ }
1068
+ async set(key, value) {
1069
+ try {
1070
+ const db = await this.getDB();
1071
+ return new Promise((resolve, reject) => {
1072
+ const transaction = db.transaction(IndexedDBTokenStorage.STORE_NAME, 'readwrite');
1073
+ const store = transaction.objectStore(IndexedDBTokenStorage.STORE_NAME);
1074
+ const request = store.put(value, key);
1075
+ request.onsuccess = () => resolve();
1076
+ request.onerror = () => reject(request.error);
1077
+ });
1078
+ }
1079
+ catch (e) {
1080
+ console.error('[IndexedDBTokenStorage] Failed to set key', key, e);
1081
+ throw e;
1082
+ }
1083
+ }
1084
+ async remove(key) {
1085
+ try {
1086
+ const db = await this.getDB();
1087
+ return new Promise((resolve, reject) => {
1088
+ const transaction = db.transaction(IndexedDBTokenStorage.STORE_NAME, 'readwrite');
1089
+ const store = transaction.objectStore(IndexedDBTokenStorage.STORE_NAME);
1090
+ const request = store.delete(key);
1091
+ request.onsuccess = () => resolve();
1092
+ request.onerror = () => reject(request.error);
1093
+ });
1094
+ }
1095
+ catch (e) {
1096
+ console.warn('[IndexedDBTokenStorage] Failed to remove key', key, e);
1097
+ }
1098
+ }
1099
+ async clear() {
1100
+ try {
1101
+ const db = await this.getDB();
1102
+ return new Promise((resolve, reject) => {
1103
+ const transaction = db.transaction(IndexedDBTokenStorage.STORE_NAME, 'readwrite');
1104
+ const store = transaction.objectStore(IndexedDBTokenStorage.STORE_NAME);
1105
+ const request = store.clear();
1106
+ request.onsuccess = () => resolve();
1107
+ request.onerror = () => reject(request.error);
1108
+ });
1109
+ }
1110
+ catch (e) {
1111
+ console.warn('[IndexedDBTokenStorage] Failed to clear storage', e);
1112
+ }
1113
+ }
1114
+ }
1115
+ IndexedDBTokenStorage.DB_NAME = 'pers-sdk-storage';
1116
+ IndexedDBTokenStorage.STORE_NAME = 'auth-tokens';
1117
+ IndexedDBTokenStorage.DB_VERSION = 1;
1118
+
750
1119
  /**
751
1120
  * Environment Detection Utility
752
1121
  * Detects runtime environment for platform-specific behaviors
@@ -5928,6 +6297,7 @@ function createPersSDK(httpClient, config) {
5928
6297
  return new PersSDK(httpClient, config);
5929
6298
  }
5930
6299
 
6300
+ exports.AUTH_STORAGE_KEYS = AUTH_STORAGE_KEYS;
5931
6301
  exports.AnalyticsManager = AnalyticsManager;
5932
6302
  exports.ApiKeyApi = ApiKeyApi;
5933
6303
  exports.ApiKeyManager = ApiKeyManager;
@@ -5938,11 +6308,14 @@ exports.AuthTokenManager = AuthTokenManager;
5938
6308
  exports.BusinessManager = BusinessManager;
5939
6309
  exports.CampaignManager = CampaignManager;
5940
6310
  exports.DEFAULT_PERS_CONFIG = DEFAULT_PERS_CONFIG;
6311
+ exports.DPOP_STORAGE_KEYS = DPOP_STORAGE_KEYS;
6312
+ exports.DPoPManager = DPoPManager;
5941
6313
  exports.DefaultAuthProvider = DefaultAuthProvider;
5942
6314
  exports.DonationManager = DonationManager;
5943
6315
  exports.FileApi = FileApi;
5944
6316
  exports.FileManager = FileManager;
5945
6317
  exports.FileService = FileService;
6318
+ exports.IndexedDBTokenStorage = IndexedDBTokenStorage;
5946
6319
  exports.LocalStorageTokenStorage = LocalStorageTokenStorage;
5947
6320
  exports.MemoryTokenStorage = MemoryTokenStorage;
5948
6321
  exports.PersApiClient = PersApiClient;
@@ -5955,10 +6328,11 @@ exports.TransactionManager = TransactionManager;
5955
6328
  exports.UserManager = UserManager;
5956
6329
  exports.UserStatusManager = UserStatusManager;
5957
6330
  exports.Web3Manager = Web3Manager;
6331
+ exports.WebDPoPCryptoProvider = WebDPoPCryptoProvider;
5958
6332
  exports.buildApiRoot = buildApiRoot;
5959
6333
  exports.createPersSDK = createPersSDK;
5960
6334
  exports.detectEnvironment = detectEnvironment;
5961
6335
  exports.environment = environment;
5962
6336
  exports.mergeWithDefaults = mergeWithDefaults;
5963
6337
  exports.warnIfProblematicEnvironment = warnIfProblematicEnvironment;
5964
- //# sourceMappingURL=pers-sdk-BbflloQE.cjs.map
6338
+ //# sourceMappingURL=pers-sdk-BGDZS3-x.cjs.map