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

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
@@ -8,10 +8,31 @@ var reactNative = require('react-native');
8
8
  require('@ethereumjs/tx');
9
9
  require('@ethereumjs/util');
10
10
  require('@ethereumjs/common');
11
+ var Keychain = require('react-native-keychain');
11
12
  var AsyncStorage = require('@react-native-async-storage/async-storage');
13
+ var crypto$1 = require('react-native-quick-crypto');
12
14
  var jsxRuntime = require('react/jsx-runtime');
13
15
  var react = require('react');
14
16
 
17
+ function _interopNamespaceDefault(e) {
18
+ var n = Object.create(null);
19
+ if (e) {
20
+ Object.keys(e).forEach(function (k) {
21
+ if (k !== 'default') {
22
+ var d = Object.getOwnPropertyDescriptor(e, k);
23
+ Object.defineProperty(n, k, d.get ? d : {
24
+ enumerable: true,
25
+ get: function () { return e[k]; }
26
+ });
27
+ }
28
+ });
29
+ }
30
+ n.default = e;
31
+ return Object.freeze(n);
32
+ }
33
+
34
+ var Keychain__namespace = /*#__PURE__*/_interopNamespaceDefault(Keychain);
35
+
15
36
  var buffer = {};
16
37
 
17
38
  var base64Js = {};
@@ -2631,6 +2652,7 @@ exports.ApiKeyType = void 0;
2631
2652
  ApiKeyType["USER_JWT_REFRESH_TOKEN"] = "USER_JWT_REFRESH_TOKEN";
2632
2653
  ApiKeyType["BLOCKCHAIN_WRITER_JWT"] = "BLOCKCHAIN_WRITER_JWT";
2633
2654
  ApiKeyType["BLOCKCHAIN_READER_JWT"] = "BLOCKCHAIN_READER_JWT";
2655
+ ApiKeyType["TRANSACTION_JWT_ACCESS_TOKEN"] = "TRANSACTION_JWT_ACCESS_TOKEN";
2634
2656
  // TODO: this needs to be removed. However, there is still dependency
2635
2657
  ApiKeyType["TENANT_LEGACY_ADMIN"] = "TENANT_ADMIN_JWT";
2636
2658
  // TENANT_SYSTEM_API_SECRET = 'TENANT_SYSTEM_API_SECRET',
@@ -2656,23 +2678,6 @@ exports.Web3ContractTypes = void 0;
2656
2678
  Web3ContractTypes["TOKEN_CONTRACT"] = "TOKEN_CONTRACT";
2657
2679
  })(exports.Web3ContractTypes || (exports.Web3ContractTypes = {}));
2658
2680
 
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
2681
  /**
2677
2682
  * Entity ownership types - defines what types of entities can own resources
2678
2683
  *
@@ -2817,6 +2822,45 @@ exports.RedemptionRedeemStatus = void 0;
2817
2822
  RedemptionRedeemStatus["FAILED"] = "FAILED"; // Processing failed
2818
2823
  })(exports.RedemptionRedeemStatus || (exports.RedemptionRedeemStatus = {}));
2819
2824
 
2825
+ /**
2826
+ * Trigger Source Types
2827
+ * Defines the different types of triggers that can activate a campaign flow
2828
+ */
2829
+ exports.TriggerSourceType = void 0;
2830
+ (function (TriggerSourceType) {
2831
+ TriggerSourceType["QR_CODE"] = "QR_CODE";
2832
+ // NFC_TAG = 'NFC_TAG',
2833
+ TriggerSourceType["API_WEBHOOK"] = "API_WEBHOOK";
2834
+ // TRANSACTION = 'TRANSACTION',
2835
+ })(exports.TriggerSourceType || (exports.TriggerSourceType = {}));
2836
+ /**
2837
+ * Source Logic Types
2838
+ * Defines how multiple trigger sources combine to activate a flow
2839
+ * Currently only ANY (OR logic) is implemented
2840
+ */
2841
+ exports.SourceLogicType = void 0;
2842
+ (function (SourceLogicType) {
2843
+ SourceLogicType["ANY"] = "any";
2844
+ // Future: ALL = 'all', SEQUENCE = 'sequence', WEIGHTED = 'weighted'
2845
+ })(exports.SourceLogicType || (exports.SourceLogicType = {}));
2846
+
2847
+ exports.CampaignConditionType = void 0;
2848
+ (function (CampaignConditionType) {
2849
+ CampaignConditionType["EQUALS"] = "EQUALS";
2850
+ CampaignConditionType["NOT_EQUALS"] = "NOT_EQUALS";
2851
+ CampaignConditionType["GREATER_THAN"] = "GREATER_THAN";
2852
+ CampaignConditionType["LESS_THAN"] = "LESS_THAN";
2853
+ CampaignConditionType["CONTAINS"] = "CONTAINS";
2854
+ CampaignConditionType["IS_PART_OF"] = "IS_PART_OF";
2855
+ })(exports.CampaignConditionType || (exports.CampaignConditionType = {}));
2856
+
2857
+ exports.CampaignTriggerType = void 0;
2858
+ (function (CampaignTriggerType) {
2859
+ CampaignTriggerType["CLAIM_BY_USER"] = "CLAIM_BY_USER";
2860
+ CampaignTriggerType["CLAIM_BY_SYSTEM"] = "CLAIM_BY_SYSTEM";
2861
+ CampaignTriggerType["CLAIM_BY_BUSINESS"] = "CLAIM_BY_BUSINESS";
2862
+ })(exports.CampaignTriggerType || (exports.CampaignTriggerType = {}));
2863
+
2820
2864
  const apiPublicKeyTestPrefix = 'pk_test_';
2821
2865
  const testnetPrefix = 'testnet-';
2822
2866
  const testnetShortPrefix = 'tn-';
@@ -2937,8 +2981,6 @@ const NativeTokenTypes = {
2937
2981
  ERC721: 'ERC721'
2938
2982
  };
2939
2983
 
2940
- /* // ✅ EXPRESSION ALIASES: Results from CASE WHEN expressions and other computed fields
2941
- & { [key: string]: string | number | Date | undefined }; */
2942
2984
  const ANALYTICS_METRIC_TYPES = ['count', 'sum', 'avg', 'min', 'max'];
2943
2985
  const ANALYTICS_GROUP_BY_TYPES = ['month', 'week', 'day', 'year', 'quarter'];
2944
2986
 
@@ -23836,12 +23878,12 @@ class CampaignApi {
23836
23878
  params.push(`tag=${encodeURIComponent(options.tag)}`);
23837
23879
  if (options.limit)
23838
23880
  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}`);
23881
+ if (options.page)
23882
+ params.push(`page=${options.page}`);
23883
+ if (options.sortBy)
23884
+ params.push(`sortBy=${options.sortBy}`);
23885
+ if (options.sortOrder)
23886
+ params.push(`sortOrder=${options.sortOrder}`);
23845
23887
  }
23846
23888
  if (params.length > 0) {
23847
23889
  url += `?${params.join('&')}`;
@@ -23950,10 +23992,25 @@ class CampaignApi {
23950
23992
  // ==========================================
23951
23993
  /**
23952
23994
  * PUBLIC: Get campaign triggers catalog
23953
- * NEW: GET /campaign-triggers
23995
+ * NEW: GET /trigger-sources
23954
23996
  */
23955
- async getCampaignTriggers() {
23956
- return this.apiClient.get(`/campaign-triggers`);
23997
+ async getCampaignTriggers(options) {
23998
+ let url = '/trigger-sources';
23999
+ const params = [];
24000
+ if (options) {
24001
+ if (options.limit)
24002
+ params.push(`limit=${options.limit}`);
24003
+ if (options.page)
24004
+ params.push(`page=${options.page}`);
24005
+ if (options.sortBy)
24006
+ params.push(`sortBy=${options.sortBy}`);
24007
+ if (options.sortOrder)
24008
+ params.push(`sortOrder=${options.sortOrder}`);
24009
+ }
24010
+ if (params.length > 0) {
24011
+ url += `?${params.join('&')}`;
24012
+ }
24013
+ return this.apiClient.get(url);
23957
24014
  }
23958
24015
  /**
23959
24016
  * ADMIN: Create campaign trigger
@@ -23983,26 +24040,12 @@ class CampaignApi {
23983
24040
  async setCampaignTrigger(campaignId, triggerId) {
23984
24041
  return this.apiClient.put(`/campaign-triggers/${triggerId}/assign/${campaignId}`, {});
23985
24042
  }
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
24043
  /**
24001
24044
  * ADMIN: Add/Remove condition to trigger
24002
24045
  * NEW: PUT /campaign-triggers/{triggerId}/conditions/{conditionId}
24003
24046
  */
24004
- async addOrRemoveConditionToTrigger(triggerId, conditionId) {
24005
- return this.apiClient.put(`/campaign-triggers/${triggerId}/conditions/${conditionId}`, {});
24047
+ async addOrRemoveConditionToTrigger(triggerId, condition) {
24048
+ return this.apiClient.put(`/campaign-triggers/${triggerId}/conditions`, condition);
24006
24049
  }
24007
24050
  // ==========================================
24008
24051
  // BUSINESS ENGAGEMENTS (/campaign-engagements)
@@ -24589,26 +24632,11 @@ class TransactionApi {
24589
24632
  /**
24590
24633
  * AUTH: Submit client-side signed transaction
24591
24634
  *
24592
- * NEW ENDPOINT: POST /transactions/{id}/submit
24635
+ * NEW ENDPOINT: POST /transactions/submit
24593
24636
  */
24594
24637
  async submitSignedTransaction(signedTxData) {
24595
24638
  return this.apiClient.post(`${this.basePath}/submit`, signedTxData);
24596
24639
  }
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
24640
  // ==========================================
24613
24641
  // BUSINESS OPERATIONS
24614
24642
  // ==========================================
@@ -24633,9 +24661,9 @@ class TransactionApi {
24633
24661
  return this.apiClient.post<TransactionDTO>(`${this.basePath}/system`, request);
24634
24662
  } */
24635
24663
  /**
24636
- * AUTH: Prepare client signed transaction
24664
+ * AUTH: Prepare client signed transaction (Create new transaction for signing)
24637
24665
  *
24638
- * UPDATED: /transaction/auth/prepare-signing → /transactions/prepare
24666
+ * UPDATED: /transaction/auth/prepare-signing → /transactions (POST)
24639
24667
  */
24640
24668
  async prepareClientSignedTransaction(request) {
24641
24669
  return this.apiClient.post(`${this.basePath}`, request);
@@ -24659,45 +24687,7 @@ class TransactionApi {
24659
24687
  * UPDATED: /transaction/admin → /transactions (same endpoint, better structure)
24660
24688
  */
24661
24689
  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('&');
24690
+ const queryString = this.buildQueryParams(params).toString();
24701
24691
  return this.apiClient.get(`${this.basePath}?${queryString}`);
24702
24692
  }
24703
24693
  /**
@@ -24750,6 +24740,43 @@ class TransactionApi {
24750
24740
  async getTransactionAnalytics(analyticsRequest) {
24751
24741
  return this.apiClient.post(`${this.basePath}/analytics`, analyticsRequest);
24752
24742
  }
24743
+ /**
24744
+ * Helper to convert DTO object to URLSearchParams
24745
+ * Handles nested 'filters' object and arrays correctly.
24746
+ */
24747
+ buildQueryParams(params) {
24748
+ const query = new URLSearchParams();
24749
+ // 1. Handle Root Pagination Fields
24750
+ if (params.page !== undefined)
24751
+ query.append('page', params.page.toString());
24752
+ if (params.limit !== undefined)
24753
+ query.append('limit', params.limit.toString());
24754
+ if (params.sortBy)
24755
+ query.append('sortBy', params.sortBy);
24756
+ if (params.sortOrder)
24757
+ query.append('sortOrder', params.sortOrder);
24758
+ // 2. Handle Nested Filters
24759
+ if (params.filters) {
24760
+ Object.entries(params.filters).forEach(([key, value]) => {
24761
+ // Skip undefined/null values
24762
+ if (value === undefined || value === null || value === '')
24763
+ return;
24764
+ if (Array.isArray(value)) {
24765
+ // Handle Arrays: NestJS expects 'status=A&status=B'
24766
+ value.forEach((item) => query.append(key, String(item)));
24767
+ }
24768
+ else {
24769
+ // Handle Single Values: 'search=0x123'
24770
+ // NOTE: Backend Controller expects flat query params for filters
24771
+ // e.g. ?search=...&status=... NOT ?filters[search]=...
24772
+ // This mapping flattens 'filters.search' -> 'search' to match @Query('search') in Controller
24773
+ const stringValue = (value instanceof Date) ? value.toISOString() : String(value);
24774
+ query.append(key, stringValue);
24775
+ }
24776
+ });
24777
+ }
24778
+ return query;
24779
+ }
24753
24780
  }
24754
24781
 
24755
24782
  /**
@@ -24792,17 +24819,17 @@ class TransactionService {
24792
24819
  return this.transactionApi.getUserTransactionHistory(role, limit);
24793
24820
  }
24794
24821
  /**
24795
- * AUTH: Prepare client signed transaction
24822
+ * AUTH: Prepare existing transaction for client-side signing
24796
24823
  */
24797
- /* async prepareClientSignedTransaction(request: TransactionRequestDTO): Promise<TransactionRequestResponseDTO> {
24798
- return this.transactionApi.prepareClientSignedTransaction(request);
24799
- } */
24824
+ async prepareExistingTransaction(transactionId) {
24825
+ return this.transactionApi.prepareExistingTransaction(transactionId);
24826
+ }
24800
24827
  /**
24801
- * AUTH: Burn user tokens
24828
+ * AUTH: Prepare client signed transaction
24802
24829
  */
24803
- /* async burnUserTokens(request: UserBurnTokenRequestDTO): Promise<TransactionRequestResponseDTO> {
24804
- return this.transactionApi.burnUserTokens(request);
24805
- } */
24830
+ async prepareClientSignedTransaction(request) {
24831
+ return this.transactionApi.prepareClientSignedTransaction(request);
24832
+ }
24806
24833
  // ==========================================
24807
24834
  // BUSINESS OPERATIONS
24808
24835
  // ==========================================
@@ -24839,6 +24866,27 @@ class TransactionService {
24839
24866
  async exportTransactionsCSV() {
24840
24867
  return this.transactionApi.exportTransactionsCSV();
24841
24868
  }
24869
+ // ==========================================
24870
+ // QUERY & ANALYTICS OPERATIONS
24871
+ // ==========================================
24872
+ /**
24873
+ * Query transactions by sender
24874
+ */
24875
+ async queryTransactionsBySender(accountSelector) {
24876
+ return this.transactionApi.queryTransactionsBySender(accountSelector);
24877
+ }
24878
+ /**
24879
+ * Query transactions by recipient
24880
+ */
24881
+ async queryTransactionsByRecipient(accountSelector) {
24882
+ return this.transactionApi.queryTransactionsByRecipient(accountSelector);
24883
+ }
24884
+ /**
24885
+ * ADMIN: Get transaction analytics
24886
+ */
24887
+ async getTransactionAnalytics(analyticsRequest) {
24888
+ return this.transactionApi.getTransactionAnalytics(analyticsRequest);
24889
+ }
24842
24890
  }
24843
24891
 
24844
24892
  /**
@@ -26172,14 +26220,19 @@ const AUTH_STORAGE_KEYS = {
26172
26220
  };
26173
26221
  /**
26174
26222
  * LocalStorage implementation
26223
+ * Note: Forces string conversion for compatibility
26175
26224
  */
26176
26225
  class LocalStorageTokenStorage {
26226
+ constructor() {
26227
+ this.supportsObjects = false;
26228
+ }
26177
26229
  async get(key) {
26178
26230
  return typeof localStorage !== 'undefined' ? localStorage.getItem(key) : null;
26179
26231
  }
26180
26232
  async set(key, value) {
26181
26233
  if (typeof localStorage !== 'undefined') {
26182
- localStorage.setItem(key, value);
26234
+ const stringValue = typeof value === 'string' ? value : JSON.stringify(value);
26235
+ localStorage.setItem(key, stringValue);
26183
26236
  }
26184
26237
  }
26185
26238
  async remove(key) {
@@ -26200,6 +26253,7 @@ class LocalStorageTokenStorage {
26200
26253
  */
26201
26254
  class MemoryTokenStorage {
26202
26255
  constructor() {
26256
+ this.supportsObjects = true;
26203
26257
  this.store = new Map();
26204
26258
  }
26205
26259
  async get(key) {
@@ -26223,26 +26277,30 @@ class AuthTokenManager {
26223
26277
  this.storage = storage;
26224
26278
  }
26225
26279
  async getAccessToken() {
26226
- return this.storage.get(AUTH_STORAGE_KEYS.ACCESS_TOKEN);
26280
+ const val = await this.storage.get(AUTH_STORAGE_KEYS.ACCESS_TOKEN);
26281
+ // Ensure we return string for tokens
26282
+ return typeof val === 'string' ? val : null;
26227
26283
  }
26228
26284
  async setAccessToken(token) {
26229
26285
  await this.storage.set(AUTH_STORAGE_KEYS.ACCESS_TOKEN, token);
26230
26286
  }
26231
26287
  async getRefreshToken() {
26232
- return this.storage.get(AUTH_STORAGE_KEYS.REFRESH_TOKEN);
26288
+ const val = await this.storage.get(AUTH_STORAGE_KEYS.REFRESH_TOKEN);
26289
+ return typeof val === 'string' ? val : null;
26233
26290
  }
26234
26291
  async setRefreshToken(token) {
26235
26292
  await this.storage.set(AUTH_STORAGE_KEYS.REFRESH_TOKEN, token);
26236
26293
  }
26237
26294
  async getProviderToken() {
26238
- return this.storage.get(AUTH_STORAGE_KEYS.PROVIDER_TOKEN);
26295
+ const val = await this.storage.get(AUTH_STORAGE_KEYS.PROVIDER_TOKEN);
26296
+ return typeof val === 'string' ? val : null;
26239
26297
  }
26240
26298
  async setProviderToken(token) {
26241
26299
  await this.storage.set(AUTH_STORAGE_KEYS.PROVIDER_TOKEN, token);
26242
26300
  }
26243
26301
  async getAuthType() {
26244
26302
  const authType = await this.storage.get(AUTH_STORAGE_KEYS.AUTH_TYPE);
26245
- return authType;
26303
+ return typeof authType === 'string' ? authType : null;
26246
26304
  }
26247
26305
  async setAuthType(authType) {
26248
26306
  await this.storage.set(AUTH_STORAGE_KEYS.AUTH_TYPE, authType);
@@ -26267,6 +26325,218 @@ class AuthTokenManager {
26267
26325
  }
26268
26326
  }
26269
26327
 
26328
+ class WebDPoPCryptoProvider {
26329
+ get crypto() {
26330
+ // Basic environment check
26331
+ if (typeof crypto !== 'undefined')
26332
+ return crypto;
26333
+ if (typeof window !== 'undefined' && window.crypto)
26334
+ return window.crypto;
26335
+ if (typeof globalThis !== 'undefined' && globalThis.crypto)
26336
+ return globalThis.crypto;
26337
+ throw new Error('WebCrypto API not found. Please polyfill crypto.subtle or use a different DPoPCryptoProvider.');
26338
+ }
26339
+ async generateKeyPair(options) {
26340
+ const extractable = options?.extractable ?? true;
26341
+ const keyPair = await this.crypto.subtle.generateKey({
26342
+ name: 'ECDSA',
26343
+ namedCurve: 'P-256',
26344
+ }, extractable, // Configurable extractability
26345
+ ['sign', 'verify']);
26346
+ const publicKey = await this.crypto.subtle.exportKey('jwk', keyPair.publicKey);
26347
+ // If extractable, export private key as JWK (plain object)
26348
+ // If NOT extractable, keep it as CryptoKey (native handle)
26349
+ // However, DPoPKeyPair interface currently expects JsonWebKey | CryptoKey
26350
+ // We need to check DPoPKeyPair type definition.
26351
+ // Wait, DPoPKeyPair definition in dpop.types.ts says:
26352
+ // publicKey: JsonWebKey; privateKey: JsonWebKey;
26353
+ // I need to change that to support CryptoKey.
26354
+ // Let's re-read dpop.types.ts
26355
+ // I assumed it might be compatible but let's verify.
26356
+ // If I change privateKey to unknown or JsonWebKey | CryptoKey, it will be safer.
26357
+ let privateKey;
26358
+ if (extractable) {
26359
+ privateKey = await this.crypto.subtle.exportKey('jwk', keyPair.privateKey);
26360
+ }
26361
+ else {
26362
+ privateKey = keyPair.privateKey;
26363
+ }
26364
+ return { publicKey, privateKey };
26365
+ }
26366
+ async signProof(payload, keyPair) {
26367
+ // 1. Prepare Header
26368
+ const header = {
26369
+ typ: 'dpop+jwt',
26370
+ alg: 'ES256',
26371
+ jwk: keyPair.publicKey
26372
+ };
26373
+ // 2. Prepare Payload
26374
+ // Ensure jti and iat if not present (though Manager usually provides them)
26375
+ const finalPayload = {
26376
+ ...payload,
26377
+ iat: payload.iat || Math.floor(Date.now() / 1000),
26378
+ jti: payload.jti || this.generateUUID()
26379
+ };
26380
+ // 3. Encode segments
26381
+ const encodedHeader = this.base64UrlEncode(JSON.stringify(header));
26382
+ const encodedPayload = this.base64UrlEncode(JSON.stringify(finalPayload));
26383
+ const signingInput = `${encodedHeader}.${encodedPayload}`;
26384
+ // 4. Import private key for signing
26385
+ let privateKey;
26386
+ // Check if it's already a CryptoKey (non-extractable handle)
26387
+ // We check for 'algorithm' property which signals a CryptoKey object
26388
+ if (keyPair.privateKey.algorithm) {
26389
+ privateKey = keyPair.privateKey;
26390
+ }
26391
+ else {
26392
+ // Otherwise import from JWK
26393
+ privateKey = await this.crypto.subtle.importKey('jwk', keyPair.privateKey, {
26394
+ name: 'ECDSA',
26395
+ namedCurve: 'P-256',
26396
+ }, false, ['sign']);
26397
+ }
26398
+ // 5. Sign
26399
+ const signatureBuffer = await this.crypto.subtle.sign({
26400
+ name: 'ECDSA',
26401
+ hash: { name: 'SHA-256' },
26402
+ }, privateKey, new TextEncoder().encode(signingInput));
26403
+ const encodedSignature = this.base64UrlEncodeBuffer(signatureBuffer);
26404
+ return `${signingInput}.${encodedSignature}`;
26405
+ }
26406
+ async hashAccessToken(accessToken) {
26407
+ const encoder = new TextEncoder();
26408
+ const data = encoder.encode(accessToken);
26409
+ const hashBuffer = await this.crypto.subtle.digest('SHA-256', data);
26410
+ return this.base64UrlEncodeBuffer(hashBuffer);
26411
+ }
26412
+ // --- Helpers ---
26413
+ generateUUID() {
26414
+ if (typeof crypto !== 'undefined' && crypto.randomUUID) {
26415
+ return crypto.randomUUID();
26416
+ }
26417
+ // Fallback for older envs
26418
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
26419
+ const r = (Math.random() * 16) | 0;
26420
+ const v = c === 'x' ? r : (r & 0x3) | 0x8;
26421
+ return v.toString(16);
26422
+ });
26423
+ }
26424
+ base64UrlEncode(str) {
26425
+ // Use TextEncoder to handle UTF-8 properly
26426
+ const encoder = new TextEncoder();
26427
+ return this.base64UrlEncodeBuffer(encoder.encode(str));
26428
+ }
26429
+ base64UrlEncodeBuffer(buffer) {
26430
+ const bytes = new Uint8Array(buffer);
26431
+ let binary = '';
26432
+ const len = bytes.byteLength;
26433
+ // Chunk processing for large buffers if needed, but proofs are small
26434
+ for (let i = 0; i < len; i++) {
26435
+ binary += String.fromCharCode(bytes[i]);
26436
+ }
26437
+ // btoa is widely available in browsers.
26438
+ // In Node (non-globals), we might need Buffer.
26439
+ // For universal 'platform agnostic' aiming at standard web-like envs:
26440
+ let base64 = '';
26441
+ if (typeof btoa === 'function') {
26442
+ base64 = btoa(binary);
26443
+ }
26444
+ else if (typeof Buffer !== 'undefined') {
26445
+ base64 = Buffer.from(binary, 'binary').toString('base64');
26446
+ }
26447
+ else {
26448
+ throw new Error('Base64 encoding not supported in this environment');
26449
+ }
26450
+ return base64
26451
+ .replace(/\+/g, '-')
26452
+ .replace(/\//g, '_')
26453
+ .replace(/=+$/, '');
26454
+ }
26455
+ }
26456
+
26457
+ const DPOP_STORAGE_KEYS = {
26458
+ PUBLIC: 'pers_dpop_public_key',
26459
+ PRIVATE: 'pers_dpop_private_key'
26460
+ };
26461
+ class DPoPManager {
26462
+ constructor(storage, cryptoProvider) {
26463
+ this.storage = storage;
26464
+ this.memoryKeyPair = null;
26465
+ this.cryptoProvider = cryptoProvider || new WebDPoPCryptoProvider();
26466
+ }
26467
+ /**
26468
+ * Ensures a DPoP key pair exists, loading from storage or generating new one.
26469
+ */
26470
+ async ensureKeyPair() {
26471
+ if (this.memoryKeyPair)
26472
+ return this.memoryKeyPair;
26473
+ const storedPublic = await this.storage.get(DPOP_STORAGE_KEYS.PUBLIC);
26474
+ const storedPrivate = await this.storage.get(DPOP_STORAGE_KEYS.PRIVATE);
26475
+ if (storedPublic && storedPrivate) {
26476
+ try {
26477
+ // Handle polymorphic storage: String (JSON) or Object (CryptoKey)
26478
+ const publicKey = typeof storedPublic === 'string' ? JSON.parse(storedPublic) : storedPublic;
26479
+ const privateKey = typeof storedPrivate === 'string' ? JSON.parse(storedPrivate) : storedPrivate;
26480
+ this.memoryKeyPair = { publicKey, privateKey };
26481
+ return this.memoryKeyPair;
26482
+ }
26483
+ catch (e) {
26484
+ console.warn('Corrupted DPoP keys in storage, regenerating.');
26485
+ }
26486
+ }
26487
+ // Generate new key pair
26488
+ // Adaptation: If storage supports raw objects (like IndexedDB or Native Keychain),
26489
+ // we can generate Non-Extractable keys for maximum security.
26490
+ // If storage is text-only (LocalStorage), we must use Extractable keys to serialize them.
26491
+ const useHighSecurity = !!this.storage.supportsObjects;
26492
+ const keyPair = await this.cryptoProvider.generateKeyPair({
26493
+ extractable: !useHighSecurity
26494
+ });
26495
+ // Save to storage
26496
+ await this.storage.set(DPOP_STORAGE_KEYS.PUBLIC, keyPair.publicKey);
26497
+ await this.storage.set(DPOP_STORAGE_KEYS.PRIVATE, keyPair.privateKey);
26498
+ this.memoryKeyPair = keyPair;
26499
+ return keyPair;
26500
+ }
26501
+ /**
26502
+ * Generates the DPoP proof header value.
26503
+ * @param method HTTP method
26504
+ * @param url Request URL
26505
+ * @param accessToken Optional access token to bind the proof to (ath claim)
26506
+ */
26507
+ async generateProofHeader(method, url, accessToken) {
26508
+ const keyPair = await this.ensureKeyPair();
26509
+ const payload = {
26510
+ htm: method,
26511
+ htu: url,
26512
+ };
26513
+ if (accessToken) {
26514
+ payload.ath = await this.cryptoProvider.hashAccessToken(accessToken);
26515
+ }
26516
+ return this.cryptoProvider.signProof(payload, keyPair);
26517
+ }
26518
+ /**
26519
+ * Clears DPoP keys (e.g. on logout)
26520
+ */
26521
+ async clearKeys() {
26522
+ this.memoryKeyPair = null;
26523
+ await this.storage.remove(DPOP_STORAGE_KEYS.PUBLIC);
26524
+ await this.storage.remove(DPOP_STORAGE_KEYS.PRIVATE);
26525
+ }
26526
+ /**
26527
+ * Gets the public key (for debugging/inspection)
26528
+ */
26529
+ async getPublicKey() {
26530
+ try {
26531
+ const kp = await this.ensureKeyPair();
26532
+ return kp.publicKey;
26533
+ }
26534
+ catch {
26535
+ return null;
26536
+ }
26537
+ }
26538
+ }
26539
+
26270
26540
  /**
26271
26541
  * Default Authentication Provider
26272
26542
  * Simple implementation of PERS authentication with token lifecycle management
@@ -26280,6 +26550,9 @@ class DefaultAuthProvider {
26280
26550
  this.authType = config.authType || 'user';
26281
26551
  const storage = config.storage || this.createStorage();
26282
26552
  this.tokenManager = new AuthTokenManager(storage);
26553
+ if (config.dpop?.enabled) {
26554
+ this.dpopManager = new DPoPManager(storage, config.dpop.cryptoProvider);
26555
+ }
26283
26556
  }
26284
26557
  createStorage() {
26285
26558
  return typeof window === 'undefined' || typeof localStorage === 'undefined'
@@ -26289,6 +26562,26 @@ class DefaultAuthProvider {
26289
26562
  getToken() {
26290
26563
  return this.tokenManager.getAccessToken();
26291
26564
  }
26565
+ async getAuthHeaders(token, method, url) {
26566
+ const headers = {};
26567
+ if (this.dpopManager && method && url) {
26568
+ // Generate DPoP Proof
26569
+ // Note: For 'bypassAuth' requests (token is null), we still generate proof (bound to public key, no ath)
26570
+ // This covers Token Requests (Login) which need DPoP header to bind the issued token.
26571
+ const proof = await this.dpopManager.generateProofHeader(method, url, token || undefined);
26572
+ headers['DPoP'] = proof;
26573
+ if (token) {
26574
+ headers['Authorization'] = `DPoP ${token}`;
26575
+ }
26576
+ }
26577
+ else {
26578
+ // Standard Bearer
26579
+ if (token) {
26580
+ headers['Authorization'] = `Bearer ${token}`;
26581
+ }
26582
+ }
26583
+ return headers;
26584
+ }
26292
26585
  async getProjectKey() {
26293
26586
  return this.config.projectKey || null;
26294
26587
  }
@@ -26323,6 +26616,9 @@ class DefaultAuthProvider {
26323
26616
  }
26324
26617
  async clearTokens() {
26325
26618
  await this.tokenManager.clearAllTokens();
26619
+ if (this.dpopManager) {
26620
+ await this.dpopManager.clearKeys();
26621
+ }
26326
26622
  }
26327
26623
  async setTokens(accessToken, refreshToken, providerToken) {
26328
26624
  await this.tokenManager.setTokens(accessToken, refreshToken, providerToken);
@@ -26371,7 +26667,11 @@ class PersApiClient {
26371
26667
  this.mergedConfig.authProvider = new DefaultAuthProvider({
26372
26668
  authType: this.mergedConfig.authType || 'user',
26373
26669
  projectKey: this.mergedConfig.apiProjectKey,
26374
- storage: this.mergedConfig.authStorage // Support custom storage
26670
+ storage: this.mergedConfig.authStorage, // Support custom storage
26671
+ dpop: {
26672
+ enabled: this.mergedConfig.dpop?.enabled ?? true, // Enable DPoP by default
26673
+ cryptoProvider: this.mergedConfig.dpop?.cryptoProvider
26674
+ }
26375
26675
  });
26376
26676
  }
26377
26677
  this.authService = new AuthService(this.authApi, this.mergedConfig.authProvider);
@@ -26434,7 +26734,7 @@ class PersApiClient {
26434
26734
  });
26435
26735
  }
26436
26736
  const requestOptions = {
26437
- headers: await this.getHeaders(!bypassAuth),
26737
+ headers: await this.getHeaders(!bypassAuth, method, url),
26438
26738
  timeout: this.mergedConfig.timeout,
26439
26739
  responseType
26440
26740
  };
@@ -26529,26 +26829,45 @@ class PersApiClient {
26529
26829
  * Get request headers with optional auth token and project key
26530
26830
  *
26531
26831
  * @param includeAuth - Whether to include the Authorization header (default: true)
26832
+ * @param method - HTTP Method (required for DPoP)
26833
+ * @param url - Full Request URL (required for DPoP)
26532
26834
  */
26533
- async getHeaders(includeAuth = true) {
26835
+ async getHeaders(includeAuth = true, method, url) {
26534
26836
  const headers = {
26535
26837
  'Content-Type': 'application/json',
26536
26838
  };
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}`;
26839
+ if (this.mergedConfig.authProvider) {
26840
+ let token = null;
26841
+ if (includeAuth) {
26842
+ token = await this.mergedConfig.authProvider.getToken();
26843
+ }
26844
+ // Handle Authentication Headers (Bearer or DPoP)
26845
+ if (this.mergedConfig.authProvider.getAuthHeaders) {
26846
+ // Provider supports advanced headers (DPoP)
26847
+ // We pass token (null if includeAuth=false) so it can generate DPoP proofs for login requests too
26848
+ try {
26849
+ const authHeaders = await this.mergedConfig.authProvider.getAuthHeaders(token, method, url);
26850
+ Object.assign(headers, authHeaders);
26543
26851
  }
26544
- else {
26545
- console.warn('[PersApiClient] No token available from auth provider');
26852
+ catch (e) {
26853
+ console.warn('[PersApiClient] Failed to generate auth headers:', e);
26854
+ // Fallback if token exists but DPoP generation failed?
26855
+ // Better to fail or let standardized fallback below handle it?
26856
+ // If DPoP fails, falling back to Bearer might be insecure if server enforces DPoP.
26857
+ // For now, valid token -> Bearer fallback logic mostly for non-DPoP legacy providers.
26546
26858
  }
26547
26859
  }
26548
- else {
26549
- console.warn('[PersApiClient] No auth provider configured');
26860
+ // Fallback: If provider didn't give headers but we have token and NO Authorization header yet
26861
+ if (includeAuth && token && !headers['Authorization']) {
26862
+ headers['Authorization'] = `Bearer ${token}`;
26863
+ }
26864
+ else if (includeAuth && !token) {
26865
+ console.warn('[PersApiClient] No token available from auth provider');
26550
26866
  }
26551
26867
  }
26868
+ else if (includeAuth) {
26869
+ console.warn('[PersApiClient] No auth provider configured');
26870
+ }
26552
26871
  // Add project key
26553
26872
  if (this.mergedConfig.authProvider) {
26554
26873
  const projectKey = await this.mergedConfig.authProvider.getProjectKey();
@@ -29567,6 +29886,75 @@ class TransactionManager {
29567
29886
  async exportTransactionsCSV() {
29568
29887
  return this.transactionService.exportTransactionsCSV();
29569
29888
  }
29889
+ /**
29890
+ * Prepare existing transaction for client-side signing
29891
+ *
29892
+ * Retrieves the necessary data to sign an existing transaction on the client side.
29893
+ * This is typically used when a transaction has been created but requires user signature.
29894
+ *
29895
+ * @param transactionId - The ID of the transaction to prepare
29896
+ * @returns Promise resolving to the transaction preparation data
29897
+ */
29898
+ async prepareExistingTransaction(transactionId) {
29899
+ return this.transactionService.prepareExistingTransaction(transactionId);
29900
+ }
29901
+ /**
29902
+ * Prepare a new transaction for client-side signing
29903
+ *
29904
+ * Creates a new transaction and immediately returns the data needed for client-side signing.
29905
+ * This combines creation and preparation into a single flow for client-signed operations.
29906
+ *
29907
+ * @param request - The transaction request details
29908
+ * @returns Promise resolving to the transaction preparation data
29909
+ */
29910
+ async prepareClientSignedTransaction(request) {
29911
+ return this.transactionService.prepareClientSignedTransaction(request);
29912
+ }
29913
+ /**
29914
+ * Submit a signed transaction
29915
+ *
29916
+ * Submits a transaction that has been signed on the client side to the backend for processing.
29917
+ *
29918
+ * @param signedTxData - The signed transaction data
29919
+ * @returns Promise resolving to the submission result
29920
+ */
29921
+ async submitSignedTransaction(signedTxData) {
29922
+ return this.transactionService.submitSignedTransaction(signedTxData);
29923
+ }
29924
+ /**
29925
+ * Query transactions by sender
29926
+ *
29927
+ * Retrieves transactions where the specified account is the sender.
29928
+ *
29929
+ * @param accountSelector - Selector for the sender account
29930
+ * @returns Promise resolving to paginated transactions
29931
+ */
29932
+ async queryTransactionsBySender(accountSelector) {
29933
+ return this.transactionService.queryTransactionsBySender(accountSelector);
29934
+ }
29935
+ /**
29936
+ * Query transactions by recipient
29937
+ *
29938
+ * Retrieves transactions where the specified account is the recipient.
29939
+ *
29940
+ * @param accountSelector - Selector for the recipient account
29941
+ * @returns Promise resolving to paginated transactions
29942
+ */
29943
+ async queryTransactionsByRecipient(accountSelector) {
29944
+ return this.transactionService.queryTransactionsByRecipient(accountSelector);
29945
+ }
29946
+ /**
29947
+ * Get transaction analytics
29948
+ *
29949
+ * Retrieves analytics data for transactions based on the provided request criteria.
29950
+ * Useful for generating reports and dashboards.
29951
+ *
29952
+ * @param analyticsRequest - The analytics query parameters
29953
+ * @returns Promise resolving to the analytics data
29954
+ */
29955
+ async getTransactionAnalytics(analyticsRequest) {
29956
+ return this.transactionService.getTransactionAnalytics(analyticsRequest);
29957
+ }
29570
29958
  /**
29571
29959
  * Get the full transaction service for advanced operations
29572
29960
  *
@@ -31677,6 +32065,143 @@ class PersSDK {
31677
32065
  }
31678
32066
  }
31679
32067
 
32068
+ /**
32069
+ * Secure Storage implementation for React Native
32070
+ * Uses Keychain/Keystore for sensitive data and AsyncStorage for non-sensitive data.
32071
+ */
32072
+ class ReactNativeSecureStorage {
32073
+ constructor(keyPrefix = 'pers_tokens_') {
32074
+ this.keyPrefix = keyPrefix;
32075
+ // Set to false because Keychain stores strings, so we need extractable (serializable) keys
32076
+ this.supportsObjects = false;
32077
+ // Define which keys MUST go to secure hardware storage
32078
+ this.SECURE_KEYS = new Set([
32079
+ DPOP_STORAGE_KEYS.PRIVATE,
32080
+ AUTH_STORAGE_KEYS.ACCESS_TOKEN,
32081
+ AUTH_STORAGE_KEYS.REFRESH_TOKEN,
32082
+ AUTH_STORAGE_KEYS.PROVIDER_TOKEN
32083
+ ]);
32084
+ }
32085
+ async set(key, value) {
32086
+ const prefixedKey = this.getKeyName(key);
32087
+ const stringValue = typeof value === 'string' ? value : JSON.stringify(value);
32088
+ // Cast key to any to bypass strict literal type checking of the Set.has method
32089
+ // In runtime, 'key' is just a string, so this is safe.
32090
+ if (this.SECURE_KEYS.has(key)) {
32091
+ // Store in Keychain/Keystore
32092
+ // Service name acts as the key identifier in simple usage
32093
+ await Keychain__namespace.setGenericPassword(prefixedKey, stringValue, { service: prefixedKey });
32094
+ }
32095
+ else {
32096
+ // Store standard config/metadata in AsyncStorage
32097
+ await AsyncStorage.setItem(prefixedKey, stringValue);
32098
+ }
32099
+ }
32100
+ async get(key) {
32101
+ const prefixedKey = this.getKeyName(key);
32102
+ if (this.SECURE_KEYS.has(key)) {
32103
+ try {
32104
+ const credentials = await Keychain__namespace.getGenericPassword({ service: prefixedKey });
32105
+ if (credentials) {
32106
+ return this.tryParse(credentials.password);
32107
+ }
32108
+ return null;
32109
+ }
32110
+ catch (e) {
32111
+ console.warn(`[ReactNativeSecureStorage] Failed to access keychain for ${key}`, e);
32112
+ return null; // Key not found or access denied
32113
+ }
32114
+ }
32115
+ else {
32116
+ try {
32117
+ const val = await AsyncStorage.getItem(prefixedKey);
32118
+ return val ? this.tryParse(val) : null;
32119
+ }
32120
+ catch (e) {
32121
+ console.warn(`[ReactNativeSecureStorage] Failed to access AsyncStorage for ${key}`, e);
32122
+ return null;
32123
+ }
32124
+ }
32125
+ }
32126
+ async remove(key) {
32127
+ const prefixedKey = this.getKeyName(key);
32128
+ if (this.SECURE_KEYS.has(key)) {
32129
+ await Keychain__namespace.resetGenericPassword({ service: prefixedKey });
32130
+ }
32131
+ else {
32132
+ await AsyncStorage.removeItem(prefixedKey);
32133
+ }
32134
+ }
32135
+ async clear() {
32136
+ // Clear all known secure keys
32137
+ for (const key of this.SECURE_KEYS) {
32138
+ await Keychain__namespace.resetGenericPassword({ service: this.getKeyName(key) });
32139
+ }
32140
+ // Clear AsyncStorage keys related to PERS
32141
+ try {
32142
+ const allKeys = await AsyncStorage.getAllKeys();
32143
+ const ourKeys = allKeys.filter(k => k.startsWith(this.keyPrefix));
32144
+ if (ourKeys.length > 0) {
32145
+ await AsyncStorage.multiRemove(ourKeys);
32146
+ }
32147
+ }
32148
+ catch (e) {
32149
+ console.warn('[ReactNativeSecureStorage] Failed to clear AsyncStorage', e);
32150
+ }
32151
+ }
32152
+ getKeyName(key) {
32153
+ // For Keychain, we might want to avoid prefixes if we want to share across apps,
32154
+ // but for simple isolation, prefix is fine to avoid collisions if multiple instances exist.
32155
+ // However, Keychain services are global to the app bundle ID usually.
32156
+ return `${this.keyPrefix}${key}`;
32157
+ }
32158
+ tryParse(val) {
32159
+ try {
32160
+ return JSON.parse(val);
32161
+ }
32162
+ catch {
32163
+ return val;
32164
+ }
32165
+ }
32166
+ }
32167
+
32168
+ /**
32169
+ * React Native Unified Auth Provider
32170
+ *
32171
+ * Creates a platform-specific auth provider that automatically selects the appropriate storage:
32172
+ * - Web: Uses LocalStorageTokenStorage from core SDK
32173
+ * - Mobile: Uses AsyncStorage-based storage
32174
+ */
32175
+ // Use React Native's built-in platform detection
32176
+ const isWebPlatform = reactNative.Platform.OS === 'web';
32177
+ /**
32178
+ * Create a React Native auth provider with platform-appropriate storage
32179
+ *
32180
+ * Automatically selects storage implementation:
32181
+ * - Web: Uses LocalStorageTokenStorage (from core SDK)
32182
+ * - Mobile: Uses AsyncStorageTokenStorage (React Native specific)
32183
+ *
32184
+ * @param projectKey - PERS project key
32185
+ * @param config - Configuration options
32186
+ * @returns DefaultAuthProvider configured for the current platform
32187
+ */
32188
+ function createReactNativeAuthProvider(projectKey, config = {}) {
32189
+ if (!projectKey || typeof projectKey !== 'string') {
32190
+ throw new Error('createReactNativeAuthProvider: projectKey is required and must be a string');
32191
+ }
32192
+ const { keyPrefix = `pers_${projectKey.slice(0, 8)}_`, debug = false, customStorage, authType = 'user' } = config;
32193
+ // Platform-specific storage selection
32194
+ const tokenStorage = customStorage || (isWebPlatform
32195
+ ? new LocalStorageTokenStorage()
32196
+ : new ReactNativeSecureStorage(keyPrefix));
32197
+ // Return DefaultAuthProvider configured with platform-appropriate storage
32198
+ return new DefaultAuthProvider({
32199
+ authType,
32200
+ projectKey,
32201
+ storage: tokenStorage
32202
+ });
32203
+ }
32204
+
31680
32205
  /**
31681
32206
  * AsyncStorage Token Storage for React Native Mobile Platforms
31682
32207
  *
@@ -31790,41 +32315,79 @@ class AsyncStorageTokenStorage {
31790
32315
  }
31791
32316
  }
31792
32317
 
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');
32318
+ class ReactNativeDPoPProvider {
32319
+ /**
32320
+ * Generates a new key pair (ES256 recommended)
32321
+ *
32322
+ * Note: options.extractable is ignored because for React Native Keychain storage,
32323
+ * we MUST export the keys as JWK/strings. We rely on the Keychain encryption
32324
+ * for security at rest (High Security), making the key "Extractable" in memory
32325
+ * but protected by hardware encryption when stored.
32326
+ */
32327
+ async generateKeyPair(options) {
32328
+ // Generate P-256 Key Pair
32329
+ return new Promise((resolve, reject) => {
32330
+ crypto$1.generateKeyPair('ec', { namedCurve: 'P-256' }, (err, publicKey, privateKey) => {
32331
+ if (err)
32332
+ return reject(err);
32333
+ // Export to JWK for SDK Compatibility
32334
+ // We use 'export' because supportsObjects=false in our storage forces the SDK
32335
+ // to expect serializable keys.
32336
+ const pubJwk = publicKey.export({ format: 'jwk' });
32337
+ const privJwk = privateKey.export({ format: 'jwk' });
32338
+ resolve({
32339
+ publicKey: pubJwk,
32340
+ privateKey: privJwk
32341
+ });
32342
+ });
32343
+ });
32344
+ }
32345
+ async signProof(payload, keyPair) {
32346
+ // 1. Construct Header
32347
+ const header = {
32348
+ typ: 'dpop+jwt',
32349
+ alg: 'ES256',
32350
+ jwk: keyPair.publicKey
32351
+ };
32352
+ // 2. Add Claims (iat/jti)
32353
+ const finalPayload = {
32354
+ ...payload,
32355
+ iat: payload.iat || Math.floor(Date.now() / 1000),
32356
+ jti: payload.jti || crypto$1.randomUUID()
32357
+ };
32358
+ // 3. Encode
32359
+ const b64Header = this.base64Url(JSON.stringify(header));
32360
+ const b64Payload = this.base64Url(JSON.stringify(finalPayload));
32361
+ const signingInput = `${b64Header}.${b64Payload}`;
32362
+ // 4. Sign
32363
+ const sign = crypto$1.createSign('SHA256');
32364
+ sign.update(signingInput);
32365
+ // sign.end() is not required/available in quick-crypto as it doesn't strictly follow stream interface
32366
+ // Import private key back from JWK to sign
32367
+ // The keyPair.privateKey is a JsonWebKey object because we exported it earlier.
32368
+ // quick-crypto createPrivateKey expects jwk object if format is jwk
32369
+ const privateKeyObj = crypto$1.createPrivateKey({ key: keyPair.privateKey, format: 'jwk' });
32370
+ const signature = sign.sign(privateKeyObj);
32371
+ // signature is a Buffer in quick-crypto
32372
+ return `${signingInput}.${this.base64UrlBuffer(signature)}`;
32373
+ }
32374
+ async hashAccessToken(accessToken) {
32375
+ const hash = crypto$1.createHash('sha256').update(accessToken).digest();
32376
+ // digest returns Buffer in quick-crypto
32377
+ return this.base64UrlBuffer(hash);
32378
+ }
32379
+ // --- Helpers ---
32380
+ base64Url(str) {
32381
+ return this.base64UrlBuffer(buffer.Buffer.from(str));
32382
+ }
32383
+ base64UrlBuffer(buf) {
32384
+ // Ensure we have a Buffer
32385
+ const buffer$1 = buffer.Buffer.isBuffer(buf) ? buf : buffer.Buffer.from(buf);
32386
+ return buffer$1.toString('base64')
32387
+ .replace(/=/g, '')
32388
+ .replace(/\+/g, '-')
32389
+ .replace(/\//g, '_');
31816
32390
  }
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
32391
  }
31829
32392
 
31830
32393
  /**
@@ -31958,6 +32521,15 @@ const PersSDKProvider = ({ children }) => {
31958
32521
  ...config,
31959
32522
  authProvider
31960
32523
  };
32524
+ // Inject Native DPoP Provider (crypto-based) for mobile platforms
32525
+ // Web platforms use built-in WebCrypto provider by default
32526
+ if (reactNative.Platform.OS !== 'web' && (!config.dpop?.cryptoProvider)) {
32527
+ sdkConfig.dpop = {
32528
+ ...(config.dpop || {}),
32529
+ enabled: config.dpop?.enabled ?? true,
32530
+ cryptoProvider: new ReactNativeDPoPProvider()
32531
+ };
32532
+ }
31961
32533
  // Initialize PersSDK with platform-aware auth provider
31962
32534
  const sdkInstance = new PersSDK(httpClient, sdkConfig);
31963
32535
  setAuthProvider(authProvider);
@@ -34419,7 +34991,9 @@ exports.ANALYTICS_METRIC_TYPES = ANALYTICS_METRIC_TYPES;
34419
34991
  exports.AsyncStorageTokenStorage = AsyncStorageTokenStorage;
34420
34992
  exports.NativeTokenTypes = NativeTokenTypes;
34421
34993
  exports.PersSDKProvider = PersSDKProvider;
34994
+ exports.ReactNativeDPoPProvider = ReactNativeDPoPProvider;
34422
34995
  exports.ReactNativeHttpClient = ReactNativeHttpClient;
34996
+ exports.ReactNativeSecureStorage = ReactNativeSecureStorage;
34423
34997
  exports.TRANSACTION_FORMATS = TRANSACTION_FORMATS;
34424
34998
  exports.TRANSACTION_FORMAT_DESCRIPTIONS = TRANSACTION_FORMAT_DESCRIPTIONS;
34425
34999
  exports.apiPublicKeyTestPrefix = apiPublicKeyTestPrefix;