@explorins/pers-sdk-react-native 1.5.26 → 1.5.28

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/dist/index.js CHANGED
@@ -2631,6 +2631,7 @@ exports.ApiKeyType = void 0;
2631
2631
  ApiKeyType["USER_JWT_REFRESH_TOKEN"] = "USER_JWT_REFRESH_TOKEN";
2632
2632
  ApiKeyType["BLOCKCHAIN_WRITER_JWT"] = "BLOCKCHAIN_WRITER_JWT";
2633
2633
  ApiKeyType["BLOCKCHAIN_READER_JWT"] = "BLOCKCHAIN_READER_JWT";
2634
+ ApiKeyType["TRANSACTION_JWT_ACCESS_TOKEN"] = "TRANSACTION_JWT_ACCESS_TOKEN";
2634
2635
  // TODO: this needs to be removed. However, there is still dependency
2635
2636
  ApiKeyType["TENANT_LEGACY_ADMIN"] = "TENANT_ADMIN_JWT";
2636
2637
  // TENANT_SYSTEM_API_SECRET = 'TENANT_SYSTEM_API_SECRET',
@@ -2656,23 +2657,6 @@ exports.Web3ContractTypes = void 0;
2656
2657
  Web3ContractTypes["TOKEN_CONTRACT"] = "TOKEN_CONTRACT";
2657
2658
  })(exports.Web3ContractTypes || (exports.Web3ContractTypes = {}));
2658
2659
 
2659
- exports.CampaignConditionType = void 0;
2660
- (function (CampaignConditionType) {
2661
- CampaignConditionType["EQUALS"] = "EQUALS";
2662
- CampaignConditionType["NOT_EQUALS"] = "NOT_EQUALS";
2663
- CampaignConditionType["GREATER_THAN"] = "GREATER_THAN";
2664
- CampaignConditionType["LESS_THAN"] = "LESS_THAN";
2665
- CampaignConditionType["CONTAINS"] = "CONTAINS";
2666
- CampaignConditionType["IS_PART_OF"] = "IS_PART_OF";
2667
- })(exports.CampaignConditionType || (exports.CampaignConditionType = {}));
2668
-
2669
- exports.CampaignTriggerType = void 0;
2670
- (function (CampaignTriggerType) {
2671
- CampaignTriggerType["CLAIM_BY_USER"] = "CLAIM_BY_USER";
2672
- CampaignTriggerType["CLAIM_BY_SYSTEM"] = "CLAIM_BY_SYSTEM";
2673
- CampaignTriggerType["CLAIM_BY_BUSINESS"] = "CLAIM_BY_BUSINESS";
2674
- })(exports.CampaignTriggerType || (exports.CampaignTriggerType = {}));
2675
-
2676
2660
  /**
2677
2661
  * Entity ownership types - defines what types of entities can own resources
2678
2662
  *
@@ -2817,6 +2801,45 @@ exports.RedemptionRedeemStatus = void 0;
2817
2801
  RedemptionRedeemStatus["FAILED"] = "FAILED"; // Processing failed
2818
2802
  })(exports.RedemptionRedeemStatus || (exports.RedemptionRedeemStatus = {}));
2819
2803
 
2804
+ /**
2805
+ * Trigger Source Types
2806
+ * Defines the different types of triggers that can activate a campaign flow
2807
+ */
2808
+ exports.TriggerSourceType = void 0;
2809
+ (function (TriggerSourceType) {
2810
+ TriggerSourceType["QR_CODE"] = "QR_CODE";
2811
+ // NFC_TAG = 'NFC_TAG',
2812
+ TriggerSourceType["API_WEBHOOK"] = "API_WEBHOOK";
2813
+ // TRANSACTION = 'TRANSACTION',
2814
+ })(exports.TriggerSourceType || (exports.TriggerSourceType = {}));
2815
+ /**
2816
+ * Source Logic Types
2817
+ * Defines how multiple trigger sources combine to activate a flow
2818
+ * Currently only ANY (OR logic) is implemented
2819
+ */
2820
+ exports.SourceLogicType = void 0;
2821
+ (function (SourceLogicType) {
2822
+ SourceLogicType["ANY"] = "any";
2823
+ // Future: ALL = 'all', SEQUENCE = 'sequence', WEIGHTED = 'weighted'
2824
+ })(exports.SourceLogicType || (exports.SourceLogicType = {}));
2825
+
2826
+ exports.CampaignConditionType = void 0;
2827
+ (function (CampaignConditionType) {
2828
+ CampaignConditionType["EQUALS"] = "EQUALS";
2829
+ CampaignConditionType["NOT_EQUALS"] = "NOT_EQUALS";
2830
+ CampaignConditionType["GREATER_THAN"] = "GREATER_THAN";
2831
+ CampaignConditionType["LESS_THAN"] = "LESS_THAN";
2832
+ CampaignConditionType["CONTAINS"] = "CONTAINS";
2833
+ CampaignConditionType["IS_PART_OF"] = "IS_PART_OF";
2834
+ })(exports.CampaignConditionType || (exports.CampaignConditionType = {}));
2835
+
2836
+ exports.CampaignTriggerType = void 0;
2837
+ (function (CampaignTriggerType) {
2838
+ CampaignTriggerType["CLAIM_BY_USER"] = "CLAIM_BY_USER";
2839
+ CampaignTriggerType["CLAIM_BY_SYSTEM"] = "CLAIM_BY_SYSTEM";
2840
+ CampaignTriggerType["CLAIM_BY_BUSINESS"] = "CLAIM_BY_BUSINESS";
2841
+ })(exports.CampaignTriggerType || (exports.CampaignTriggerType = {}));
2842
+
2820
2843
  const apiPublicKeyTestPrefix = 'pk_test_';
2821
2844
  const testnetPrefix = 'testnet-';
2822
2845
  const testnetShortPrefix = 'tn-';
@@ -2937,8 +2960,6 @@ const NativeTokenTypes = {
2937
2960
  ERC721: 'ERC721'
2938
2961
  };
2939
2962
 
2940
- /* // ✅ EXPRESSION ALIASES: Results from CASE WHEN expressions and other computed fields
2941
- & { [key: string]: string | number | Date | undefined }; */
2942
2963
  const ANALYTICS_METRIC_TYPES = ['count', 'sum', 'avg', 'min', 'max'];
2943
2964
  const ANALYTICS_GROUP_BY_TYPES = ['month', 'week', 'day', 'year', 'quarter'];
2944
2965
 
@@ -23836,12 +23857,12 @@ class CampaignApi {
23836
23857
  params.push(`tag=${encodeURIComponent(options.tag)}`);
23837
23858
  if (options.limit)
23838
23859
  params.push(`limit=${options.limit}`);
23839
- if (options.offset)
23840
- params.push(`offset=${options.offset}`);
23841
- if (options.sort)
23842
- params.push(`sort=${options.sort}`);
23843
- if (options.order)
23844
- params.push(`order=${options.order}`);
23860
+ if (options.page)
23861
+ params.push(`page=${options.page}`);
23862
+ if (options.sortBy)
23863
+ params.push(`sortBy=${options.sortBy}`);
23864
+ if (options.sortOrder)
23865
+ params.push(`sortOrder=${options.sortOrder}`);
23845
23866
  }
23846
23867
  if (params.length > 0) {
23847
23868
  url += `?${params.join('&')}`;
@@ -23950,10 +23971,25 @@ class CampaignApi {
23950
23971
  // ==========================================
23951
23972
  /**
23952
23973
  * PUBLIC: Get campaign triggers catalog
23953
- * NEW: GET /campaign-triggers
23974
+ * NEW: GET /trigger-sources
23954
23975
  */
23955
- async getCampaignTriggers() {
23956
- return this.apiClient.get(`/campaign-triggers`);
23976
+ async getCampaignTriggers(options) {
23977
+ let url = '/trigger-sources';
23978
+ const params = [];
23979
+ if (options) {
23980
+ if (options.limit)
23981
+ params.push(`limit=${options.limit}`);
23982
+ if (options.page)
23983
+ params.push(`page=${options.page}`);
23984
+ if (options.sortBy)
23985
+ params.push(`sortBy=${options.sortBy}`);
23986
+ if (options.sortOrder)
23987
+ params.push(`sortOrder=${options.sortOrder}`);
23988
+ }
23989
+ if (params.length > 0) {
23990
+ url += `?${params.join('&')}`;
23991
+ }
23992
+ return this.apiClient.get(url);
23957
23993
  }
23958
23994
  /**
23959
23995
  * ADMIN: Create campaign trigger
@@ -23983,26 +24019,12 @@ class CampaignApi {
23983
24019
  async setCampaignTrigger(campaignId, triggerId) {
23984
24020
  return this.apiClient.put(`/campaign-triggers/${triggerId}/assign/${campaignId}`, {});
23985
24021
  }
23986
- /**
23987
- * ADMIN: Create trigger condition
23988
- * NEW: POST /campaign-triggers/conditions
23989
- */
23990
- async createTriggerCondition(condition) {
23991
- return this.apiClient.post('/campaign-triggers/conditions', condition);
23992
- }
23993
- /**
23994
- * ADMIN: Update trigger condition
23995
- * NEW: PUT /campaign-triggers/conditions/{id}
23996
- */
23997
- async updateTriggerCondition(conditionId, condition) {
23998
- return this.apiClient.put(`/campaign-triggers/conditions/${conditionId}`, condition);
23999
- }
24000
24022
  /**
24001
24023
  * ADMIN: Add/Remove condition to trigger
24002
24024
  * NEW: PUT /campaign-triggers/{triggerId}/conditions/{conditionId}
24003
24025
  */
24004
- async addOrRemoveConditionToTrigger(triggerId, conditionId) {
24005
- return this.apiClient.put(`/campaign-triggers/${triggerId}/conditions/${conditionId}`, {});
24026
+ async addOrRemoveConditionToTrigger(triggerId, condition) {
24027
+ return this.apiClient.put(`/campaign-triggers/${triggerId}/conditions`, condition);
24006
24028
  }
24007
24029
  // ==========================================
24008
24030
  // BUSINESS ENGAGEMENTS (/campaign-engagements)
@@ -24589,26 +24611,11 @@ class TransactionApi {
24589
24611
  /**
24590
24612
  * AUTH: Submit client-side signed transaction
24591
24613
  *
24592
- * NEW ENDPOINT: POST /transactions/{id}/submit
24614
+ * NEW ENDPOINT: POST /transactions/submit
24593
24615
  */
24594
24616
  async submitSignedTransaction(signedTxData) {
24595
24617
  return this.apiClient.post(`${this.basePath}/submit`, signedTxData);
24596
24618
  }
24597
- /**
24598
- * AUTH: Burn user tokens
24599
- *
24600
- * UPDATED: Uses new user transaction endpoint with burn-specific parameters
24601
- * Note: This might need backend confirmation on burn functionality implementation
24602
- */
24603
- /* async burnUserTokens(request: UserBurnTokenRequestDTO): Promise<TransactionRequestResponseDTO> {
24604
- // Map burn request to TransactionRequestDTO format for new endpoint
24605
- const transactionRequest: TransactionRequestDTO = {
24606
- ...request,
24607
- // Add any specific burn transaction parameters here
24608
- } as any;
24609
-
24610
- return this.apiClient.post<TransactionRequestResponseDTO>(`${this.basePath}`, transactionRequest);
24611
- } */
24612
24619
  // ==========================================
24613
24620
  // BUSINESS OPERATIONS
24614
24621
  // ==========================================
@@ -24633,9 +24640,9 @@ class TransactionApi {
24633
24640
  return this.apiClient.post<TransactionDTO>(`${this.basePath}/system`, request);
24634
24641
  } */
24635
24642
  /**
24636
- * AUTH: Prepare client signed transaction
24643
+ * AUTH: Prepare client signed transaction (Create new transaction for signing)
24637
24644
  *
24638
- * UPDATED: /transaction/auth/prepare-signing → /transactions/prepare
24645
+ * UPDATED: /transaction/auth/prepare-signing → /transactions (POST)
24639
24646
  */
24640
24647
  async prepareClientSignedTransaction(request) {
24641
24648
  return this.apiClient.post(`${this.basePath}`, request);
@@ -24659,45 +24666,7 @@ class TransactionApi {
24659
24666
  * UPDATED: /transaction/admin → /transactions (same endpoint, better structure)
24660
24667
  */
24661
24668
  async getPaginatedTransactions(params) {
24662
- // Build query parameters
24663
- const queryParams = {
24664
- page: params.page.toString(),
24665
- limit: params.limit.toString()
24666
- };
24667
- // Add sorting parameters if provided
24668
- if (params.sortBy) {
24669
- queryParams['sortBy'] = params.sortBy;
24670
- }
24671
- if (params.sortOrder) {
24672
- queryParams['sortOrder'] = params.sortOrder;
24673
- }
24674
- // Add user-specific filtering if provided
24675
- if (params.participantId) {
24676
- queryParams['participantId'] = params.participantId;
24677
- }
24678
- // Add status filtering if provided
24679
- if (params.status) {
24680
- queryParams['status'] = params.status;
24681
- }
24682
- // Add additional filters if provided
24683
- if (params.filters) {
24684
- if (params.filters.startDate) {
24685
- queryParams['startDate'] = params.filters.startDate;
24686
- }
24687
- if (params.filters.endDate) {
24688
- queryParams['endDate'] = params.filters.endDate;
24689
- }
24690
- if (params.filters.type && params.filters.type.length > 0) {
24691
- queryParams['type'] = params.filters.type.join(',');
24692
- }
24693
- if (params.filters.tokenType && params.filters.tokenType.length > 0) {
24694
- queryParams['tokenType'] = params.filters.tokenType.join(',');
24695
- }
24696
- }
24697
- // Build query string
24698
- const queryString = Object.entries(queryParams)
24699
- .map(([key, value]) => `${key}=${encodeURIComponent(value)}`)
24700
- .join('&');
24669
+ const queryString = this.buildQueryParams(params).toString();
24701
24670
  return this.apiClient.get(`${this.basePath}?${queryString}`);
24702
24671
  }
24703
24672
  /**
@@ -24750,6 +24719,43 @@ class TransactionApi {
24750
24719
  async getTransactionAnalytics(analyticsRequest) {
24751
24720
  return this.apiClient.post(`${this.basePath}/analytics`, analyticsRequest);
24752
24721
  }
24722
+ /**
24723
+ * Helper to convert DTO object to URLSearchParams
24724
+ * Handles nested 'filters' object and arrays correctly.
24725
+ */
24726
+ buildQueryParams(params) {
24727
+ const query = new URLSearchParams();
24728
+ // 1. Handle Root Pagination Fields
24729
+ if (params.page !== undefined)
24730
+ query.append('page', params.page.toString());
24731
+ if (params.limit !== undefined)
24732
+ query.append('limit', params.limit.toString());
24733
+ if (params.sortBy)
24734
+ query.append('sortBy', params.sortBy);
24735
+ if (params.sortOrder)
24736
+ query.append('sortOrder', params.sortOrder);
24737
+ // 2. Handle Nested Filters
24738
+ if (params.filters) {
24739
+ Object.entries(params.filters).forEach(([key, value]) => {
24740
+ // Skip undefined/null values
24741
+ if (value === undefined || value === null || value === '')
24742
+ return;
24743
+ if (Array.isArray(value)) {
24744
+ // Handle Arrays: NestJS expects 'status=A&status=B'
24745
+ value.forEach((item) => query.append(key, String(item)));
24746
+ }
24747
+ else {
24748
+ // Handle Single Values: 'search=0x123'
24749
+ // NOTE: Backend Controller expects flat query params for filters
24750
+ // e.g. ?search=...&status=... NOT ?filters[search]=...
24751
+ // This mapping flattens 'filters.search' -> 'search' to match @Query('search') in Controller
24752
+ const stringValue = (value instanceof Date) ? value.toISOString() : String(value);
24753
+ query.append(key, stringValue);
24754
+ }
24755
+ });
24756
+ }
24757
+ return query;
24758
+ }
24753
24759
  }
24754
24760
 
24755
24761
  /**
@@ -24792,17 +24798,17 @@ class TransactionService {
24792
24798
  return this.transactionApi.getUserTransactionHistory(role, limit);
24793
24799
  }
24794
24800
  /**
24795
- * AUTH: Prepare client signed transaction
24801
+ * AUTH: Prepare existing transaction for client-side signing
24796
24802
  */
24797
- /* async prepareClientSignedTransaction(request: TransactionRequestDTO): Promise<TransactionRequestResponseDTO> {
24798
- return this.transactionApi.prepareClientSignedTransaction(request);
24799
- } */
24803
+ async prepareExistingTransaction(transactionId) {
24804
+ return this.transactionApi.prepareExistingTransaction(transactionId);
24805
+ }
24800
24806
  /**
24801
- * AUTH: Burn user tokens
24807
+ * AUTH: Prepare client signed transaction
24802
24808
  */
24803
- /* async burnUserTokens(request: UserBurnTokenRequestDTO): Promise<TransactionRequestResponseDTO> {
24804
- return this.transactionApi.burnUserTokens(request);
24805
- } */
24809
+ async prepareClientSignedTransaction(request) {
24810
+ return this.transactionApi.prepareClientSignedTransaction(request);
24811
+ }
24806
24812
  // ==========================================
24807
24813
  // BUSINESS OPERATIONS
24808
24814
  // ==========================================
@@ -24839,6 +24845,27 @@ class TransactionService {
24839
24845
  async exportTransactionsCSV() {
24840
24846
  return this.transactionApi.exportTransactionsCSV();
24841
24847
  }
24848
+ // ==========================================
24849
+ // QUERY & ANALYTICS OPERATIONS
24850
+ // ==========================================
24851
+ /**
24852
+ * Query transactions by sender
24853
+ */
24854
+ async queryTransactionsBySender(accountSelector) {
24855
+ return this.transactionApi.queryTransactionsBySender(accountSelector);
24856
+ }
24857
+ /**
24858
+ * Query transactions by recipient
24859
+ */
24860
+ async queryTransactionsByRecipient(accountSelector) {
24861
+ return this.transactionApi.queryTransactionsByRecipient(accountSelector);
24862
+ }
24863
+ /**
24864
+ * ADMIN: Get transaction analytics
24865
+ */
24866
+ async getTransactionAnalytics(analyticsRequest) {
24867
+ return this.transactionApi.getTransactionAnalytics(analyticsRequest);
24868
+ }
24842
24869
  }
24843
24870
 
24844
24871
  /**
@@ -26172,14 +26199,19 @@ const AUTH_STORAGE_KEYS = {
26172
26199
  };
26173
26200
  /**
26174
26201
  * LocalStorage implementation
26202
+ * Note: Forces string conversion for compatibility
26175
26203
  */
26176
26204
  class LocalStorageTokenStorage {
26205
+ constructor() {
26206
+ this.supportsObjects = false;
26207
+ }
26177
26208
  async get(key) {
26178
26209
  return typeof localStorage !== 'undefined' ? localStorage.getItem(key) : null;
26179
26210
  }
26180
26211
  async set(key, value) {
26181
26212
  if (typeof localStorage !== 'undefined') {
26182
- localStorage.setItem(key, value);
26213
+ const stringValue = typeof value === 'string' ? value : JSON.stringify(value);
26214
+ localStorage.setItem(key, stringValue);
26183
26215
  }
26184
26216
  }
26185
26217
  async remove(key) {
@@ -26200,6 +26232,7 @@ class LocalStorageTokenStorage {
26200
26232
  */
26201
26233
  class MemoryTokenStorage {
26202
26234
  constructor() {
26235
+ this.supportsObjects = true;
26203
26236
  this.store = new Map();
26204
26237
  }
26205
26238
  async get(key) {
@@ -26223,26 +26256,30 @@ class AuthTokenManager {
26223
26256
  this.storage = storage;
26224
26257
  }
26225
26258
  async getAccessToken() {
26226
- return this.storage.get(AUTH_STORAGE_KEYS.ACCESS_TOKEN);
26259
+ const val = await this.storage.get(AUTH_STORAGE_KEYS.ACCESS_TOKEN);
26260
+ // Ensure we return string for tokens
26261
+ return typeof val === 'string' ? val : null;
26227
26262
  }
26228
26263
  async setAccessToken(token) {
26229
26264
  await this.storage.set(AUTH_STORAGE_KEYS.ACCESS_TOKEN, token);
26230
26265
  }
26231
26266
  async getRefreshToken() {
26232
- return this.storage.get(AUTH_STORAGE_KEYS.REFRESH_TOKEN);
26267
+ const val = await this.storage.get(AUTH_STORAGE_KEYS.REFRESH_TOKEN);
26268
+ return typeof val === 'string' ? val : null;
26233
26269
  }
26234
26270
  async setRefreshToken(token) {
26235
26271
  await this.storage.set(AUTH_STORAGE_KEYS.REFRESH_TOKEN, token);
26236
26272
  }
26237
26273
  async getProviderToken() {
26238
- return this.storage.get(AUTH_STORAGE_KEYS.PROVIDER_TOKEN);
26274
+ const val = await this.storage.get(AUTH_STORAGE_KEYS.PROVIDER_TOKEN);
26275
+ return typeof val === 'string' ? val : null;
26239
26276
  }
26240
26277
  async setProviderToken(token) {
26241
26278
  await this.storage.set(AUTH_STORAGE_KEYS.PROVIDER_TOKEN, token);
26242
26279
  }
26243
26280
  async getAuthType() {
26244
26281
  const authType = await this.storage.get(AUTH_STORAGE_KEYS.AUTH_TYPE);
26245
- return authType;
26282
+ return typeof authType === 'string' ? authType : null;
26246
26283
  }
26247
26284
  async setAuthType(authType) {
26248
26285
  await this.storage.set(AUTH_STORAGE_KEYS.AUTH_TYPE, authType);
@@ -26267,6 +26304,218 @@ class AuthTokenManager {
26267
26304
  }
26268
26305
  }
26269
26306
 
26307
+ class WebDPoPCryptoProvider {
26308
+ get crypto() {
26309
+ // Basic environment check
26310
+ if (typeof crypto !== 'undefined')
26311
+ return crypto;
26312
+ if (typeof window !== 'undefined' && window.crypto)
26313
+ return window.crypto;
26314
+ if (typeof globalThis !== 'undefined' && globalThis.crypto)
26315
+ return globalThis.crypto;
26316
+ throw new Error('WebCrypto API not found. Please polyfill crypto.subtle or use a different DPoPCryptoProvider.');
26317
+ }
26318
+ async generateKeyPair(options) {
26319
+ const extractable = options?.extractable ?? true;
26320
+ const keyPair = await this.crypto.subtle.generateKey({
26321
+ name: 'ECDSA',
26322
+ namedCurve: 'P-256',
26323
+ }, extractable, // Configurable extractability
26324
+ ['sign', 'verify']);
26325
+ const publicKey = await this.crypto.subtle.exportKey('jwk', keyPair.publicKey);
26326
+ // If extractable, export private key as JWK (plain object)
26327
+ // If NOT extractable, keep it as CryptoKey (native handle)
26328
+ // However, DPoPKeyPair interface currently expects JsonWebKey | CryptoKey
26329
+ // We need to check DPoPKeyPair type definition.
26330
+ // Wait, DPoPKeyPair definition in dpop.types.ts says:
26331
+ // publicKey: JsonWebKey; privateKey: JsonWebKey;
26332
+ // I need to change that to support CryptoKey.
26333
+ // Let's re-read dpop.types.ts
26334
+ // I assumed it might be compatible but let's verify.
26335
+ // If I change privateKey to unknown or JsonWebKey | CryptoKey, it will be safer.
26336
+ let privateKey;
26337
+ if (extractable) {
26338
+ privateKey = await this.crypto.subtle.exportKey('jwk', keyPair.privateKey);
26339
+ }
26340
+ else {
26341
+ privateKey = keyPair.privateKey;
26342
+ }
26343
+ return { publicKey, privateKey };
26344
+ }
26345
+ async signProof(payload, keyPair) {
26346
+ // 1. Prepare Header
26347
+ const header = {
26348
+ typ: 'dpop+jwt',
26349
+ alg: 'ES256',
26350
+ jwk: keyPair.publicKey
26351
+ };
26352
+ // 2. Prepare Payload
26353
+ // Ensure jti and iat if not present (though Manager usually provides them)
26354
+ const finalPayload = {
26355
+ ...payload,
26356
+ iat: payload.iat || Math.floor(Date.now() / 1000),
26357
+ jti: payload.jti || this.generateUUID()
26358
+ };
26359
+ // 3. Encode segments
26360
+ const encodedHeader = this.base64UrlEncode(JSON.stringify(header));
26361
+ const encodedPayload = this.base64UrlEncode(JSON.stringify(finalPayload));
26362
+ const signingInput = `${encodedHeader}.${encodedPayload}`;
26363
+ // 4. Import private key for signing
26364
+ let privateKey;
26365
+ // Check if it's already a CryptoKey (non-extractable handle)
26366
+ // We check for 'algorithm' property which signals a CryptoKey object
26367
+ if (keyPair.privateKey.algorithm) {
26368
+ privateKey = keyPair.privateKey;
26369
+ }
26370
+ else {
26371
+ // Otherwise import from JWK
26372
+ privateKey = await this.crypto.subtle.importKey('jwk', keyPair.privateKey, {
26373
+ name: 'ECDSA',
26374
+ namedCurve: 'P-256',
26375
+ }, false, ['sign']);
26376
+ }
26377
+ // 5. Sign
26378
+ const signatureBuffer = await this.crypto.subtle.sign({
26379
+ name: 'ECDSA',
26380
+ hash: { name: 'SHA-256' },
26381
+ }, privateKey, new TextEncoder().encode(signingInput));
26382
+ const encodedSignature = this.base64UrlEncodeBuffer(signatureBuffer);
26383
+ return `${signingInput}.${encodedSignature}`;
26384
+ }
26385
+ async hashAccessToken(accessToken) {
26386
+ const encoder = new TextEncoder();
26387
+ const data = encoder.encode(accessToken);
26388
+ const hashBuffer = await this.crypto.subtle.digest('SHA-256', data);
26389
+ return this.base64UrlEncodeBuffer(hashBuffer);
26390
+ }
26391
+ // --- Helpers ---
26392
+ generateUUID() {
26393
+ if (typeof crypto !== 'undefined' && crypto.randomUUID) {
26394
+ return crypto.randomUUID();
26395
+ }
26396
+ // Fallback for older envs
26397
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
26398
+ const r = (Math.random() * 16) | 0;
26399
+ const v = c === 'x' ? r : (r & 0x3) | 0x8;
26400
+ return v.toString(16);
26401
+ });
26402
+ }
26403
+ base64UrlEncode(str) {
26404
+ // Use TextEncoder to handle UTF-8 properly
26405
+ const encoder = new TextEncoder();
26406
+ return this.base64UrlEncodeBuffer(encoder.encode(str));
26407
+ }
26408
+ base64UrlEncodeBuffer(buffer) {
26409
+ const bytes = new Uint8Array(buffer);
26410
+ let binary = '';
26411
+ const len = bytes.byteLength;
26412
+ // Chunk processing for large buffers if needed, but proofs are small
26413
+ for (let i = 0; i < len; i++) {
26414
+ binary += String.fromCharCode(bytes[i]);
26415
+ }
26416
+ // btoa is widely available in browsers.
26417
+ // In Node (non-globals), we might need Buffer.
26418
+ // For universal 'platform agnostic' aiming at standard web-like envs:
26419
+ let base64 = '';
26420
+ if (typeof btoa === 'function') {
26421
+ base64 = btoa(binary);
26422
+ }
26423
+ else if (typeof Buffer !== 'undefined') {
26424
+ base64 = Buffer.from(binary, 'binary').toString('base64');
26425
+ }
26426
+ else {
26427
+ throw new Error('Base64 encoding not supported in this environment');
26428
+ }
26429
+ return base64
26430
+ .replace(/\+/g, '-')
26431
+ .replace(/\//g, '_')
26432
+ .replace(/=+$/, '');
26433
+ }
26434
+ }
26435
+
26436
+ const DPOP_STORAGE_KEYS = {
26437
+ PUBLIC: 'pers_dpop_public_key',
26438
+ PRIVATE: 'pers_dpop_private_key'
26439
+ };
26440
+ class DPoPManager {
26441
+ constructor(storage, cryptoProvider) {
26442
+ this.storage = storage;
26443
+ this.memoryKeyPair = null;
26444
+ this.cryptoProvider = cryptoProvider || new WebDPoPCryptoProvider();
26445
+ }
26446
+ /**
26447
+ * Ensures a DPoP key pair exists, loading from storage or generating new one.
26448
+ */
26449
+ async ensureKeyPair() {
26450
+ if (this.memoryKeyPair)
26451
+ return this.memoryKeyPair;
26452
+ const storedPublic = await this.storage.get(DPOP_STORAGE_KEYS.PUBLIC);
26453
+ const storedPrivate = await this.storage.get(DPOP_STORAGE_KEYS.PRIVATE);
26454
+ if (storedPublic && storedPrivate) {
26455
+ try {
26456
+ // Handle polymorphic storage: String (JSON) or Object (CryptoKey)
26457
+ const publicKey = typeof storedPublic === 'string' ? JSON.parse(storedPublic) : storedPublic;
26458
+ const privateKey = typeof storedPrivate === 'string' ? JSON.parse(storedPrivate) : storedPrivate;
26459
+ this.memoryKeyPair = { publicKey, privateKey };
26460
+ return this.memoryKeyPair;
26461
+ }
26462
+ catch (e) {
26463
+ console.warn('Corrupted DPoP keys in storage, regenerating.');
26464
+ }
26465
+ }
26466
+ // Generate new key pair
26467
+ // Adaptation: If storage supports raw objects (like IndexedDB or Native Keychain),
26468
+ // we can generate Non-Extractable keys for maximum security.
26469
+ // If storage is text-only (LocalStorage), we must use Extractable keys to serialize them.
26470
+ const useHighSecurity = !!this.storage.supportsObjects;
26471
+ const keyPair = await this.cryptoProvider.generateKeyPair({
26472
+ extractable: !useHighSecurity
26473
+ });
26474
+ // Save to storage
26475
+ await this.storage.set(DPOP_STORAGE_KEYS.PUBLIC, keyPair.publicKey);
26476
+ await this.storage.set(DPOP_STORAGE_KEYS.PRIVATE, keyPair.privateKey);
26477
+ this.memoryKeyPair = keyPair;
26478
+ return keyPair;
26479
+ }
26480
+ /**
26481
+ * Generates the DPoP proof header value.
26482
+ * @param method HTTP method
26483
+ * @param url Request URL
26484
+ * @param accessToken Optional access token to bind the proof to (ath claim)
26485
+ */
26486
+ async generateProofHeader(method, url, accessToken) {
26487
+ const keyPair = await this.ensureKeyPair();
26488
+ const payload = {
26489
+ htm: method,
26490
+ htu: url,
26491
+ };
26492
+ if (accessToken) {
26493
+ payload.ath = await this.cryptoProvider.hashAccessToken(accessToken);
26494
+ }
26495
+ return this.cryptoProvider.signProof(payload, keyPair);
26496
+ }
26497
+ /**
26498
+ * Clears DPoP keys (e.g. on logout)
26499
+ */
26500
+ async clearKeys() {
26501
+ this.memoryKeyPair = null;
26502
+ await this.storage.remove(DPOP_STORAGE_KEYS.PUBLIC);
26503
+ await this.storage.remove(DPOP_STORAGE_KEYS.PRIVATE);
26504
+ }
26505
+ /**
26506
+ * Gets the public key (for debugging/inspection)
26507
+ */
26508
+ async getPublicKey() {
26509
+ try {
26510
+ const kp = await this.ensureKeyPair();
26511
+ return kp.publicKey;
26512
+ }
26513
+ catch {
26514
+ return null;
26515
+ }
26516
+ }
26517
+ }
26518
+
26270
26519
  /**
26271
26520
  * Default Authentication Provider
26272
26521
  * Simple implementation of PERS authentication with token lifecycle management
@@ -26280,6 +26529,9 @@ class DefaultAuthProvider {
26280
26529
  this.authType = config.authType || 'user';
26281
26530
  const storage = config.storage || this.createStorage();
26282
26531
  this.tokenManager = new AuthTokenManager(storage);
26532
+ if (config.dpop?.enabled) {
26533
+ this.dpopManager = new DPoPManager(storage, config.dpop.cryptoProvider);
26534
+ }
26283
26535
  }
26284
26536
  createStorage() {
26285
26537
  return typeof window === 'undefined' || typeof localStorage === 'undefined'
@@ -26289,6 +26541,26 @@ class DefaultAuthProvider {
26289
26541
  getToken() {
26290
26542
  return this.tokenManager.getAccessToken();
26291
26543
  }
26544
+ async getAuthHeaders(token, method, url) {
26545
+ const headers = {};
26546
+ if (this.dpopManager && method && url) {
26547
+ // Generate DPoP Proof
26548
+ // Note: For 'bypassAuth' requests (token is null), we still generate proof (bound to public key, no ath)
26549
+ // This covers Token Requests (Login) which need DPoP header to bind the issued token.
26550
+ const proof = await this.dpopManager.generateProofHeader(method, url, token || undefined);
26551
+ headers['DPoP'] = proof;
26552
+ if (token) {
26553
+ headers['Authorization'] = `DPoP ${token}`;
26554
+ }
26555
+ }
26556
+ else {
26557
+ // Standard Bearer
26558
+ if (token) {
26559
+ headers['Authorization'] = `Bearer ${token}`;
26560
+ }
26561
+ }
26562
+ return headers;
26563
+ }
26292
26564
  async getProjectKey() {
26293
26565
  return this.config.projectKey || null;
26294
26566
  }
@@ -26323,6 +26595,9 @@ class DefaultAuthProvider {
26323
26595
  }
26324
26596
  async clearTokens() {
26325
26597
  await this.tokenManager.clearAllTokens();
26598
+ if (this.dpopManager) {
26599
+ await this.dpopManager.clearKeys();
26600
+ }
26326
26601
  }
26327
26602
  async setTokens(accessToken, refreshToken, providerToken) {
26328
26603
  await this.tokenManager.setTokens(accessToken, refreshToken, providerToken);
@@ -26371,7 +26646,11 @@ class PersApiClient {
26371
26646
  this.mergedConfig.authProvider = new DefaultAuthProvider({
26372
26647
  authType: this.mergedConfig.authType || 'user',
26373
26648
  projectKey: this.mergedConfig.apiProjectKey,
26374
- storage: this.mergedConfig.authStorage // Support custom storage
26649
+ storage: this.mergedConfig.authStorage, // Support custom storage
26650
+ dpop: {
26651
+ enabled: this.mergedConfig.dpop?.enabled ?? true, // Enable DPoP by default
26652
+ cryptoProvider: this.mergedConfig.dpop?.cryptoProvider
26653
+ }
26375
26654
  });
26376
26655
  }
26377
26656
  this.authService = new AuthService(this.authApi, this.mergedConfig.authProvider);
@@ -26434,7 +26713,7 @@ class PersApiClient {
26434
26713
  });
26435
26714
  }
26436
26715
  const requestOptions = {
26437
- headers: await this.getHeaders(!bypassAuth),
26716
+ headers: await this.getHeaders(!bypassAuth, method, url),
26438
26717
  timeout: this.mergedConfig.timeout,
26439
26718
  responseType
26440
26719
  };
@@ -26529,26 +26808,45 @@ class PersApiClient {
26529
26808
  * Get request headers with optional auth token and project key
26530
26809
  *
26531
26810
  * @param includeAuth - Whether to include the Authorization header (default: true)
26811
+ * @param method - HTTP Method (required for DPoP)
26812
+ * @param url - Full Request URL (required for DPoP)
26532
26813
  */
26533
- async getHeaders(includeAuth = true) {
26814
+ async getHeaders(includeAuth = true, method, url) {
26534
26815
  const headers = {
26535
26816
  'Content-Type': 'application/json',
26536
26817
  };
26537
- if (includeAuth) {
26538
- // Add authentication token
26539
- if (this.mergedConfig.authProvider) {
26540
- const token = await this.mergedConfig.authProvider.getToken();
26541
- if (token) {
26542
- headers['Authorization'] = `Bearer ${token}`;
26818
+ if (this.mergedConfig.authProvider) {
26819
+ let token = null;
26820
+ if (includeAuth) {
26821
+ token = await this.mergedConfig.authProvider.getToken();
26822
+ }
26823
+ // Handle Authentication Headers (Bearer or DPoP)
26824
+ if (this.mergedConfig.authProvider.getAuthHeaders) {
26825
+ // Provider supports advanced headers (DPoP)
26826
+ // We pass token (null if includeAuth=false) so it can generate DPoP proofs for login requests too
26827
+ try {
26828
+ const authHeaders = await this.mergedConfig.authProvider.getAuthHeaders(token, method, url);
26829
+ Object.assign(headers, authHeaders);
26543
26830
  }
26544
- else {
26545
- console.warn('[PersApiClient] No token available from auth provider');
26831
+ catch (e) {
26832
+ console.warn('[PersApiClient] Failed to generate auth headers:', e);
26833
+ // Fallback if token exists but DPoP generation failed?
26834
+ // Better to fail or let standardized fallback below handle it?
26835
+ // If DPoP fails, falling back to Bearer might be insecure if server enforces DPoP.
26836
+ // For now, valid token -> Bearer fallback logic mostly for non-DPoP legacy providers.
26546
26837
  }
26547
26838
  }
26548
- else {
26549
- console.warn('[PersApiClient] No auth provider configured');
26839
+ // Fallback: If provider didn't give headers but we have token and NO Authorization header yet
26840
+ if (includeAuth && token && !headers['Authorization']) {
26841
+ headers['Authorization'] = `Bearer ${token}`;
26842
+ }
26843
+ else if (includeAuth && !token) {
26844
+ console.warn('[PersApiClient] No token available from auth provider');
26550
26845
  }
26551
26846
  }
26847
+ else if (includeAuth) {
26848
+ console.warn('[PersApiClient] No auth provider configured');
26849
+ }
26552
26850
  // Add project key
26553
26851
  if (this.mergedConfig.authProvider) {
26554
26852
  const projectKey = await this.mergedConfig.authProvider.getProjectKey();
@@ -29567,6 +29865,75 @@ class TransactionManager {
29567
29865
  async exportTransactionsCSV() {
29568
29866
  return this.transactionService.exportTransactionsCSV();
29569
29867
  }
29868
+ /**
29869
+ * Prepare existing transaction for client-side signing
29870
+ *
29871
+ * Retrieves the necessary data to sign an existing transaction on the client side.
29872
+ * This is typically used when a transaction has been created but requires user signature.
29873
+ *
29874
+ * @param transactionId - The ID of the transaction to prepare
29875
+ * @returns Promise resolving to the transaction preparation data
29876
+ */
29877
+ async prepareExistingTransaction(transactionId) {
29878
+ return this.transactionService.prepareExistingTransaction(transactionId);
29879
+ }
29880
+ /**
29881
+ * Prepare a new transaction for client-side signing
29882
+ *
29883
+ * Creates a new transaction and immediately returns the data needed for client-side signing.
29884
+ * This combines creation and preparation into a single flow for client-signed operations.
29885
+ *
29886
+ * @param request - The transaction request details
29887
+ * @returns Promise resolving to the transaction preparation data
29888
+ */
29889
+ async prepareClientSignedTransaction(request) {
29890
+ return this.transactionService.prepareClientSignedTransaction(request);
29891
+ }
29892
+ /**
29893
+ * Submit a signed transaction
29894
+ *
29895
+ * Submits a transaction that has been signed on the client side to the backend for processing.
29896
+ *
29897
+ * @param signedTxData - The signed transaction data
29898
+ * @returns Promise resolving to the submission result
29899
+ */
29900
+ async submitSignedTransaction(signedTxData) {
29901
+ return this.transactionService.submitSignedTransaction(signedTxData);
29902
+ }
29903
+ /**
29904
+ * Query transactions by sender
29905
+ *
29906
+ * Retrieves transactions where the specified account is the sender.
29907
+ *
29908
+ * @param accountSelector - Selector for the sender account
29909
+ * @returns Promise resolving to paginated transactions
29910
+ */
29911
+ async queryTransactionsBySender(accountSelector) {
29912
+ return this.transactionService.queryTransactionsBySender(accountSelector);
29913
+ }
29914
+ /**
29915
+ * Query transactions by recipient
29916
+ *
29917
+ * Retrieves transactions where the specified account is the recipient.
29918
+ *
29919
+ * @param accountSelector - Selector for the recipient account
29920
+ * @returns Promise resolving to paginated transactions
29921
+ */
29922
+ async queryTransactionsByRecipient(accountSelector) {
29923
+ return this.transactionService.queryTransactionsByRecipient(accountSelector);
29924
+ }
29925
+ /**
29926
+ * Get transaction analytics
29927
+ *
29928
+ * Retrieves analytics data for transactions based on the provided request criteria.
29929
+ * Useful for generating reports and dashboards.
29930
+ *
29931
+ * @param analyticsRequest - The analytics query parameters
29932
+ * @returns Promise resolving to the analytics data
29933
+ */
29934
+ async getTransactionAnalytics(analyticsRequest) {
29935
+ return this.transactionService.getTransactionAnalytics(analyticsRequest);
29936
+ }
29570
29937
  /**
29571
29938
  * Get the full transaction service for advanced operations
29572
29939
  *
@@ -31677,6 +32044,153 @@ class PersSDK {
31677
32044
  }
31678
32045
  }
31679
32046
 
32047
+ // Conditionally require Keychain to avoid Web bundler errors
32048
+ let Keychain;
32049
+ if (reactNative.Platform.OS !== 'web') {
32050
+ try {
32051
+ Keychain = require('react-native-keychain');
32052
+ }
32053
+ catch (e) {
32054
+ console.warn('ReactNativeSecureStorage: Failed to load react-native-keychain', e);
32055
+ }
32056
+ }
32057
+ /**
32058
+ * Secure Storage implementation for React Native
32059
+ * Uses Keychain/Keystore for sensitive data and AsyncStorage for non-sensitive data.
32060
+ */
32061
+ class ReactNativeSecureStorage {
32062
+ constructor(keyPrefix = 'pers_tokens_') {
32063
+ this.keyPrefix = keyPrefix;
32064
+ // Set to false because Keychain stores strings, so we need extractable (serializable) keys
32065
+ this.supportsObjects = false;
32066
+ // Define which keys MUST go to secure hardware storage
32067
+ this.SECURE_KEYS = new Set([
32068
+ DPOP_STORAGE_KEYS.PRIVATE,
32069
+ AUTH_STORAGE_KEYS.ACCESS_TOKEN,
32070
+ AUTH_STORAGE_KEYS.REFRESH_TOKEN,
32071
+ AUTH_STORAGE_KEYS.PROVIDER_TOKEN
32072
+ ]);
32073
+ }
32074
+ async set(key, value) {
32075
+ const prefixedKey = this.getKeyName(key);
32076
+ const stringValue = typeof value === 'string' ? value : JSON.stringify(value);
32077
+ // Cast key to any to bypass strict literal type checking of the Set.has method
32078
+ // In runtime, 'key' is just a string, so this is safe.
32079
+ if (this.SECURE_KEYS.has(key)) {
32080
+ // Store in Keychain/Keystore
32081
+ // Service name acts as the key identifier in simple usage
32082
+ await Keychain.setGenericPassword(prefixedKey, stringValue, { service: prefixedKey });
32083
+ }
32084
+ else {
32085
+ // Store standard config/metadata in AsyncStorage
32086
+ await AsyncStorage.setItem(prefixedKey, stringValue);
32087
+ }
32088
+ }
32089
+ async get(key) {
32090
+ const prefixedKey = this.getKeyName(key);
32091
+ if (this.SECURE_KEYS.has(key)) {
32092
+ try {
32093
+ const credentials = await Keychain.getGenericPassword({ service: prefixedKey });
32094
+ if (credentials) {
32095
+ return this.tryParse(credentials.password);
32096
+ }
32097
+ return null;
32098
+ }
32099
+ catch (e) {
32100
+ console.warn(`[ReactNativeSecureStorage] Failed to access keychain for ${key}`, e);
32101
+ return null; // Key not found or access denied
32102
+ }
32103
+ }
32104
+ else {
32105
+ try {
32106
+ const val = await AsyncStorage.getItem(prefixedKey);
32107
+ return val ? this.tryParse(val) : null;
32108
+ }
32109
+ catch (e) {
32110
+ console.warn(`[ReactNativeSecureStorage] Failed to access AsyncStorage for ${key}`, e);
32111
+ return null;
32112
+ }
32113
+ }
32114
+ }
32115
+ async remove(key) {
32116
+ const prefixedKey = this.getKeyName(key);
32117
+ if (this.SECURE_KEYS.has(key)) {
32118
+ await Keychain.resetGenericPassword({ service: prefixedKey });
32119
+ }
32120
+ else {
32121
+ await AsyncStorage.removeItem(prefixedKey);
32122
+ }
32123
+ }
32124
+ async clear() {
32125
+ // Clear all known secure keys
32126
+ for (const key of this.SECURE_KEYS) {
32127
+ await Keychain.resetGenericPassword({ service: this.getKeyName(key) });
32128
+ }
32129
+ // Clear AsyncStorage keys related to PERS
32130
+ try {
32131
+ const allKeys = await AsyncStorage.getAllKeys();
32132
+ const ourKeys = allKeys.filter(k => k.startsWith(this.keyPrefix));
32133
+ if (ourKeys.length > 0) {
32134
+ await AsyncStorage.multiRemove(ourKeys);
32135
+ }
32136
+ }
32137
+ catch (e) {
32138
+ console.warn('[ReactNativeSecureStorage] Failed to clear AsyncStorage', e);
32139
+ }
32140
+ }
32141
+ getKeyName(key) {
32142
+ // For Keychain, we might want to avoid prefixes if we want to share across apps,
32143
+ // but for simple isolation, prefix is fine to avoid collisions if multiple instances exist.
32144
+ // However, Keychain services are global to the app bundle ID usually.
32145
+ return `${this.keyPrefix}${key}`;
32146
+ }
32147
+ tryParse(val) {
32148
+ try {
32149
+ return JSON.parse(val);
32150
+ }
32151
+ catch {
32152
+ return val;
32153
+ }
32154
+ }
32155
+ }
32156
+
32157
+ /**
32158
+ * React Native Unified Auth Provider
32159
+ *
32160
+ * Creates a platform-specific auth provider that automatically selects the appropriate storage:
32161
+ * - Web: Uses LocalStorageTokenStorage from core SDK
32162
+ * - Mobile: Uses AsyncStorage-based storage
32163
+ */
32164
+ // Use React Native's built-in platform detection
32165
+ const isWebPlatform = reactNative.Platform.OS === 'web';
32166
+ /**
32167
+ * Create a React Native auth provider with platform-appropriate storage
32168
+ *
32169
+ * Automatically selects storage implementation:
32170
+ * - Web: Uses LocalStorageTokenStorage (from core SDK)
32171
+ * - Mobile: Uses AsyncStorageTokenStorage (React Native specific)
32172
+ *
32173
+ * @param projectKey - PERS project key
32174
+ * @param config - Configuration options
32175
+ * @returns DefaultAuthProvider configured for the current platform
32176
+ */
32177
+ function createReactNativeAuthProvider(projectKey, config = {}) {
32178
+ if (!projectKey || typeof projectKey !== 'string') {
32179
+ throw new Error('createReactNativeAuthProvider: projectKey is required and must be a string');
32180
+ }
32181
+ const { keyPrefix = `pers_${projectKey.slice(0, 8)}_`, debug = false, customStorage, authType = 'user' } = config;
32182
+ // Platform-specific storage selection
32183
+ const tokenStorage = customStorage || (isWebPlatform
32184
+ ? new LocalStorageTokenStorage()
32185
+ : new ReactNativeSecureStorage(keyPrefix));
32186
+ // Return DefaultAuthProvider configured with platform-appropriate storage
32187
+ return new DefaultAuthProvider({
32188
+ authType,
32189
+ projectKey,
32190
+ storage: tokenStorage
32191
+ });
32192
+ }
32193
+
31680
32194
  /**
31681
32195
  * AsyncStorage Token Storage for React Native Mobile Platforms
31682
32196
  *
@@ -31790,41 +32304,96 @@ class AsyncStorageTokenStorage {
31790
32304
  }
31791
32305
  }
31792
32306
 
31793
- /**
31794
- * React Native Unified Auth Provider
31795
- *
31796
- * Creates a platform-specific auth provider that automatically selects the appropriate storage:
31797
- * - Web: Uses LocalStorageTokenStorage from core SDK
31798
- * - Mobile: Uses AsyncStorage-based storage
31799
- */
31800
- // Use React Native's built-in platform detection
31801
- const isWebPlatform = reactNative.Platform.OS === 'web';
31802
- /**
31803
- * Create a React Native auth provider with platform-appropriate storage
31804
- *
31805
- * Automatically selects storage implementation:
31806
- * - Web: Uses LocalStorageTokenStorage (from core SDK)
31807
- * - Mobile: Uses AsyncStorageTokenStorage (React Native specific)
31808
- *
31809
- * @param projectKey - PERS project key
31810
- * @param config - Configuration options
31811
- * @returns DefaultAuthProvider configured for the current platform
31812
- */
31813
- function createReactNativeAuthProvider(projectKey, config = {}) {
31814
- if (!projectKey || typeof projectKey !== 'string') {
31815
- throw new Error('createReactNativeAuthProvider: projectKey is required and must be a string');
32307
+ // Conditionally require quick-crypto for Native platforms only
32308
+ let crypto$1;
32309
+ if (reactNative.Platform.OS !== 'web') {
32310
+ try {
32311
+ crypto$1 = require('react-native-quick-crypto').default || require('react-native-quick-crypto');
32312
+ }
32313
+ catch (e) {
32314
+ console.warn('ReactNativeDPoPProvider: Failed to load react-native-quick-crypto', e);
32315
+ }
32316
+ }
32317
+ else {
32318
+ // on Web, we shouldn't be using this provider anyway (Core SDK has WebDPoPProvider)
32319
+ // But to be safe, we can mock or throw
32320
+ crypto$1 = {
32321
+ generateKeyPair: () => { throw new Error('ReactNativeDPoPProvider not supported on Web'); }
32322
+ };
32323
+ }
32324
+ class ReactNativeDPoPProvider {
32325
+ /**
32326
+ * Generates a new key pair (ES256 recommended)
32327
+ *
32328
+ * Note: options.extractable is ignored because for React Native Keychain storage,
32329
+ * we MUST export the keys as JWK/strings. We rely on the Keychain encryption
32330
+ * for security at rest (High Security), making the key "Extractable" in memory
32331
+ * but protected by hardware encryption when stored.
32332
+ */
32333
+ async generateKeyPair(options) {
32334
+ // Generate P-256 Key Pair
32335
+ return new Promise((resolve, reject) => {
32336
+ crypto$1.generateKeyPair('ec', { namedCurve: 'P-256' }, (err, publicKey, privateKey) => {
32337
+ if (err)
32338
+ return reject(err);
32339
+ // Export to JWK for SDK Compatibility
32340
+ // We use 'export' because supportsObjects=false in our storage forces the SDK
32341
+ // to expect serializable keys.
32342
+ const pubJwk = publicKey.export({ format: 'jwk' });
32343
+ const privJwk = privateKey.export({ format: 'jwk' });
32344
+ resolve({
32345
+ publicKey: pubJwk,
32346
+ privateKey: privJwk
32347
+ });
32348
+ });
32349
+ });
32350
+ }
32351
+ async signProof(payload, keyPair) {
32352
+ // 1. Construct Header
32353
+ const header = {
32354
+ typ: 'dpop+jwt',
32355
+ alg: 'ES256',
32356
+ jwk: keyPair.publicKey
32357
+ };
32358
+ // 2. Add Claims (iat/jti)
32359
+ const finalPayload = {
32360
+ ...payload,
32361
+ iat: payload.iat || Math.floor(Date.now() / 1000),
32362
+ jti: payload.jti || crypto$1.randomUUID()
32363
+ };
32364
+ // 3. Encode
32365
+ const b64Header = this.base64Url(JSON.stringify(header));
32366
+ const b64Payload = this.base64Url(JSON.stringify(finalPayload));
32367
+ const signingInput = `${b64Header}.${b64Payload}`;
32368
+ // 4. Sign
32369
+ const sign = crypto$1.createSign('SHA256');
32370
+ sign.update(signingInput);
32371
+ // sign.end() is not required/available in quick-crypto as it doesn't strictly follow stream interface
32372
+ // Import private key back from JWK to sign
32373
+ // The keyPair.privateKey is a JsonWebKey object because we exported it earlier.
32374
+ // quick-crypto createPrivateKey expects jwk object if format is jwk
32375
+ const privateKeyObj = crypto$1.createPrivateKey({ key: keyPair.privateKey, format: 'jwk' });
32376
+ const signature = sign.sign(privateKeyObj);
32377
+ // signature is a Buffer in quick-crypto
32378
+ return `${signingInput}.${this.base64UrlBuffer(signature)}`;
32379
+ }
32380
+ async hashAccessToken(accessToken) {
32381
+ const hash = crypto$1.createHash('sha256').update(accessToken).digest();
32382
+ // digest returns Buffer in quick-crypto
32383
+ return this.base64UrlBuffer(hash);
32384
+ }
32385
+ // --- Helpers ---
32386
+ base64Url(str) {
32387
+ return this.base64UrlBuffer(buffer.Buffer.from(str));
32388
+ }
32389
+ base64UrlBuffer(buf) {
32390
+ // Ensure we have a Buffer
32391
+ const buffer$1 = buffer.Buffer.isBuffer(buf) ? buf : buffer.Buffer.from(buf);
32392
+ return buffer$1.toString('base64')
32393
+ .replace(/=/g, '')
32394
+ .replace(/\+/g, '-')
32395
+ .replace(/\//g, '_');
31816
32396
  }
31817
- const { keyPrefix = `pers_${projectKey.slice(0, 8)}_`, debug = false, customStorage, authType = 'user' } = config;
31818
- // Platform-specific storage selection
31819
- const tokenStorage = customStorage || (isWebPlatform
31820
- ? new LocalStorageTokenStorage()
31821
- : new AsyncStorageTokenStorage(keyPrefix));
31822
- // Return DefaultAuthProvider configured with platform-appropriate storage
31823
- return new DefaultAuthProvider({
31824
- authType,
31825
- projectKey,
31826
- storage: tokenStorage
31827
- });
31828
32397
  }
31829
32398
 
31830
32399
  /**
@@ -31958,6 +32527,15 @@ const PersSDKProvider = ({ children }) => {
31958
32527
  ...config,
31959
32528
  authProvider
31960
32529
  };
32530
+ // Inject Native DPoP Provider (crypto-based) for mobile platforms
32531
+ // Web platforms use built-in WebCrypto provider by default
32532
+ if (reactNative.Platform.OS !== 'web' && (!config.dpop?.cryptoProvider)) {
32533
+ sdkConfig.dpop = {
32534
+ ...(config.dpop || {}),
32535
+ enabled: config.dpop?.enabled ?? true,
32536
+ cryptoProvider: new ReactNativeDPoPProvider()
32537
+ };
32538
+ }
31961
32539
  // Initialize PersSDK with platform-aware auth provider
31962
32540
  const sdkInstance = new PersSDK(httpClient, sdkConfig);
31963
32541
  setAuthProvider(authProvider);
@@ -34419,7 +34997,9 @@ exports.ANALYTICS_METRIC_TYPES = ANALYTICS_METRIC_TYPES;
34419
34997
  exports.AsyncStorageTokenStorage = AsyncStorageTokenStorage;
34420
34998
  exports.NativeTokenTypes = NativeTokenTypes;
34421
34999
  exports.PersSDKProvider = PersSDKProvider;
35000
+ exports.ReactNativeDPoPProvider = ReactNativeDPoPProvider;
34422
35001
  exports.ReactNativeHttpClient = ReactNativeHttpClient;
35002
+ exports.ReactNativeSecureStorage = ReactNativeSecureStorage;
34423
35003
  exports.TRANSACTION_FORMATS = TRANSACTION_FORMATS;
34424
35004
  exports.TRANSACTION_FORMAT_DESCRIPTIONS = TRANSACTION_FORMAT_DESCRIPTIONS;
34425
35005
  exports.apiPublicKeyTestPrefix = apiPublicKeyTestPrefix;