@explorins/pers-signer 1.0.11 → 1.0.16

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.
@@ -50,7 +50,17 @@ class FetchHttpClient {
50
50
  setTimeout(() => controller.abort(), options.timeout);
51
51
  }
52
52
  const response = await fetch(url, fetchOptions);
53
- const data = await response.json();
53
+ // Check if response is actually JSON before parsing
54
+ const responseText = await response.text();
55
+ let data;
56
+ try {
57
+ data = JSON.parse(responseText);
58
+ }
59
+ catch (parseError) {
60
+ console.error('[HttpClient] JSON parse error:', parseError);
61
+ console.error('[HttpClient] Failed to parse response text:', responseText);
62
+ throw new Error(`Failed to parse JSON response: ${parseError instanceof Error ? parseError.message : 'Unknown error'}`);
63
+ }
54
64
  return {
55
65
  data,
56
66
  status: response.status,
@@ -160,74 +170,31 @@ function getServiceConfig() {
160
170
 
161
171
  /**
162
172
  * Wallet management service
163
- * Handles wallet listing and management operations
173
+ * Updated for v1 API endpoints per migration reference
164
174
  */
165
175
  class WalletService {
166
176
  /**
167
177
  * List all wallets for authenticated user
168
- * @param authToken - Authentication token
169
- * @returns Promise resolving to wallet list
178
+ * @param signerToken - Signer JWT token
179
+ * @returns Promise resolving to wallet data
170
180
  */
171
- static async listWallets(authToken) {
181
+ static async listWallets(signerToken) {
172
182
  const httpClient = getHttpClient();
173
183
  const config = getServiceConfig();
174
184
  try {
175
- const response = await httpClient.post(`${config.apiUrl}/wallets/list`, {
176
- authToken,
177
- appId: config.appId,
178
- });
179
- console.log('[WalletService] Raw API response:', response.data);
180
- return response.data;
185
+ const requestBody = {};
186
+ const response = await httpClient.post(`${config.apiUrl}/wallets/list`, requestBody, { Authorization: `Bearer ${signerToken}` });
187
+ const backendResponse = response.data;
188
+ if (backendResponse.success && backendResponse.data) {
189
+ return backendResponse.data;
190
+ }
191
+ throw new Error('Failed to list wallets: Invalid response');
181
192
  }
182
193
  catch (error) {
183
194
  console.error('[WalletService] Error listing wallets:', error);
184
195
  throw error;
185
196
  }
186
197
  }
187
- /**
188
- * Get wallet by ID (when backend supports it)
189
- * @param authToken - Authentication token
190
- * @param walletId - Wallet identifier
191
- * @returns Promise resolving to wallet details
192
- */
193
- static async getWallet(authToken, walletId) {
194
- const httpClient = getHttpClient();
195
- const config = getServiceConfig();
196
- try {
197
- const response = await httpClient.post(`${config.apiUrl}/wallets/get`, {
198
- authToken,
199
- walletId,
200
- appId: config.appId,
201
- });
202
- return response.data;
203
- }
204
- catch (error) {
205
- console.error(`[WalletService] Error getting wallet ${walletId}:`, error);
206
- throw error;
207
- }
208
- }
209
- /**
210
- * Create new wallet (when backend supports it)
211
- * @param authToken - Authentication token
212
- * @param walletConfig - Wallet configuration
213
- * @returns Promise resolving to wallet creation result
214
- */
215
- static async createWallet(authToken, walletConfig) {
216
- const httpClient = getHttpClient();
217
- const config = getServiceConfig();
218
- try {
219
- const response = await httpClient.post(`${config.apiUrl}/wallets/create`, {
220
- authToken,
221
- ...walletConfig,
222
- appId: config.appId,
223
- });
224
- return response.data;
225
- }
226
- catch (error) {
227
- console.error('[WalletService] Error creating wallet:', error);
228
- throw error;
229
- }
230
- }
231
198
  }
232
199
 
233
200
  /**
@@ -306,10 +273,10 @@ class PersService {
306
273
  * @returns Promise with tenant public information
307
274
  */
308
275
  static async getTenantById(tenantId, authToken) {
309
- // Check memory cache first
276
+ // Check memory cache first with expiration
310
277
  const cached = this.tenantCache.get(tenantId);
311
- if (cached) {
312
- return cached;
278
+ if (cached && Date.now() < cached.expiresAt) {
279
+ return cached.tenant;
313
280
  }
314
281
  try {
315
282
  const headers = {
@@ -347,10 +314,16 @@ class PersService {
347
314
  }
348
315
  }
349
316
  const tenantData = await response.json();
350
- // Cache the tenant data in memory only
351
- this.tenantCache.set(tenantId, tenantData);
317
+ // Cache the tenant data with expiration
318
+ const now = Date.now();
319
+ this.tenantCache.set(tenantId, {
320
+ tenant: tenantData,
321
+ cachedAt: now,
322
+ expiresAt: now + this.TENANT_CACHE_TTL
323
+ });
352
324
  // Update current project key (check multiple possible property names)
353
325
  this.currentProjectKey = tenantData.projectKey || tenantData.projectApiKey || tenantData.apiKey;
326
+ this.currentTenantId = tenantId;
354
327
  return tenantData;
355
328
  }
356
329
  catch (error) {
@@ -370,14 +343,35 @@ class PersService {
370
343
  const projectKey = tenantData.projectApiKey;
371
344
  if (projectKey) {
372
345
  this.currentProjectKey = projectKey;
346
+ this.currentTenantId = tenantId;
373
347
  }
374
348
  return tenantData;
375
349
  }
350
+ /**
351
+ * Ensure tenant is initialized and current, with automatic revalidation
352
+ * @param tenantId - The tenant ID to ensure is initialized
353
+ * @param authToken - Optional auth token for authentication
354
+ * @returns Promise with tenant information
355
+ */
356
+ static async ensureTenantInitialized(tenantId, authToken) {
357
+ // Check if we already have the right tenant initialized and it's still valid
358
+ const cached = this.tenantCache.get(tenantId);
359
+ if (this.currentTenantId === tenantId && cached && Date.now() < cached.expiresAt) {
360
+ return cached.tenant;
361
+ }
362
+ // Initialize or refresh tenant data
363
+ return await this.initializeTenant(tenantId, authToken);
364
+ }
376
365
  /**
377
366
  * Get the current project key (either from tenant or fallback)
367
+ * @param tenantId - Optional tenant ID to ensure is initialized
378
368
  * @returns The project key to use for API calls
379
369
  */
380
- static getProjectKey() {
370
+ static async getProjectKey(tenantId) {
371
+ // If tenantId provided and different from current, initialize it
372
+ if (tenantId && this.currentTenantId !== tenantId) {
373
+ await this.ensureTenantInitialized(tenantId);
374
+ }
381
375
  if (this.currentProjectKey) {
382
376
  return this.currentProjectKey;
383
377
  }
@@ -393,17 +387,19 @@ class PersService {
393
387
  static clearTenantCache() {
394
388
  this.tenantCache.clear();
395
389
  this.currentProjectKey = null;
390
+ this.currentTenantId = null;
396
391
  }
397
392
  /**
398
393
  * Authenticates a user with the PERS backend using their auth token
399
394
  *
400
395
  * @param authToken - The authentication token received from DFNS after login/registration
396
+ * @param tenantId - Optional tenant ID for automatic initialization
401
397
  * @returns A promise that resolves to the authentication response
402
398
  * @throws If the request fails
403
399
  */
404
- static async authenticateUser(authToken) {
400
+ static async authenticateUser(authToken, tenantId) {
405
401
  try {
406
- const projectKey = this.getProjectKey();
402
+ const projectKey = await this.getProjectKey(tenantId);
407
403
  const headers = {
408
404
  'accept': 'application/json',
409
405
  'x-project-key': projectKey,
@@ -453,30 +449,6 @@ class PersService {
453
449
  };
454
450
  }
455
451
  }
456
- /**
457
- * Prepares a transaction by calling the backend endpoint
458
- *
459
- * @param form - The transaction details
460
- * @param persAccessToken - The PERS access token for authentication (Bearer)
461
- * @returns A promise that resolves to the transaction preparation response
462
- * @throws If the request fails
463
- */
464
- static async prepareTransaction(form, persAccessToken) {
465
- // ✅ UPDATED: /transaction/auth/prepare-signing → /transactions/prepare
466
- const res = await fetch(`${getPersApiUrl(this.useStaging)}/transactions`, {
467
- method: "POST",
468
- headers: {
469
- "Content-Type": "application/json",
470
- 'x-project-key': this.getProjectKey(),
471
- Authorization: `Bearer ${persAccessToken}`,
472
- },
473
- body: JSON.stringify(form),
474
- });
475
- if (!res.ok)
476
- throw new Error("Failed to prepare transaction");
477
- const response = await res.json();
478
- return response;
479
- }
480
452
  /**
481
453
  * Submits a transaction by calling the backend endpoint
482
454
  *
@@ -484,208 +456,86 @@ class PersService {
484
456
  * @param signedTransactionOrSignature - The signed transaction data or EIP-712 signature
485
457
  * @param persAccessToken - The PERS access token for authentication (Bearer)
486
458
  * @param submissionType - The transaction format type
459
+ * @param tenantId - Optional tenant ID for automatic initialization
487
460
  * @returns A promise that resolves to the transaction submission response
488
461
  * @throws If the request fails
489
462
  */
490
- static async submitTransaction(transactionId, signedTransactionOrSignature, persAccessToken, submissionType) {
491
- // Map TransactionFormat to the backend's expected submission type
492
- const dto = {
493
- transactionId,
494
- type: submissionType,
495
- ...(submissionType === browser.TRANSACTION_FORMATS.EIP_712
496
- ? { signature: signedTransactionOrSignature }
497
- : { signedTransaction: signedTransactionOrSignature })
498
- };
499
- // ✅ UPDATED: /transaction/auth/submit/${transactionId} → /transactions/${transactionId}/submit
500
- const res = await fetch(`${getPersApiUrl(this.useStaging)}/transactions/submit`, {
501
- method: "POST",
502
- headers: {
503
- "Content-Type": "application/json",
504
- 'x-project-key': this.getProjectKey(),
505
- Authorization: `Bearer ${persAccessToken}`,
506
- },
507
- body: JSON.stringify(dto),
508
- });
509
- if (!res.ok)
510
- throw new Error("Failed to submit transaction");
511
- return await res.json();
512
- }
513
- /**
514
- * Fetches a prepared transaction for signing by transactionId
515
- * @param transactionId - The transaction ID to fetch
516
- * @param persAccessToken - The PERS access token for authentication (Bearer)
517
- * @returns The prepared transaction data
518
- * @throws If the request fails
519
- */
520
- static async fetchPreparedTransaction(transactionId, persAccessToken) {
521
- // ✅ UPDATED: /transaction/auth/prepare-signing/${transactionId} → /transactions/${transactionId}/prepare
522
- const res = await fetch(`${getPersApiUrl(this.useStaging)}/transactions/${transactionId}/prepare`, {
523
- headers: {
524
- "Content-Type": "application/json",
525
- "x-project-key": this.getProjectKey(),
526
- Authorization: `Bearer ${persAccessToken}`,
527
- },
528
- });
529
- const response = await res.json();
530
- if (!res.ok) {
531
- // Throw structured error with the backend response
532
- throw {
533
- status: res.status,
534
- message: response.message || "Failed to fetch prepared transaction",
535
- error: response
536
- };
537
- }
538
- return response;
539
- }
540
- /**
541
- * Claims a reward from the PERS blockchain system
542
- * @param rewardId - The ID of the reward to claim
543
- * @param pointsCost - The points cost for the reward
544
- * @param persAccessToken - The PERS access token for authentication (Bearer)
545
- * @returns The reward claim response including reward image URL
546
- * @throws If the request fails
547
- */
548
- static async claimReward(rewardId, pointsCost, persAccessToken) {
549
- const res = await fetch(`${getPersApiUrl(this.useStaging)}/rewards/auth/claim`, {
550
- method: "POST",
551
- headers: {
552
- "Content-Type": "application/json",
553
- 'x-project-key': this.getProjectKey(),
554
- Authorization: `Bearer ${persAccessToken}`,
555
- },
556
- body: JSON.stringify({
557
- rewardId,
558
- pointsCost
559
- }),
560
- });
561
- if (!res.ok) {
562
- const error = await res.text();
563
- throw new Error(`Failed to claim reward: ${error}`);
564
- }
565
- return await res.json();
566
- }
567
- /**
568
- * Get all active campaigns
569
- * @returns Promise with list of active campaigns
570
- */
571
- static async getActiveCampaigns() {
572
- try {
573
- const response = await fetch(`${getPersApiUrl(this.useStaging)}/campaign`, {
574
- method: 'GET',
575
- headers: {
576
- 'accept': 'application/json',
577
- 'x-project-key': this.getProjectKey()
578
- }
579
- });
580
- if (!response.ok) {
581
- const errorData = await response.json().catch(() => ({}));
582
- throw {
583
- status: response.status,
584
- message: errorData.message || 'Failed to fetch active campaigns',
585
- error: errorData
586
- };
587
- }
588
- const data = await response.json();
589
- return data;
590
- }
591
- catch (error) {
592
- throw error;
593
- }
594
- }
595
- /**
596
- * Claims a campaign for a user
597
- * @param campaignId - The ID of the campaign to claim
598
- * @param persAccessToken - The PERS access token for authentication (Bearer)
599
- * @returns Promise with the campaign claim response
600
- * @throws If the request fails
601
- */
602
- static async claimCampaign(campaignId, persAccessToken) {
463
+ static async submitTransaction(transactionId, signedTransactionOrSignature, persAccessToken, submissionType, tenantId) {
603
464
  try {
604
- const response = await fetch(`${getPersApiUrl(this.useStaging)}/campaign/auth/claim`, {
605
- method: 'POST',
465
+ // Map TransactionFormat to the backend's expected submission type
466
+ const dto = {
467
+ transactionId,
468
+ type: submissionType,
469
+ ...(submissionType === browser.TRANSACTION_FORMATS.EIP_712
470
+ ? { signature: signedTransactionOrSignature }
471
+ : { signedTransaction: signedTransactionOrSignature })
472
+ };
473
+ // ✅ UPDATED: /transaction/auth/submit/${transactionId} → /transactions/${transactionId}/submit
474
+ const res = await fetch(`${getPersApiUrl(this.useStaging)}/transactions/submit`, {
475
+ method: "POST",
606
476
  headers: {
607
- 'accept': 'application/json',
608
- 'x-project-key': this.getProjectKey(),
609
- 'Authorization': `Bearer ${persAccessToken}`,
610
- 'Content-Type': 'application/json'
477
+ "Content-Type": "application/json",
478
+ 'x-project-key': await this.getProjectKey(tenantId),
479
+ Authorization: `Bearer ${persAccessToken}`,
611
480
  },
612
- body: JSON.stringify({
613
- campaignId
614
- })
481
+ body: JSON.stringify(dto),
615
482
  });
616
- if (!response.ok) {
617
- const errorData = await response.json().catch(() => ({}));
483
+ const response = await res.json();
484
+ if (!res.ok) {
485
+ // Throw structured error with the backend response
618
486
  throw {
619
- status: response.status,
620
- message: errorData.message || 'Failed to claim campaign',
621
- error: errorData
487
+ status: res.status,
488
+ message: response.message || "Failed to submit transaction",
489
+ error: response
622
490
  };
623
491
  }
624
- const data = await response.json();
625
- return data;
492
+ return response;
626
493
  }
627
494
  catch (error) {
628
- throw error;
495
+ // Rethrow a structured error
496
+ throw {
497
+ status: error.status || 500,
498
+ message: error.message || 'Failed to submit transaction to PERS backend',
499
+ error
500
+ };
629
501
  }
630
502
  }
631
503
  /**
632
- * Gets all campaign claims for the authenticated user
504
+ * Fetches a prepared transaction for signing by transactionId
505
+ * @param transactionId - The transaction ID to fetch
633
506
  * @param persAccessToken - The PERS access token for authentication (Bearer)
634
- * @returns Promise with list of user's campaign claims
635
- * @throws If the request fails
636
- */
637
- static async getUserCampaignClaims(persAccessToken) {
638
- try {
639
- const response = await fetch(`${getPersApiUrl(this.useStaging)}/campaign/auth/claim`, {
640
- method: 'GET',
641
- headers: {
642
- 'accept': 'application/json',
643
- 'x-project-key': this.getProjectKey(),
644
- 'Authorization': `Bearer ${persAccessToken}`
645
- }
646
- });
647
- if (!response.ok) {
648
- const errorData = await response.json().catch(() => ({}));
649
- throw {
650
- status: response.status,
651
- message: errorData.message || 'Failed to fetch user campaign claims',
652
- error: errorData
653
- };
654
- }
655
- const data = await response.json();
656
- return data;
657
- }
658
- catch (error) {
659
- throw error;
660
- }
661
- }
662
- /**
663
- * Gets all available rewards for redemption
664
- * @returns Promise with list of available rewards that can be exchanged for points
507
+ * @param tenantId - Optional tenant ID for automatic initialization
508
+ * @returns The prepared transaction data
665
509
  * @throws If the request fails
666
510
  */
667
- static async getAvailableRedemptions() {
511
+ static async fetchPreparedTransaction(transactionId, persAccessToken, tenantId) {
668
512
  try {
669
- const response = await fetch(`${getPersApiUrl(this.useStaging)}/redemption`, {
670
- method: 'GET',
513
+ // UPDATED: /transaction/auth/prepare-signing/${transactionId} → /transactions/${transactionId}/prepare
514
+ const res = await fetch(`${getPersApiUrl(this.useStaging)}/transactions/${transactionId}/prepare`, {
671
515
  headers: {
672
- 'accept': 'application/json',
673
- 'x-project-key': this.getProjectKey()
674
- }
516
+ "Content-Type": "application/json",
517
+ "x-project-key": await this.getProjectKey(tenantId),
518
+ Authorization: `Bearer ${persAccessToken}`,
519
+ },
675
520
  });
676
- if (!response.ok) {
677
- const errorData = await response.json().catch(() => ({}));
521
+ const response = await res.json();
522
+ if (!res.ok) {
523
+ // Throw structured error with the backend response
678
524
  throw {
679
- status: response.status,
680
- message: errorData.message || 'Failed to fetch available redemptions',
681
- error: errorData
525
+ status: res.status,
526
+ message: response.message || "Failed to fetch prepared transaction",
527
+ error: response
682
528
  };
683
529
  }
684
- const data = await response.json();
685
- return data;
530
+ return response;
686
531
  }
687
532
  catch (error) {
688
- throw error;
533
+ // Rethrow a structured error
534
+ throw {
535
+ status: error.status || 500,
536
+ message: error.message || 'Failed to fetch prepared transaction from PERS backend',
537
+ error
538
+ };
689
539
  }
690
540
  }
691
541
  /**
@@ -703,7 +553,7 @@ class PersService {
703
553
  * @throws If transaction data is not available
704
554
  */
705
555
  static getTransactionDataForSigning(transactionResponse) {
706
- if (!this.isTransactionReadyForSigning(transactionResponse)) {
556
+ if (!transactionResponse.signingData) {
707
557
  throw new Error('Transaction data is not ready for signing');
708
558
  }
709
559
  return transactionResponse.signingData;
@@ -712,8 +562,53 @@ class PersService {
712
562
  PersService.config = DEFAULT_PERS_CONFIG;
713
563
  PersService.tenantCache = new Map();
714
564
  PersService.currentProjectKey = null;
565
+ PersService.currentTenantId = null;
715
566
  PersService.useStaging = false;
567
+ PersService.TENANT_CACHE_TTL = 24 * 60 * 60 * 1000; // 24 hours - tenant configs are essentially static
716
568
 
569
+ // Adapter function to convert backend signature response to legacy format
570
+ const adaptSignatureResponse = (newResponse) => {
571
+ if (!newResponse.signature) {
572
+ console.error('[KeyWallet] No signature in response:', newResponse);
573
+ throw new Error('Signature missing from response');
574
+ }
575
+ // Handle structured signature format (typically from EIP-712 signing)
576
+ if (typeof newResponse.signature === 'object' && 'r' in newResponse.signature) {
577
+ const { r, s, recid } = newResponse.signature;
578
+ return {
579
+ status: 'Signed',
580
+ signature: {
581
+ r,
582
+ s,
583
+ recid // Use recid directly from backend
584
+ }
585
+ };
586
+ }
587
+ // Handle string signature format (potentially from hash signing)
588
+ if (typeof newResponse.signature === 'string') {
589
+ // Parse string signature into components
590
+ // Assuming it's a hex string that can be split into r, s, v components
591
+ const signature = newResponse.signature;
592
+ if (signature.length === 132) { // 0x + 32 bytes r + 32 bytes s + 1 byte v
593
+ const r = '0x' + signature.slice(2, 66);
594
+ const s = '0x' + signature.slice(66, 130);
595
+ const v = parseInt(signature.slice(130, 132), 16);
596
+ const recid = v >= 27 ? v - 27 : v; // Convert v to recid
597
+ return {
598
+ status: 'Signed',
599
+ signature: {
600
+ r,
601
+ s,
602
+ recid
603
+ }
604
+ };
605
+ }
606
+ console.error('[KeyWallet] Invalid string signature length:', signature.length);
607
+ throw new Error(`Invalid string signature format - expected 132 characters, got ${signature.length}`);
608
+ }
609
+ console.error('[KeyWallet] Invalid signature format - expected structured object or string:', typeof newResponse.signature);
610
+ throw new Error('Invalid signature format in response - expected structured object or string from backend');
611
+ };
717
612
  let DfnsError$1 = class DfnsError extends Error {
718
613
  constructor(code, message, details) {
719
614
  super(message);
@@ -732,14 +627,16 @@ const assertSigned = (res) => {
732
627
  };
733
628
  const combineSignature = (res) => {
734
629
  if (!res.signature) {
630
+ console.error('[KeyWallet] No signature in response for combining:', res);
735
631
  throw new DfnsError$1(-1, "signature missing", res);
736
632
  }
737
633
  const { r, s, recid } = res.signature;
738
- return ethers.Signature.from({
634
+ const ethersSignature = ethers.Signature.from({
739
635
  r,
740
636
  s,
741
637
  v: recid ? 0x1c : 0x1b, // Assuming 0x1c for chain_id > 0, 0x1b otherwise. DFNS usually provides recid.
742
- }).serialized;
638
+ });
639
+ return ethersSignature.serialized;
743
640
  };
744
641
  class KeyWallet extends ethers.AbstractSigner {
745
642
  constructor(options, provider) {
@@ -765,9 +662,11 @@ class KeyWallet extends ethers.AbstractSigner {
765
662
  return this.address;
766
663
  }
767
664
  async signHash(hash) {
768
- const res = await this.signingService.signHash(this.authToken, this.metadata.id, hash);
665
+ const rawRes = await this.signingService.signHash(this.authToken, this.metadata.id, hash);
666
+ const res = adaptSignatureResponse(rawRes);
769
667
  assertSigned(res);
770
- return combineSignature(res);
668
+ const combinedSignature = combineSignature(res);
669
+ return combinedSignature;
771
670
  }
772
671
  async signTransaction(tx) {
773
672
  // Resolve any addresses
@@ -804,9 +703,11 @@ class KeyWallet extends ethers.AbstractSigner {
804
703
  async signTypedData(domain, types, value) {
805
704
  try {
806
705
  // Use DFNS native EIP-712 support with correct structure
807
- const res = await this.signingService.signTypedData(this.authToken, this.metadata.id, domain, types, value);
706
+ const rawRes = await this.signingService.signTypedData(this.authToken, this.metadata.id, domain, types, value);
707
+ const res = adaptSignatureResponse(rawRes);
808
708
  assertSigned(res);
809
- return combineSignature(res);
709
+ const combinedSignature = combineSignature(res);
710
+ return combinedSignature;
810
711
  }
811
712
  catch (error) {
812
713
  console.error('[KeyWallet] EIP-712 signing failed:', error);
@@ -936,27 +837,36 @@ class SigningService {
936
837
  const config = getServiceConfig();
937
838
  try {
938
839
  // Step 1: Initialize signing and get challenge
939
- const initResponse = await httpClient.post(`${config.apiUrl}/wallets/signatures/init`, {
940
- authToken,
840
+ const initRequest = {
941
841
  walletId,
942
- request,
943
- appId: config.appId,
944
- });
945
- const { requestBody, challenge } = initResponse.data;
842
+ request
843
+ };
844
+ const initResponse = await httpClient.post(`${config.apiUrl}/wallets/signatures/init`, initRequest, { Authorization: `Bearer ${authToken}` });
845
+ const backendInitResponse = initResponse.data;
846
+ if (!backendInitResponse.success || !backendInitResponse.data) {
847
+ console.error('[SigningService] Init response invalid:', backendInitResponse);
848
+ throw new Error('Signature initialization failed');
849
+ }
850
+ const { requestBody, challenge } = backendInitResponse.data;
946
851
  // Step 2: Sign the challenge using WebAuthn
947
852
  const assertion = await this.webAuthnProvider.sign(challenge);
948
853
  // Step 3: Complete signing with signed challenge
949
- const completeResponse = await httpClient.post(`${config.apiUrl}/wallets/signatures/complete`, {
950
- authToken,
854
+ const completeRequest = {
951
855
  walletId,
952
856
  requestBody,
953
857
  signedChallenge: {
954
- challengeIdentifier: challenge.challengeIdentifier,
858
+ challengeIdentifier: challenge?.challengeIdentifier,
955
859
  firstFactor: assertion,
956
- },
957
- appId: config.appId,
958
- });
959
- return completeResponse.data;
860
+ }
861
+ };
862
+ const completeResponse = await httpClient.post(`${config.apiUrl}/wallets/signatures/complete`, completeRequest, { Authorization: `Bearer ${authToken}` });
863
+ const backendCompleteResponse = completeResponse.data;
864
+ if (!backendCompleteResponse.success || !backendCompleteResponse.data) {
865
+ console.error('[SigningService] Complete response invalid:', backendCompleteResponse);
866
+ throw new Error('Signature completion failed');
867
+ }
868
+ // Return the complete signature data from backend response
869
+ return backendCompleteResponse.data;
960
870
  }
961
871
  catch (error) {
962
872
  console.error(`[SigningService] Signing failed for wallet ${walletId}:`, error);
@@ -1153,8 +1063,8 @@ class TransactionValidator {
1153
1063
  if (!params.authTokens) {
1154
1064
  throw TransactionErrorHandler.createError(exports.TransactionSigningErrorCode.INVALID_TOKENS, 'Authentication tokens are required', params.transactionId);
1155
1065
  }
1156
- if (!params.authTokens.backendAuthToken || !params.authTokens.persAccessToken) {
1157
- throw TransactionErrorHandler.createError(exports.TransactionSigningErrorCode.INVALID_TOKENS, 'Both backend and PERS authentication tokens are required', params.transactionId);
1066
+ if (!params.authTokens.signerAuthToken || !params.authTokens.persAccessToken) {
1067
+ throw TransactionErrorHandler.createError(exports.TransactionSigningErrorCode.INVALID_TOKENS, 'Both signer and PERS authentication tokens are required', params.transactionId);
1158
1068
  }
1159
1069
  if (!params.ethersProviderUrl || typeof params.ethersProviderUrl !== 'string') {
1160
1070
  throw TransactionErrorHandler.createError(exports.TransactionSigningErrorCode.INVALID_TOKENS, 'Ethers provider URL is required', params.transactionId);
@@ -1166,81 +1076,18 @@ class TransactionValidator {
1166
1076
  * @returns true if valid, throws error if invalid
1167
1077
  */
1168
1078
  static validateAuthTokens(authTokens) {
1169
- if (!authTokens.persAccessToken || !authTokens.backendAuthToken) {
1170
- throw TransactionErrorHandler.createError(exports.TransactionSigningErrorCode.INVALID_TOKENS, 'Both PERS access token and backend auth token are required');
1079
+ if (!authTokens.persAccessToken || !authTokens.signerAuthToken) {
1080
+ throw TransactionErrorHandler.createError(exports.TransactionSigningErrorCode.INVALID_TOKENS, 'Both PERS access token and signer auth token are required');
1171
1081
  }
1172
1082
  return true;
1173
1083
  }
1174
1084
  }
1175
1085
 
1176
- /**
1177
- * Handles transaction submission, success flows, and redirect logic
1178
- */
1179
- class TransactionSubmissionHandler {
1180
- /**
1181
- * Create redirect URL with transaction parameters
1182
- * @param returnUrl - Base return URL
1183
- * @param transactionHash - Transaction hash to include
1184
- * @param metadata - Optional metadata to include as parameters
1185
- * @returns Promise resolving to the complete redirect URL
1186
- */
1187
- static async createRedirectUrl(returnUrl, transactionHash, metadata) {
1188
- const additionalParams = {
1189
- txHash: transactionHash,
1190
- success: 'true'
1191
- };
1192
- // Add metadata if available
1193
- if (metadata) {
1194
- Object.entries(metadata).forEach(([key, value]) => {
1195
- // metadata is already constrained to string values by TransactionMetadata interface
1196
- additionalParams[key] = value;
1197
- });
1198
- }
1199
- // Dynamic import to avoid build issues
1200
- const { createUrlWithSearchParams } = await Promise.resolve().then(function () { return searchParams; });
1201
- return createUrlWithSearchParams(returnUrl, ['transactionId', 'returnUrl', 'jwt', 'token'], additionalParams);
1202
- }
1203
- /**
1204
- * Submit transaction and handle success flow
1205
- * @param preparedTransaction - The prepared transaction
1206
- * @param signature - The transaction signature
1207
- * @param signingData - The signing data used
1208
- * @param authTokens - Authentication tokens
1209
- * @param transactionId - Transaction ID for tracking
1210
- * @param returnUrl - Optional return URL for redirect
1211
- * @param metadata - Optional metadata for redirect parameters
1212
- * @returns Promise resolving to submission result
1213
- */
1214
- static async handleTransactionSubmission(preparedTransaction, signature, signingData, authTokens, returnUrl, metadata) {
1215
- // Submit signed transaction
1216
- const submitResult = await PersService.submitTransaction(preparedTransaction.transaction.id, signature, authTokens.persAccessToken, signingData.format);
1217
- // Get transaction hash for return URL or response
1218
- const transactionHash = submitResult.transaction?.transactionHash || '';
1219
- // Handle success - check for return URL
1220
- if (returnUrl) {
1221
- const redirectUrl = await this.createRedirectUrl(returnUrl, transactionHash, metadata);
1222
- return { submitResult, shouldRedirect: true, redirectUrl };
1223
- }
1224
- else {
1225
- return { submitResult, shouldRedirect: false };
1226
- }
1227
- }
1228
- }
1229
-
1230
1086
  /**
1231
1087
  * Utility class for coordinating WebAuthn operations to prevent conflicts
1232
1088
  * Manages global state flags to ensure only one WebAuthn operation runs at a time
1233
1089
  */
1234
1090
  class WebAuthnCoordinator {
1235
- /**
1236
- * Clear the landing authentication flag
1237
- * Used when starting a new transaction signing flow
1238
- */
1239
- static clearLandingAuthentication() {
1240
- if (typeof window !== 'undefined') {
1241
- window.landingAuthenticationInProgress = false;
1242
- }
1243
- }
1244
1091
  /**
1245
1092
  * Check if a WebAuthn operation is currently in progress
1246
1093
  * @returns True if an operation is in progress, false otherwise
@@ -1273,16 +1120,16 @@ const SIGNABLE_STATUSES = [browser.TransactionStatus.PENDING_SIGNATURE, browser.
1273
1120
  * Uses constructor-based dependency injection for WebAuthn provider
1274
1121
  */
1275
1122
  class TransactionSigningService {
1276
- constructor(webAuthnProvider) {
1277
- this.webAuthnProvider = webAuthnProvider;
1123
+ constructor(config) {
1124
+ this.webAuthnProvider = config.webAuthnProvider;
1278
1125
  }
1279
1126
  /**
1280
1127
  * Prepare transaction for signing - fetch and validate
1281
1128
  */
1282
- async prepareTransaction(transactionId, authTokens) {
1129
+ async prepareTransaction(transactionId, authTokens, tenantId) {
1283
1130
  let preparedTransaction;
1284
1131
  try {
1285
- preparedTransaction = await PersService.fetchPreparedTransaction(transactionId, authTokens.persAccessToken);
1132
+ preparedTransaction = await PersService.fetchPreparedTransaction(transactionId, authTokens.persAccessToken, tenantId);
1286
1133
  }
1287
1134
  catch (err) {
1288
1135
  // Use TransactionErrorHandler to process PERS API errors
@@ -1303,7 +1150,7 @@ class TransactionSigningService {
1303
1150
  // Authenticate with PERS using backend signer token to set up signing account
1304
1151
  let updatedPersAccessToken = authTokens.persAccessToken;
1305
1152
  try {
1306
- const persSignerAuth = await PersService.authenticateUser(authTokens.backendAuthToken);
1153
+ const persSignerAuth = await PersService.authenticateUser(authTokens.signerAuthToken, tenantId);
1307
1154
  // Update PERS access token with the new one that has signing account linked
1308
1155
  const newPersAccessToken = persSignerAuth.accessToken;
1309
1156
  if (newPersAccessToken) {
@@ -1317,7 +1164,7 @@ class TransactionSigningService {
1317
1164
  }
1318
1165
  // If transaction is 'created' status but doesn't have signingData, fetch again with wallet-enabled token
1319
1166
  if (transactionStatus === browser.TransactionStatus.CREATED && !PersService.isTransactionReadyForSigning(preparedTransaction)) {
1320
- preparedTransaction = await PersService.fetchPreparedTransaction(transactionId, updatedPersAccessToken);
1167
+ preparedTransaction = await PersService.fetchPreparedTransaction(transactionId, updatedPersAccessToken, tenantId);
1321
1168
  }
1322
1169
  if (!PersService.isTransactionReadyForSigning(preparedTransaction)) {
1323
1170
  throw TransactionErrorHandler.createError(exports.TransactionSigningErrorCode.TRANSACTION_NOT_READY, 'Transaction is not ready for signing', transactionId);
@@ -1332,19 +1179,21 @@ class TransactionSigningService {
1332
1179
  */
1333
1180
  async prepareWallet(authTokens, ethersProviderUrl) {
1334
1181
  // Wallet validation will be handled through API responses - no localStorage needed
1335
- let walletData;
1182
+ let walletListResult;
1336
1183
  try {
1337
- walletData = await WalletService.listWallets(authTokens.backendAuthToken);
1338
- console.log('[TransactionSigningService] Wallet list response:', walletData);
1184
+ // Use new WalletService API with signer token
1185
+ walletListResult = await WalletService.listWallets(authTokens.signerAuthToken);
1339
1186
  }
1340
1187
  catch (error) {
1341
1188
  console.error('[TransactionSigningService] Wallet list API failed:', error);
1342
1189
  throw TransactionErrorHandler.createError(exports.TransactionSigningErrorCode.WALLET_NOT_AVAILABLE, 'Failed to retrieve wallet information. Please refresh the page and try again.', undefined, error);
1343
1190
  }
1344
- // Check both possible response structures for compatibility
1345
- const wallets = walletData?.wallets || walletData?.items || [];
1191
+ // Handle multiple response formats for compatibility
1192
+ const wallets = walletListResult?.items ||
1193
+ walletListResult?.data?.wallets ||
1194
+ walletListResult?.wallets || [];
1346
1195
  if (!wallets?.length) {
1347
- console.error('[TransactionSigningService] No wallets found in response:', walletData);
1196
+ console.error('[TransactionSigningService] No wallets found in response:', walletListResult);
1348
1197
  throw TransactionErrorHandler.createError(exports.TransactionSigningErrorCode.WALLET_NOT_AVAILABLE, 'No wallet found for transaction signing. Please refresh the page and complete account setup.');
1349
1198
  }
1350
1199
  // Create SigningService with injected WebAuthn provider
@@ -1352,7 +1201,7 @@ class TransactionSigningService {
1352
1201
  // Create wallet instance with provider - use unknown type for flexibility
1353
1202
  const provider = new ethers.JsonRpcProvider(ethersProviderUrl);
1354
1203
  const wallet = new KeyWallet({
1355
- authToken: authTokens.backendAuthToken,
1204
+ authToken: authTokens.signerAuthToken,
1356
1205
  wallet: wallets[0],
1357
1206
  signingService: signingService,
1358
1207
  }).connect(provider);
@@ -1361,7 +1210,9 @@ class TransactionSigningService {
1361
1210
  /**
1362
1211
  * Execute the transaction signing with WebAuthn coordination
1363
1212
  */
1364
- async executeTransactionSigning(wallet, preparedTransaction) {
1213
+ async executeTransactionSigning(wallet,
1214
+ // preparedTransaction: TransactionRequestResponseDTO,
1215
+ signingData) {
1365
1216
  // Check for concurrent WebAuthn operations
1366
1217
  if (WebAuthnCoordinator.checkConcurrentOperations()) {
1367
1218
  throw TransactionErrorHandler.createError(exports.TransactionSigningErrorCode.WEBAUTHN_OPERATION_IN_PROGRESS, 'Another WebAuthn operation is in progress. Please wait and try again.');
@@ -1369,7 +1220,7 @@ class TransactionSigningService {
1369
1220
  // Set WebAuthn operation flag for transaction signing
1370
1221
  WebAuthnCoordinator.setOperationInProgress(true);
1371
1222
  try {
1372
- const signingData = PersService.getTransactionDataForSigning(preparedTransaction);
1223
+ // const signingData = PersService.getTransactionDataForSigning(preparedTransaction);
1373
1224
  const signature = await wallet.signPersTransaction(signingData);
1374
1225
  return signature;
1375
1226
  }
@@ -1388,6 +1239,13 @@ class TransactionSigningService {
1388
1239
  WebAuthnCoordinator.setOperationInProgress(false);
1389
1240
  }
1390
1241
  }
1242
+ async getPersSigningData(data) {
1243
+ // Step 1: Prepare transaction for signing
1244
+ const { preparedTransaction } = await this.prepareTransaction(data.transactionId, data.authTokens, data.tenantId);
1245
+ // const signingData = PersService.getTransactionDataForSigning(preparedTransaction);
1246
+ const signingData = PersService.getTransactionDataForSigning(preparedTransaction);
1247
+ return signingData;
1248
+ }
1391
1249
  /**
1392
1250
  * Main transaction signing orchestration method
1393
1251
  * Handles the complete flow from preparation to submission
@@ -1395,31 +1253,23 @@ class TransactionSigningService {
1395
1253
  * @returns Promise resolving to transaction signing result
1396
1254
  * @throws TransactionSigningError for validation and operation failures
1397
1255
  */
1398
- async signTransaction(params) {
1256
+ async signTransaction(params, signingData) {
1399
1257
  // Validate input parameters first using TransactionValidator
1400
1258
  TransactionValidator.validateSigningParams(params);
1401
- const { transactionId, authTokens, ethersProviderUrl, returnUrl, metadata } = params;
1259
+ const { transactionId, authTokens, ethersProviderUrl } = params;
1402
1260
  console.info(`[TransactionSigningService] Starting signature process for ${transactionId}`);
1403
1261
  try {
1404
- // Clear previous landing authentication state
1405
- WebAuthnCoordinator.clearLandingAuthentication();
1406
- // Step 1: Prepare transaction for signing
1407
- const { preparedTransaction, updatedTokens } = await this.prepareTransaction(transactionId, authTokens);
1408
1262
  // Step 2: Prepare wallet for signing
1409
- const wallet = await this.prepareWallet(updatedTokens, ethersProviderUrl);
1263
+ const wallet = await this.prepareWallet(authTokens, ethersProviderUrl);
1410
1264
  // Step 3: Execute transaction signing
1411
- const signature = await this.executeTransactionSigning(wallet, preparedTransaction);
1412
- const signingData = PersService.getTransactionDataForSigning(preparedTransaction);
1413
- // Step 4: Submit transaction and handle success using TransactionSubmissionHandler
1414
- const { submitResult, shouldRedirect, redirectUrl } = await TransactionSubmissionHandler.handleTransactionSubmission(preparedTransaction, signature, signingData, updatedTokens, returnUrl, metadata);
1415
- console.info(`[TransactionSigningService] Completed successfully: ${transactionId}`);
1265
+ const signature = await this.executeTransactionSigning(wallet, signingData);
1266
+ console.info(`[TransactionSigningService] Completed signing successfully: ${transactionId}`);
1416
1267
  return {
1417
1268
  success: true,
1418
1269
  transactionId,
1419
- transactionHash: submitResult.transaction?.transactionHash ?? undefined,
1270
+ signingData,
1271
+ // transactionHash: submitResult.transaction?.transactionHash ?? undefined,
1420
1272
  signature,
1421
- shouldRedirect,
1422
- redirectUrl
1423
1273
  };
1424
1274
  }
1425
1275
  catch (error) {
@@ -1445,710 +1295,727 @@ class TransactionSigningService {
1445
1295
  }
1446
1296
  }
1447
1297
 
1298
+ /**
1299
+ * Handles transaction submission, success flows, and redirect logic
1300
+ */
1301
+ class TransactionSubmissionHandler {
1302
+ /**
1303
+ * Create redirect URL with transaction parameters
1304
+ * @param returnUrl - Base return URL
1305
+ * @param transactionHash - Transaction hash to include
1306
+ * @param metadata - Optional metadata to include as parameters
1307
+ * @returns Promise resolving to the complete redirect URL
1308
+ */
1309
+ static async createRedirectUrl(returnUrl, transactionHash) {
1310
+ const additionalParams = {
1311
+ txHash: transactionHash,
1312
+ success: 'true'
1313
+ };
1314
+ // Build URL with required params
1315
+ const paramKeys = ['transactionId', 'returnUrl', 'jwt', 'token'];
1316
+ const url = new URL(returnUrl, window.location.origin);
1317
+ // Add required params if present in additionalParams
1318
+ paramKeys.forEach(key => {
1319
+ if (additionalParams[key]) {
1320
+ url.searchParams.set(key, additionalParams[key]);
1321
+ }
1322
+ });
1323
+ // Add all other params
1324
+ Object.entries(additionalParams).forEach(([key, value]) => {
1325
+ if (!paramKeys.includes(key)) {
1326
+ url.searchParams.set(key, value);
1327
+ }
1328
+ });
1329
+ return url.toString();
1330
+ }
1331
+ }
1332
+
1448
1333
  /**
1449
1334
  * Authentication service for user login and registration
1450
1335
  * Uses constructor-based dependency injection for WebAuthn provider
1336
+ * Updated for new v1 API endpoints
1451
1337
  */
1452
1338
  class AuthenticationService {
1453
- constructor(webAuthnProvider) {
1454
- this.webAuthnProvider = webAuthnProvider;
1339
+ constructor(config) {
1340
+ this.signerToken = null;
1341
+ this.config = config;
1342
+ this.webAuthnProvider = config.webAuthnProvider;
1455
1343
  }
1456
1344
  /**
1457
- * Authenticate user with username
1458
- * @param username - User identifier
1459
- * @returns Promise resolving to authentication token
1345
+ * Login with PERS token to get signer JWT
1346
+ * @param persToken - PERS JWT from PERS authentication
1347
+ * @returns Promise resolving to login response or provider challenge data
1460
1348
  */
1461
- async login(username) {
1349
+ async loginWithPersToken(persToken) {
1462
1350
  const httpClient = getHttpClient();
1463
1351
  const config = getServiceConfig();
1464
1352
  try {
1465
- const response = await httpClient.post(`${config.apiUrl}/login`, {
1466
- username,
1467
- appId: config.appId,
1468
- });
1469
- // Check if the response contains an error
1470
- if (response.data.status && response.data.status >= 400) {
1471
- throw new Error(response.data.message || 'Login failed');
1472
- }
1473
- if (!response.data.token) {
1474
- throw new Error(response.data.message || 'No token received from login');
1353
+ const requestBody = {
1354
+ authToken: persToken
1355
+ };
1356
+ const response = await httpClient.post(`${config.apiUrl}/auth/login`, requestBody
1357
+ // Note: Login endpoint doesn't require Authorization header (public endpoint)
1358
+ );
1359
+ const backendResponse = response.data;
1360
+ if (backendResponse && backendResponse.success) {
1361
+ // Check if it's JWT response or wrapped provider response
1362
+ if ('access_token' in backendResponse) {
1363
+ // JWT response - set token and return
1364
+ this.signerToken = backendResponse.access_token;
1365
+ return backendResponse;
1366
+ }
1367
+ else {
1368
+ // Wrapped provider response - return the data
1369
+ return backendResponse.data;
1370
+ }
1475
1371
  }
1476
- console.info(`[WebAuthn] User login successful: ${username}`);
1477
- return response.data.token;
1372
+ console.error('[AuthenticationService] Invalid response - no success or success=false:', backendResponse);
1373
+ throw new Error('Login failed: Invalid response format');
1478
1374
  }
1479
1375
  catch (error) {
1480
- console.error(`[AuthenticationService] Login failed for ${username}:`, error);
1376
+ // console.error(`[AuthenticationService] PERS token login failed:`, error);
1481
1377
  throw error;
1482
1378
  }
1483
1379
  }
1484
1380
  /**
1485
- * Register new user with WebAuthn credential
1486
- * @param username - User identifier
1487
- * @returns Promise resolving to registration result
1381
+ * Verify signer token validity
1382
+ * @param token - Signer JWT to verify
1383
+ * @returns Promise resolving to verification result
1488
1384
  */
1489
- async register(username) {
1385
+ async verifyToken(token) {
1490
1386
  const httpClient = getHttpClient();
1491
1387
  const config = getServiceConfig();
1492
1388
  try {
1493
- // Step 1: Initialize registration and get challenge
1494
- const initResponse = await httpClient.post(`${config.apiUrl}/register/init`, {
1495
- username,
1496
- appId: config.appId,
1497
- });
1498
- const challenge = initResponse.data;
1499
- // Check if the registration init failed
1500
- if (challenge.status && challenge.status >= 400) {
1501
- throw new Error(challenge.message || 'Registration initialization failed');
1502
- }
1503
- // Step 2: Create WebAuthn credential using the challenge
1504
- const attestation = await this.webAuthnProvider.create(challenge);
1505
- console.info(`[WebAuthn] Credential created successfully for user: ${username}`);
1506
- // Step 3: Complete registration with signed challenge
1507
- const completeResponse = await httpClient.post(`${config.apiUrl}/register/complete`, {
1508
- signedChallenge: { firstFactorCredential: attestation },
1509
- temporaryAuthenticationToken: challenge.temporaryAuthenticationToken,
1510
- appId: config.appId,
1511
- });
1512
- const result = completeResponse.data;
1513
- // Check if the registration completion failed
1514
- if (result.status && result.status >= 400) {
1515
- throw new Error(result.message || 'Registration completion failed');
1389
+ const requestBody = {
1390
+ token
1391
+ };
1392
+ const response = await httpClient.post(`${config.apiUrl}/auth/verify`, requestBody, { Authorization: `Bearer ${token}` });
1393
+ const verifyData = response.data;
1394
+ if (!verifyData.valid) {
1395
+ throw new Error('Token verification failed');
1516
1396
  }
1517
- console.info(`[AuthenticationService] Registration successful for: ${username}`);
1518
- return result;
1397
+ return verifyData;
1519
1398
  }
1520
1399
  catch (error) {
1521
- console.error(`[AuthenticationService] Registration failed for ${username}:`, error);
1400
+ console.error(`[AuthenticationService] Token verification failed:`, error);
1522
1401
  throw error;
1523
1402
  }
1524
1403
  }
1525
1404
  /**
1526
- * Validate authentication token (when backend supports it)
1527
- * @param authToken - Token to validate
1528
- * @returns Promise resolving to validation result
1405
+ * Initialize user registration
1406
+ * @param persToken - PERS JWT token (registration is public)
1407
+ * @returns Promise resolving to registration challenge
1529
1408
  */
1530
- async validateToken(authToken) {
1409
+ async initializeRegistration(persToken) {
1531
1410
  const httpClient = getHttpClient();
1532
1411
  const config = getServiceConfig();
1533
1412
  try {
1534
- const response = await httpClient.post(`${config.apiUrl}/validate-token`, {
1535
- authToken,
1536
- appId: config.appId,
1537
- });
1538
- return response.data.valid === true;
1413
+ const requestBody = {
1414
+ authToken: persToken
1415
+ };
1416
+ const response = await httpClient.post(`${config.apiUrl}/auth/register/init`, requestBody);
1417
+ const backendResponse = response.data;
1418
+ if (backendResponse.success && backendResponse.data) {
1419
+ return backendResponse.data;
1420
+ }
1421
+ throw new Error('Registration initialization failed: No data returned');
1539
1422
  }
1540
1423
  catch (error) {
1541
- console.error('[AuthenticationService] Error validating token:', error);
1542
- return false;
1424
+ // console.error(`[AuthenticationService] Registration initialization failed:`, error);
1425
+ throw error;
1543
1426
  }
1544
1427
  }
1545
1428
  /**
1546
- * Get user information (when backend supports it)
1547
- * @param authToken - Authentication token
1548
- * @returns Promise resolving to user info or null
1429
+ * Get current signer token
1430
+ * @returns The current signer JWT token
1431
+ */
1432
+ getSignerToken() {
1433
+ return this.signerToken;
1434
+ }
1435
+ /**
1436
+ * Set signer token (for external token management)
1437
+ * @param token - Signer JWT token
1549
1438
  */
1550
- async getUser(authToken) {
1439
+ setSignerToken(token) {
1440
+ this.signerToken = token;
1441
+ }
1442
+ /**
1443
+ * Complete registration with WebAuthn challenge data (v1 API format)
1444
+ * @param tmpAuthToken - Temporary auth token from init registration (temporaryAuthenticationToken)
1445
+ * @param signedChallenge - WebAuthn credential response (will be restructured for backend)
1446
+ * @param persToken - PERS JWT token (authToken)
1447
+ * @returns Promise resolving to registration result
1448
+ */
1449
+ async completeRegistrationWithChallenge(tmpAuthToken, signedChallenge, persToken) {
1551
1450
  const httpClient = getHttpClient();
1451
+ const config = getServiceConfig();
1552
1452
  try {
1553
- const response = await httpClient.get(`${getServiceConfig().apiUrl}/user`, {
1554
- 'Authorization': `Bearer ${authToken}`
1555
- });
1556
- return response.data;
1557
- }
1558
- catch (error) {
1559
- console.error('[AuthenticationService] Error getting user:', error);
1560
- return null;
1453
+ const requestBody = {
1454
+ temporaryAuthenticationToken: tmpAuthToken,
1455
+ signedChallenge: {
1456
+ firstFactorCredential: {
1457
+ credentialKind: signedChallenge.credentialKind || "Fido2",
1458
+ credentialInfo: {
1459
+ credId: signedChallenge.credentialInfo?.credId || "",
1460
+ clientData: signedChallenge.credentialInfo?.clientData || "",
1461
+ attestationData: signedChallenge.credentialInfo?.attestationData || ""
1462
+ }
1463
+ }
1464
+ },
1465
+ authToken: persToken
1466
+ };
1467
+ const response = await httpClient.post(`${config.apiUrl}/auth/register/complete`, requestBody);
1468
+ const backendResponse = response.data;
1469
+ if (backendResponse && backendResponse.success) {
1470
+ // Check if it's JWT response (has access_token at root level) or wrapped provider response
1471
+ if ('access_token' in backendResponse) {
1472
+ // JWT response - set token and return
1473
+ this.signerToken = backendResponse.access_token;
1474
+ return backendResponse;
1475
+ }
1476
+ else if ('data' in backendResponse) {
1477
+ // Wrapped provider response - registration is not complete yet, needs more steps
1478
+ return backendResponse.data;
1479
+ }
1480
+ else {
1481
+ // Wrapped provider response - return the data
1482
+ return backendResponse.data;
1483
+ }
1484
+ }
1485
+ console.error('[AuthenticationService] Registration with challenge failed - invalid response:', backendResponse);
1486
+ throw new Error('Registration completion with challenge failed');
1487
+ }
1488
+ catch (error) {
1489
+ console.error(`[AuthenticationService] Registration completion with challenge failed:`, error);
1490
+ throw error;
1561
1491
  }
1562
1492
  }
1563
- }
1564
-
1565
- /**
1566
- * PERS Blockchain Signer SDK - Simple Orchestrator
1567
- *
1568
- * Lightweight SDK that orchestrates existing services.
1569
- * Uses environment configuration and existing infrastructure.
1570
- */
1571
- /**
1572
- * Main PERS Signer SDK class
1573
- *
1574
- * Simple orchestrator that uses existing services and environment configuration.
1575
- * No complex initialization needed - services are already configured.
1576
- */
1577
- class PersSignerSDK {
1578
- constructor(config) {
1579
- this.config = config;
1580
- this.webAuthnProvider = config.webAuthnProvider;
1581
- // Set up the configuration provider with stable defaults from constants
1582
- const serviceConfig = {
1583
- apiUrl: config.apiUrl || SIGNER_CONFIG.DEFAULT_SIGNER_API_URL,
1584
- relyingParty: {
1585
- id: typeof window !== 'undefined' ? window.location.hostname : 'localhost',
1586
- name: config.relyingPartyName || SIGNER_CONFIG.DEFAULT_RELYING_PARTY_NAME,
1587
- origin: typeof window !== 'undefined' ? window.location.origin : undefined,
1588
- },
1589
- };
1590
- setConfigProvider(new WebConfigProvider(serviceConfig));
1591
- // Initialize services with the WebAuthn provider
1592
- this.authService = new AuthenticationService(this.webAuthnProvider);
1593
- this.signingService = new SigningService(this.webAuthnProvider);
1594
- this.transactionSigningService = new TransactionSigningService(this.webAuthnProvider);
1595
- }
1596
- /**
1597
- * Re-export TransactionSigningService with WebAuthn provider already injected
1598
- * This provides the same interface as the original service but with automatic provider injection
1599
- */
1600
- get TransactionSigningService() {
1601
- return this.transactionSigningService;
1602
- }
1603
- // ========================================================================
1604
- // HIGH-LEVEL METHODS (Same as Web Project)
1605
- // ========================================================================
1606
1493
  /**
1607
- * Complete user onboarding flow - same as web project
1608
- * Uses existing AuthenticationService and PersService
1494
+ * Combined authentication flow - handles both login and registration
1495
+ * @param identifier - User identifier (email/userId)
1496
+ * @param persAccessToken - PERS JWT token for authentication
1497
+ * @param webAuthnProvider - WebAuthn provider for credential creation
1498
+ * @param relyingPartyConfig - Configuration for WebAuthn relying party
1499
+ * @returns Promise resolving to authenticated user with signer token
1609
1500
  */
1610
- async authenticateUser(userInfo) {
1611
- const identifier = userInfo.identifier;
1501
+ async combinedAuthentication(identifier, persAccessToken) {
1612
1502
  try {
1613
- // Step 1: Try login first (existing user)
1614
- let signerAuthToken;
1503
+ // Step 1: Try to login with PERS token first (this will get signer JWT if user exists)
1504
+ let signerToken;
1615
1505
  try {
1616
- signerAuthToken = await this.authService.login(identifier);
1506
+ const loginResult = await this.loginWithPersToken(persAccessToken);
1507
+ // Extract token from login result
1508
+ if (loginResult && typeof loginResult === 'object' && 'access_token' in loginResult) {
1509
+ signerToken = loginResult.access_token;
1510
+ }
1511
+ else {
1512
+ throw new Error('Invalid login response format');
1513
+ }
1617
1514
  }
1618
1515
  catch (loginError) {
1619
- // User doesn't exist - register new user
1620
- await this.authService.register(identifier);
1621
- signerAuthToken = await this.authService.login(identifier);
1622
- }
1623
- // Step 2: Authenticate with PERS backend
1624
- let persAccessToken = '';
1625
- try {
1626
- const persResponse = await PersService.authenticateUser(signerAuthToken);
1627
- persAccessToken = persResponse?.accessToken || '';
1628
- }
1629
- catch (persError) {
1630
- console.warn(`PERS authentication failed:`, persError);
1631
- // Continue without PERS but provide empty token
1632
- persAccessToken = '';
1516
+ // Step 2: User doesn't exist - register with v1 API
1517
+ // 2a. Initialize registration
1518
+ const initResult = await this.initializeRegistration(persAccessToken);
1519
+ let signedChallenge = null;
1520
+ let tmpAuthToken = null;
1521
+ // 2b. Create WebAuthn credential using the challenge (if available)
1522
+ if (initResult && typeof initResult === 'object') {
1523
+ // Extract challenge and tmpAuthToken from init result with better type safety
1524
+ const initData = initResult;
1525
+ const challenge = initData.challenge;
1526
+ tmpAuthToken = initData.temporaryAuthenticationToken || initData.tmpAuthToken || null;
1527
+ if (challenge && initData.user) {
1528
+ // Construct proper WebAuthn credential creation options
1529
+ const credentialCreationOptions = {
1530
+ challenge: challenge,
1531
+ rp: {
1532
+ name: this.config.relyingPartyName || 'PERS Signer',
1533
+ id: typeof window !== 'undefined' ? window.location.hostname : 'localhost'
1534
+ },
1535
+ user: {
1536
+ id: initData.user.id,
1537
+ name: initData.user.name,
1538
+ displayName: initData.user.displayName
1539
+ },
1540
+ pubKeyCredParams: initData.pubKeyCredParams || [
1541
+ { alg: -7, type: "public-key" },
1542
+ { alg: -257, type: "public-key" }
1543
+ ],
1544
+ authenticatorSelection: initData.authenticatorSelection || {},
1545
+ attestation: initData.attestation || "direct",
1546
+ excludeCredentials: initData.excludeCredentials || []
1547
+ };
1548
+ signedChallenge = await this.webAuthnProvider.create(credentialCreationOptions);
1549
+ }
1550
+ else {
1551
+ console.warn(`[PersSignerSDK] Missing challenge or user data in init result`);
1552
+ }
1553
+ }
1554
+ // 2c. Complete registration with proper challenge data - call AuthenticationService directly
1555
+ const completeResult = await this.completeRegistrationWithChallenge(tmpAuthToken, signedChallenge, persAccessToken);
1556
+ // Extract token from registration result
1557
+ if (completeResult && typeof completeResult === 'object') {
1558
+ // Check if this is a complete JWT response with access_token
1559
+ if ('access_token' in completeResult) {
1560
+ signerToken = completeResult.access_token;
1561
+ }
1562
+ else {
1563
+ // This is a wrapped provider response - registration is not complete
1564
+ // We have user created and wallet created, but need to get JWT token
1565
+ // For now, throw an error indicating we need additional backend integration steps
1566
+ throw new Error('Registration created user and wallet but JWT token not provided. Backend integration incomplete.');
1567
+ }
1568
+ }
1569
+ else {
1570
+ throw new Error('Registration completed but no response received');
1571
+ }
1633
1572
  }
1634
1573
  return {
1635
1574
  identifier,
1636
- signerAuthToken,
1575
+ signerAuthToken: signerToken,
1637
1576
  persAccessToken
1638
1577
  };
1639
1578
  }
1640
1579
  catch (error) {
1641
- throw new Error(`User authentication failed: ${error}`);
1580
+ console.error(`[PersSignerSDK] Combined authentication failed for ${identifier}:`, error);
1581
+ throw new Error(`Combined authentication failed: ${error}`);
1642
1582
  }
1643
1583
  }
1584
+ }
1585
+
1586
+ /**
1587
+ * JWT Utility Functions
1588
+ *
1589
+ * Browser-compatible JWT handling without external dependencies
1590
+ */
1591
+ /**
1592
+ * Extract and validate JWT token payload
1593
+ * Uses a browser-compatible approach without external dependencies
1594
+ */
1595
+ function extractJWTFromToken(jwtToken) {
1596
+ if (!jwtToken) {
1597
+ return { payload: null, isExpired: false };
1598
+ }
1599
+ try {
1600
+ // Use a more compatible JWT decoding approach
1601
+ // Split JWT into parts
1602
+ const parts = jwtToken.split('.');
1603
+ if (parts.length !== 3) {
1604
+ throw new Error('Invalid JWT format');
1605
+ }
1606
+ // Decode the payload (middle part)
1607
+ const payload = JSON.parse(atob(parts[1]));
1608
+ // Check expiration
1609
+ const isExpired = payload.exp ? Date.now() >= payload.exp * 1000 : false;
1610
+ return { payload, isExpired };
1611
+ }
1612
+ catch (error) {
1613
+ console.warn('JWT decode failed:', error);
1614
+ return { payload: null, isExpired: false };
1615
+ }
1616
+ }
1617
+
1618
+ /**
1619
+ * In-memory User Cache
1620
+ *
1621
+ * Provides secure, storage-free user authentication caching
1622
+ * Uses memory-only storage with TTL expiration for security
1623
+ */
1624
+ /**
1625
+ * In-memory user cache with TTL expiration
1626
+ * Security-first approach - no persistent storage
1627
+ */
1628
+ class UserCache {
1644
1629
  /**
1645
- * Complete PERS transaction signing flow - exactly like web project
1646
- * Uses existing TransactionSigningService with injected WebAuthn provider
1630
+ * Get cached user by identifier
1631
+ * @param identifier - User identifier (email/userId)
1632
+ * @returns Cached user or null if not found/expired
1647
1633
  */
1648
- async signPersTransaction(user, transactionId) {
1649
- if (!user.signerAuthToken) {
1650
- throw new Error('Signer authentication token required');
1651
- }
1652
- if (!user.persAccessToken) {
1653
- throw new Error('PERS access token required for transaction signing');
1654
- }
1655
- try {
1656
- // Use the existing high-level service with injected WebAuthn provider
1657
- const result = await this.transactionSigningService.signTransaction({
1658
- transactionId,
1659
- authTokens: {
1660
- backendAuthToken: user.signerAuthToken,
1661
- persAccessToken: user.persAccessToken
1662
- },
1663
- ethersProviderUrl: this.config.ethersProviderUrl || 'https://sepolia.infura.io/v3/2781b4b5242343d5b0954c98f287b29e'
1664
- });
1665
- return {
1666
- success: result.success,
1667
- transactionHash: result.transactionHash,
1668
- error: result.error
1669
- };
1634
+ static get(identifier) {
1635
+ const cached = this.cache.get(identifier);
1636
+ if (!cached) {
1637
+ return null;
1670
1638
  }
1671
- catch (error) {
1672
- return {
1673
- success: false,
1674
- error: `Transaction signing failed: ${error}`
1675
- };
1639
+ // Check if expired
1640
+ if (Date.now() > cached.expiresAt) {
1641
+ this.cache.delete(identifier);
1642
+ return null;
1676
1643
  }
1644
+ return cached.user;
1677
1645
  }
1678
- // ========================================================================
1679
- // GRANULAR METHODS (For Custom Integrations)
1680
- // ========================================================================
1681
1646
  /**
1682
- * Register new user - uses existing AuthenticationService
1647
+ * Cache user with TTL
1648
+ * @param identifier - User identifier
1649
+ * @param user - Authenticated user data
1650
+ * @param ttlMs - Time to live in milliseconds (default: 5 minutes)
1683
1651
  */
1684
- async registerUser(identifier) {
1685
- try {
1686
- await this.authService.register(identifier);
1687
- const authToken = await this.authService.login(identifier);
1688
- return { authToken };
1689
- }
1690
- catch (error) {
1691
- throw new Error(`User registration failed: ${error}`);
1692
- }
1652
+ static set(identifier, user, ttlMs = this.DEFAULT_TTL_MS) {
1653
+ this.cache.set(identifier, {
1654
+ user,
1655
+ expiresAt: Date.now() + ttlMs
1656
+ });
1693
1657
  }
1694
1658
  /**
1695
- * Login existing user - uses existing AuthenticationService
1659
+ * Remove user from cache
1660
+ * @param identifier - User identifier
1696
1661
  */
1697
- async loginUser(identifier) {
1698
- try {
1699
- return await this.authService.login(identifier);
1700
- }
1701
- catch (error) {
1702
- throw new Error(`User login failed: ${error}`);
1703
- }
1662
+ static delete(identifier) {
1663
+ return this.cache.delete(identifier);
1704
1664
  }
1705
1665
  /**
1706
- * Add wallet to PERS user - uses existing PersService
1666
+ * Clear all cached users
1707
1667
  */
1708
- async addWalletToPersUser(signerAuthToken) {
1709
- try {
1710
- const persResponse = await PersService.authenticateUser(signerAuthToken);
1711
- if (!persResponse?.accessToken) {
1712
- throw new Error('Failed to get PERS access token');
1713
- }
1714
- return persResponse.accessToken;
1715
- }
1716
- catch (error) {
1717
- throw new Error(`Failed to add wallet to PERS user: ${error}`);
1718
- }
1668
+ static clear() {
1669
+ this.cache.clear();
1719
1670
  }
1720
1671
  /**
1721
- * Retrieve transaction data from PERS - uses existing PersService
1672
+ * Get cache size (for debugging)
1722
1673
  */
1723
- async retrieveTransactionData(transactionId, persAccessToken) {
1724
- try {
1725
- return await PersService.fetchPreparedTransaction(transactionId, persAccessToken);
1726
- }
1727
- catch (error) {
1728
- // Preserve original error structure for proper error handling
1729
- throw error;
1730
- }
1674
+ static size() {
1675
+ return this.cache.size;
1731
1676
  }
1732
1677
  /**
1733
- * Sign transaction data - uses existing SigningService
1734
- * Follows same patterns as KeyWallet.signPersTransaction
1678
+ * Clean up expired entries
1735
1679
  */
1736
- async signTransactionData(signerAuthToken, signingData) {
1737
- try {
1738
- const wallets = await WalletService.listWallets(signerAuthToken);
1739
- if (!wallets.items || wallets.items.length === 0) {
1740
- throw new Error('No wallet found for user');
1741
- }
1742
- const walletId = wallets.items[0].id;
1743
- // Sign based on transaction format - same pattern as KeyWallet
1744
- switch (signingData.format) {
1745
- case browser.TRANSACTION_FORMATS.EIP_712: {
1746
- const eip712Data = signingData;
1747
- const typedData = eip712Data.typedData;
1748
- // Sign using same pattern as KeyWallet
1749
- const signResponse = await this.signingService.signTypedData(signerAuthToken, walletId, typedData.domain, { Transaction: typedData.types.Transaction }, typedData.message);
1750
- return `${signResponse.signature?.r}${signResponse.signature?.s}`;
1751
- }
1752
- default: {
1753
- // For legacy and other formats, use raw signing data
1754
- const signResponse = await this.signingService.signHash(signerAuthToken, walletId, JSON.stringify(signingData));
1755
- return `${signResponse.signature?.r}${signResponse.signature?.s}`;
1756
- }
1680
+ static cleanup() {
1681
+ const now = Date.now();
1682
+ let cleaned = 0;
1683
+ for (const [identifier, cached] of this.cache.entries()) {
1684
+ if (now > cached.expiresAt) {
1685
+ this.cache.delete(identifier);
1686
+ cleaned++;
1757
1687
  }
1758
1688
  }
1759
- catch (error) {
1760
- throw new Error(`Transaction signing failed: ${error}`);
1761
- }
1689
+ return cleaned;
1762
1690
  }
1691
+ }
1692
+ UserCache.cache = new Map();
1693
+ UserCache.DEFAULT_TTL_MS = 300000; // 5 minutes
1694
+
1695
+ /**
1696
+ * PERS Blockchain Signer SDK
1697
+ *
1698
+ * A lightweight blockchain transaction signing SDK with WebAuthn authentication.
1699
+ * Provides 5 focused methods for complete transaction lifecycle management:
1700
+ *
1701
+ * 1. loginUser(jwtToken) - Authenticate user with 5-minute caching
1702
+ * 2. signTransaction(signingData, jwtToken) - Sign transactions with auto-login
1703
+ * 3. submitTransaction(signedTx, jwtToken) - Submit signed transactions to blockchain
1704
+ * 4. signPersTransaction(jwtToken) - Legacy one-liner for backward compatibility
1705
+ * 5. signAndSubmitPersTransaction(jwtToken) - Complete sign + submit flow
1706
+ *
1707
+ * @example
1708
+ * ```typescript
1709
+ * import { createPersSignerSDK } from '@explorins/pers-signer';
1710
+ *
1711
+ * const sdk = createPersSignerSDK({
1712
+ * webAuthnProvider: myWebAuthnProvider,
1713
+ * ethersProviderUrl: 'https://ethereum-rpc.com'
1714
+ * });
1715
+ *
1716
+ * // Quick sign and submit
1717
+ * const result = await sdk.signAndSubmitPersTransaction(jwtToken);
1718
+ * if (result.success) {
1719
+ * console.log('Transaction submitted:', result.transactionHash);
1720
+ * }
1721
+ * ```
1722
+ */
1723
+ /**
1724
+ * PERS Blockchain Signer SDK Class
1725
+ *
1726
+ * Main SDK class providing blockchain transaction signing capabilities with WebAuthn authentication.
1727
+ * Implements a clean 5-method API for complete transaction lifecycle management.
1728
+ *
1729
+ * Features:
1730
+ * - WebAuthn-based secure authentication
1731
+ * - 5-minute user session caching
1732
+ * - Automatic transaction data fetching
1733
+ * - Blockchain transaction signing and submission
1734
+ * - Multi-tenant support
1735
+ *
1736
+ * @class PersSignerSDK
1737
+ */
1738
+ class PersSignerSDK {
1763
1739
  /**
1764
- * Submit signed transaction to PERS - uses existing PersService
1740
+ * Initialize the PERS Signer SDK
1741
+ *
1742
+ * @param {PersSignerConfig} config - SDK configuration object
1743
+ * @throws {Error} If required configuration is missing
1765
1744
  */
1766
- async submitTransaction(transactionId, signature, persAccessToken, transactionFormat = browser.TRANSACTION_FORMATS.EIP_712) {
1767
- try {
1768
- const submitResult = await PersService.submitTransaction(transactionId, signature, persAccessToken, transactionFormat);
1769
- return {
1770
- transactionHash: submitResult.transaction?.transactionHash || undefined,
1771
- success: true
1772
- };
1773
- }
1774
- catch (error) {
1775
- throw new Error(`Transaction submission failed: ${error}`);
1776
- }
1745
+ constructor(config) {
1746
+ this.config = config;
1747
+ setConfigProvider(new WebConfigProvider({
1748
+ apiUrl: config.apiUrl || SIGNER_CONFIG.DEFAULT_SIGNER_API_URL,
1749
+ relyingParty: {
1750
+ id: typeof window !== 'undefined' ? window.location.hostname : 'localhost',
1751
+ name: config.relyingPartyName || SIGNER_CONFIG.DEFAULT_RELYING_PARTY_NAME,
1752
+ origin: typeof window !== 'undefined' ? window.location.origin : undefined,
1753
+ },
1754
+ }));
1755
+ this.authenticationService = new AuthenticationService(this.config);
1756
+ this.transactionSigningService = new TransactionSigningService(config);
1777
1757
  }
1778
1758
  /**
1779
- * Check if user exists - uses existing AuthenticationService
1759
+ * Authenticate user and cache session for 5 minutes
1760
+ *
1761
+ * Validates JWT token, authenticates with both signer and PERS backends,
1762
+ * and caches the authenticated user session to avoid repeated authentication.
1763
+ *
1764
+ * @param {string} jwtToken - JWT token containing user identifier and tenant info
1765
+ * @returns {Promise<AuthenticatedUser>} Authenticated user with access tokens
1766
+ * @throws {Error} If JWT is invalid, expired, or authentication fails
1767
+ *
1768
+ * @example
1769
+ * ```typescript
1770
+ * try {
1771
+ * const user = await sdk.loginUser(jwtToken);
1772
+ * console.log('Authenticated:', user.identifier);
1773
+ * } catch (error) {
1774
+ * console.error('Authentication failed:', error.message);
1775
+ * }
1776
+ * ```
1780
1777
  */
1781
- async checkUserExists(identifier) {
1782
- try {
1783
- await this.authService.login(identifier);
1784
- return true;
1778
+ async loginUser(jwtToken) {
1779
+ if (!jwtToken || typeof jwtToken !== 'string') {
1780
+ throw new Error('JWT token is required and must be a string');
1785
1781
  }
1786
- catch {
1787
- return false;
1782
+ const { payload, isExpired } = extractJWTFromToken(jwtToken);
1783
+ if (!payload || isExpired) {
1784
+ throw new Error('Invalid or expired JWT token');
1788
1785
  }
1789
- }
1790
- /**
1791
- * Get user wallets - uses existing WalletService
1792
- */
1793
- async getUserWallets(signerAuthToken) {
1794
- try {
1795
- return await WalletService.listWallets(signerAuthToken);
1786
+ const identifier = payload.identifierEmail || payload.email || payload.userId;
1787
+ const tenantId = payload.tenantId || this.config.tenantId || '';
1788
+ if (!identifier) {
1789
+ throw new Error('JWT token missing user identifier (identifierEmail, email, or userId)');
1796
1790
  }
1797
- catch (error) {
1798
- throw new Error(`Failed to get user wallets: ${error}`);
1791
+ // Check cache first
1792
+ const cachedUser = UserCache.get(identifier);
1793
+ if (cachedUser && cachedUser.tenantId === tenantId && Date.now() < cachedUser.expiresAt) {
1794
+ return cachedUser;
1799
1795
  }
1800
- }
1801
- /**
1802
- * Get transaction status - uses existing PersService
1803
- */
1804
- async getTransactionStatus(transactionId, persAccessToken) {
1805
1796
  try {
1806
- const transaction = await PersService.fetchPreparedTransaction(transactionId, persAccessToken);
1807
- return {
1808
- status: transaction.transactionStatus,
1809
- transactionHash: transaction.transaction?.transactionHash || undefined
1797
+ // Authenticate and cache
1798
+ const authResult = await this.authenticationService.combinedAuthentication(identifier, jwtToken);
1799
+ const user = {
1800
+ identifier: authResult.identifier,
1801
+ signerAuthToken: authResult.signerAuthToken,
1802
+ persAccessToken: authResult.persAccessToken,
1803
+ tenantId,
1804
+ expiresAt: Date.now() + 300000 // 5 minutes
1810
1805
  };
1806
+ UserCache.set(identifier, user);
1807
+ return user;
1811
1808
  }
1812
1809
  catch (error) {
1813
- throw new Error(`Failed to get transaction status: ${error}`);
1810
+ throw new Error(`Authentication failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
1814
1811
  }
1815
1812
  }
1816
1813
  /**
1817
- * Initialize tenant - uses existing PersService
1818
- */
1819
- async initializeTenant(tenantId) {
1820
- await PersService.initializeTenant(tenantId);
1821
- }
1822
- // ========================================================================
1823
- // JWT AUTHENTICATION METHODS (For Legacy PERS Flow)
1824
- // ========================================================================
1825
- /**
1826
- * Extract and validate JWT token from URL search parameters
1827
- * Uses a simpler approach compatible with browser environments
1814
+ * Sign a PERS transaction (legacy compatibility method)
1815
+ *
1816
+ * Automatically handles user authentication, transaction data fetching,
1817
+ * and transaction signing in a single call. This is the legacy method
1818
+ * maintained for backward compatibility.
1819
+ *
1820
+ * @param {string} jwtToken - JWT token containing transaction ID and user info
1821
+ * @returns {Promise<TransactionSigningResult>} Signing result with signature and metadata
1822
+ * @throws {Error} If authentication fails, transaction ID missing, or signing fails
1823
+ *
1824
+ * @example
1825
+ * ```typescript
1826
+ * try {
1827
+ * const result = await sdk.signPersTransaction(jwtToken);
1828
+ * if (result.success) {
1829
+ * console.log('Transaction signed:', result.signature);
1830
+ * }
1831
+ * } catch (error) {
1832
+ * console.error('Signing failed:', error.message);
1833
+ * }
1834
+ * ```
1828
1835
  */
1829
- extractJWTFromURL(searchParams) {
1830
- const jwtToken = searchParams.get('jwt') || searchParams.get('token');
1831
- if (!jwtToken) {
1832
- return { payload: null, isExpired: false };
1833
- }
1836
+ async signPersTransaction(jwtToken) {
1837
+ if (!jwtToken || typeof jwtToken !== 'string') {
1838
+ throw new Error('JWT token is required and must be a string');
1839
+ }
1840
+ const user = await this.loginUser(jwtToken);
1841
+ const { payload } = extractJWTFromToken(jwtToken);
1842
+ if (!payload?.transactionId) {
1843
+ throw new Error('JWT token missing transactionId in payload');
1844
+ }
1845
+ const authTokens = {
1846
+ signerAuthToken: user.signerAuthToken,
1847
+ persAccessToken: user.persAccessToken
1848
+ };
1834
1849
  try {
1835
- // Use a more compatible JWT decoding approach
1836
- // Split JWT into parts
1837
- const parts = jwtToken.split('.');
1838
- if (parts.length !== 3) {
1839
- throw new Error('Invalid JWT format');
1850
+ const persSigningData = await this.transactionSigningService.getPersSigningData({
1851
+ transactionId: payload.transactionId,
1852
+ authTokens,
1853
+ tenantId: user.tenantId
1854
+ });
1855
+ const result = await this.signTransaction(persSigningData, jwtToken);
1856
+ if (!result.success) {
1857
+ throw new Error(result.error || 'Transaction signing failed');
1840
1858
  }
1841
- // Decode the payload (middle part)
1842
- const payload = JSON.parse(atob(parts[1]));
1843
- // Check expiration
1844
- const isExpired = payload.exp ? Date.now() >= payload.exp * 1000 : false;
1845
- return { payload, isExpired };
1859
+ return result;
1846
1860
  }
1847
1861
  catch (error) {
1848
- console.warn('JWT decode failed:', error);
1849
- return { payload: null, isExpired: false };
1862
+ throw new Error(`PERS transaction signing failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
1850
1863
  }
1851
1864
  }
1852
1865
  /**
1853
- * Initialize tenant from JWT payload using existing PersService
1866
+ * Sign a transaction with provided signing data
1867
+ *
1868
+ * Low-level method to sign transactions when you already have the signing data.
1869
+ * Automatically handles user authentication and applies the blockchain signature.
1870
+ *
1871
+ * @param {CounterfactualWalletTransactionResponse | LegacyTransaction} signingData - Transaction data to sign
1872
+ * @param {string} jwtToken - JWT token containing transaction ID and user info
1873
+ * @returns {Promise<TransactionSigningResult>} Signing result with signature and metadata
1874
+ * @throws {Error} If authentication fails, transaction ID missing, or signing fails
1875
+ *
1876
+ * @example
1877
+ * ```typescript
1878
+ * const signingData = await getTransactionData(transactionId);
1879
+ * const result = await sdk.signTransaction(signingData, jwtToken);
1880
+ * console.log('Signed transaction:', result.signature);
1881
+ * ```
1854
1882
  */
1855
- async initializeTenantFromJWT(payload) {
1856
- if (!payload.tenantId) {
1857
- return 'default';
1883
+ async signTransaction(signingData, jwtToken) {
1884
+ if (!signingData) {
1885
+ throw new Error('Signing data is required');
1886
+ }
1887
+ if (!jwtToken || typeof jwtToken !== 'string') {
1888
+ throw new Error('JWT token is required and must be a string');
1858
1889
  }
1890
+ const user = await this.loginUser(jwtToken);
1891
+ const { payload } = extractJWTFromToken(jwtToken);
1892
+ if (!payload?.transactionId) {
1893
+ throw new Error('JWT token missing transactionId in payload');
1894
+ }
1895
+ const authTokens = {
1896
+ signerAuthToken: user.signerAuthToken,
1897
+ persAccessToken: user.persAccessToken
1898
+ };
1859
1899
  try {
1860
- // Use existing PersService.initializeTenant method
1861
- const tenantData = await PersService.initializeTenant(payload.tenantId);
1862
- // Extract project key from tenant data
1863
- const projectKey = tenantData.projectKey ||
1864
- tenantData.projectApiKey ||
1865
- tenantData.apiKey ||
1866
- payload.tenantId;
1867
- return projectKey;
1900
+ const result = await this.transactionSigningService.signTransaction({
1901
+ transactionId: payload.transactionId,
1902
+ tenantId: user.tenantId,
1903
+ authTokens,
1904
+ ethersProviderUrl: this.config.ethersProviderUrl || ''
1905
+ }, signingData);
1906
+ return result;
1868
1907
  }
1869
1908
  catch (error) {
1870
- console.warn('Tenant initialization failed:', error);
1871
- return payload.tenantId;
1909
+ throw new Error(`Transaction signing failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
1872
1910
  }
1873
1911
  }
1874
1912
  /**
1875
- * Authenticate user with PERS API using existing PersService pattern
1876
- */
1877
- /**
1878
- * Authenticate user with PERS API using existing PersService
1913
+ * Complete transaction flow: sign and submit in one call
1914
+ *
1915
+ * Convenience method that combines signing and submission into a single operation.
1916
+ * This is the recommended method for most use cases as it handles the complete
1917
+ * transaction lifecycle automatically.
1918
+ *
1919
+ * @param {string} jwtToken - JWT token containing transaction ID and user info
1920
+ * @returns {Promise<SubmissionResult>} Submission result with transaction hash and status
1921
+ * @throws {Error} If authentication, signing, or submission fails
1922
+ *
1923
+ * @example
1924
+ * ```typescript
1925
+ * try {
1926
+ * const result = await sdk.signAndSubmitPersTransaction(jwtToken);
1927
+ * if (result.success) {
1928
+ * console.log('Transaction completed:', result.transactionHash);
1929
+ * if (result.shouldRedirect) {
1930
+ * window.location.href = result.redirectUrl;
1931
+ * }
1932
+ * }
1933
+ * } catch (error) {
1934
+ * console.error('Transaction failed:', error.message);
1935
+ * }
1936
+ * ```
1879
1937
  */
1880
- async authenticatePersUser(jwtToken, projectKey) {
1938
+ async signAndSubmitPersTransaction(jwtToken) {
1939
+ if (!jwtToken || typeof jwtToken !== 'string') {
1940
+ throw new Error('JWT token is required and must be a string');
1941
+ }
1881
1942
  try {
1882
- // Configure PersService with the project key from tenant initialization
1883
- PersService.configure({ projectKey });
1884
- // Use the existing PersService method which handles all the complexity
1885
- const result = await PersService.authenticateUser(jwtToken);
1886
- // Convert to expected PersAuthResult format
1887
- return {
1888
- user: {
1889
- email: result.user?.email || undefined,
1890
- id: result.user?.id || undefined
1891
- },
1892
- accessToken: result.accessToken
1893
- };
1943
+ const signedTx = await this.signPersTransaction(jwtToken);
1944
+ const submittedTx = await this.submitTransaction(signedTx, jwtToken);
1945
+ return submittedTx;
1894
1946
  }
1895
1947
  catch (error) {
1896
- throw new Error(`PERS authentication failed: ${error}`);
1948
+ throw new Error(`Sign and submit transaction failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
1897
1949
  }
1898
1950
  }
1899
1951
  /**
1900
- * Combined PERS + DFNS authentication flow
1952
+ * Submit a signed transaction to the blockchain
1953
+ *
1954
+ * Takes a signed transaction result and submits it to the blockchain network.
1955
+ * Returns detailed submission results including transaction hash and any
1956
+ * redirect information for UI flows.
1957
+ *
1958
+ * @param {TransactionSigningResult} signingResult - Result from a successful transaction signing
1959
+ * @param {string} jwtToken - JWT token containing tenant and user info
1960
+ * @returns {Promise<SubmissionResult>} Submission result with transaction hash and status
1961
+ * @throws {Error} If signing result is invalid, JWT is missing tenantId, or submission fails
1962
+ *
1963
+ * @example
1964
+ * ```typescript
1965
+ * const signedTx = await sdk.signPersTransaction(jwtToken);
1966
+ * const result = await sdk.submitTransaction(signedTx, jwtToken);
1967
+ * console.log('Transaction submitted:', result.transactionHash);
1968
+ * ```
1901
1969
  */
1902
- async combinedAuthentication(identifier, persAccessToken) {
1970
+ async submitTransaction(signingResult, jwtToken) {
1971
+ if (!signingResult) {
1972
+ throw new Error('Signing result is required');
1973
+ }
1974
+ if (!jwtToken || typeof jwtToken !== 'string') {
1975
+ throw new Error('JWT token is required and must be a string');
1976
+ }
1977
+ if (!signingResult.success || !signingResult.signature || !signingResult.signingData) {
1978
+ throw new Error('Invalid signing result: must be successful with signature and signing data');
1979
+ }
1980
+ const { payload } = extractJWTFromToken(jwtToken);
1981
+ if (!payload?.tenantId) {
1982
+ throw new Error('JWT token missing tenantId in payload');
1983
+ }
1984
+ const user = await this.loginUser(jwtToken);
1903
1985
  try {
1904
- // Authenticate with DFNS only (we already have PERS token)
1905
- const signerAuthToken = await this.loginUser(identifier);
1986
+ const submitResult = await PersService.submitTransaction(signingResult.transactionId, signingResult.signature, user.persAccessToken, signingResult.signingData.format, payload.tenantId);
1987
+ // Transform response to SubmissionResult
1906
1988
  return {
1907
- identifier,
1908
- signerAuthToken,
1909
- persAccessToken
1989
+ success: true,
1990
+ transactionHash: submitResult.transaction.transactionHash,
1991
+ shouldRedirect: false, // Can be configured based on business requirements
1992
+ redirectUrl: undefined, // Can be set based on business logic
1993
+ error: undefined,
1994
+ submitResult: submitResult
1910
1995
  };
1911
1996
  }
1912
1997
  catch (error) {
1913
- throw new Error(`Combined authentication failed: ${error}`);
1998
+ throw new Error(`Transaction submission failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
1914
1999
  }
1915
2000
  }
1916
2001
  /**
1917
- * Complete JWT-based authentication flow (legacy PERS flow)
2002
+ * Clear user authentication cache
2003
+ *
2004
+ * Removes all cached user sessions, forcing fresh authentication
2005
+ * on the next method call. Useful for logout scenarios or when
2006
+ * switching between different user contexts.
2007
+ *
2008
+ * @example
2009
+ * ```typescript
2010
+ * // Clear cache on user logout
2011
+ * sdk.clearCache();
2012
+ * console.log('User cache cleared');
2013
+ * ```
1918
2014
  */
1919
- async authenticateWithJWT(searchParams) {
1920
- // 1. Extract JWT from URL
1921
- const { payload, isExpired } = this.extractJWTFromURL(searchParams);
1922
- if (!payload) {
1923
- // Return null instead of throwing error to allow graceful handling
1924
- return null;
1925
- }
1926
- if (isExpired) {
1927
- return {
1928
- user: {
1929
- identifier: payload.email || payload.userId || 'unknown',
1930
- signerAuthToken: '',
1931
- persAccessToken: ''
1932
- },
1933
- isExpired: true
1934
- };
1935
- }
1936
- // 2. Initialize tenant and get project key
1937
- const projectKey = await this.initializeTenantFromJWT(payload);
1938
- // 3. Get JWT token from URL
1939
- const jwtToken = searchParams.get('jwt') || searchParams.get('token');
1940
- if (!jwtToken) {
1941
- throw new Error('JWT token not found in URL parameters');
1942
- }
1943
- // 4. Authenticate with PERS
1944
- const persResult = await this.authenticatePersUser(jwtToken, projectKey);
1945
- // 5. Get identifier for DFNS authentication
1946
- const identifier = persResult.user.email || persResult.user.id;
1947
- if (!identifier) {
1948
- throw new Error('No identifier found in PERS response');
1949
- }
1950
- // 6. Combined authentication
1951
- const user = await this.combinedAuthentication(identifier, persResult.accessToken);
1952
- return {
1953
- user,
1954
- isExpired: false
1955
- };
1956
- }
1957
- }
1958
-
1959
- /**
1960
- * Utility functions for handling URL search parameters in a consistent way across applications
1961
- */
1962
- /**
1963
- * Creates a URL search string that preserves all current parameters
1964
- * @param paramsToExclude Array of parameter names to exclude from the result
1965
- * @param paramsToAdd Object with additional parameters to add or override
1966
- * @param baseParams Optional URLSearchParams object to use instead of window.location.search
1967
- * @returns A search string with '?' prefix if there are parameters, or empty string if no parameters
1968
- */
1969
- function createSearchString(paramsToExclude = [], paramsToAdd = {}, baseParams) {
1970
- // Get base search params from the provided value or window location
1971
- const currentParams = new URLSearchParams(baseParams instanceof URLSearchParams
1972
- ? baseParams
1973
- : baseParams !== undefined
1974
- ? baseParams
1975
- : (typeof window !== 'undefined' ? window.location.search : ''));
1976
- // Remove excluded parameters
1977
- paramsToExclude.forEach(param => {
1978
- if (currentParams.has(param)) {
1979
- currentParams.delete(param);
1980
- }
1981
- });
1982
- // Add or update new parameters
1983
- Object.entries(paramsToAdd).forEach(([key, value]) => {
1984
- if (value !== undefined && value !== null) {
1985
- currentParams.set(key, value);
1986
- }
1987
- });
1988
- const searchString = currentParams.toString();
1989
- return searchString ? `?${searchString}` : '';
1990
- }
1991
- /**
1992
- * Creates a URL path with search parameters
1993
- * @param basePath The base path without search parameters
1994
- * @param paramsToExclude Array of parameter names to exclude from the result
1995
- * @param paramsToAdd Object with additional parameters to add or override
1996
- * @param baseParams Optional URLSearchParams object to use instead of window.location.search
1997
- * @returns A full path with search parameters
1998
- */
1999
- function createUrlWithSearchParams(basePath, paramsToExclude = [], paramsToAdd = {}, baseParams) {
2000
- const searchString = createSearchString(paramsToExclude, paramsToAdd, baseParams);
2001
- return `${basePath}${searchString}`;
2002
- }
2003
- /**
2004
- * Combines the current search parameters with new ones
2005
- * @param searchParams The current URLSearchParams object
2006
- * @param additionalParams Object with parameters to add or update
2007
- * @returns A new URLSearchParams object
2008
- */
2009
- function mergeSearchParams(searchParams, additionalParams = {}) {
2010
- const merged = new URLSearchParams(searchParams.toString());
2011
- Object.entries(additionalParams).forEach(([key, value]) => {
2012
- if (value !== undefined && value !== null) {
2013
- merged.set(key, value);
2014
- }
2015
- });
2016
- return merged;
2017
- }
2018
- /**
2019
- * Gets a specific search parameter value
2020
- * @param key The parameter name to get
2021
- * @param search Optional search string to parse (defaults to window.location.search)
2022
- * @returns The parameter value or null if not found
2023
- */
2024
- function getSearchParam(key, search) {
2025
- const params = new URLSearchParams(search || (typeof window !== 'undefined' ? window.location.search : ''));
2026
- return params.get(key);
2027
- }
2028
- /**
2029
- * Gets all search parameters as a URLSearchParams object
2030
- * @param search Optional search string to parse (defaults to window.location.search)
2031
- * @returns A URLSearchParams object
2032
- */
2033
- function getAllSearchParams(search) {
2034
- return new URLSearchParams(search || (typeof window !== 'undefined' ? window.location.search : ''));
2035
- }
2036
- /**
2037
- * Removes a specific search parameter
2038
- * @param key The parameter name to remove
2039
- * @param search Optional search string to parse (defaults to window.location.search)
2040
- * @returns A new search string without the specified parameter
2041
- */
2042
- function removeSearchParam(key, search) {
2043
- const params = new URLSearchParams(search || (typeof window !== 'undefined' ? window.location.search : ''));
2044
- params.delete(key);
2045
- const searchString = params.toString();
2046
- return searchString ? `?${searchString}` : '';
2047
- }
2048
- /**
2049
- * Updates or adds a specific search parameter
2050
- * @param key The parameter name to update
2051
- * @param value The new value for the parameter
2052
- * @param search Optional search string to parse (defaults to window.location.search)
2053
- * @returns A new search string with the updated parameter
2054
- */
2055
- function updateSearchParam(key, value, search) {
2056
- const params = new URLSearchParams(search || (typeof window !== 'undefined' ? window.location.search : ''));
2057
- params.set(key, value);
2058
- const searchString = params.toString();
2059
- return searchString ? `?${searchString}` : '';
2060
- }
2061
-
2062
- var searchParams = /*#__PURE__*/Object.freeze({
2063
- __proto__: null,
2064
- createSearchString: createSearchString,
2065
- createUrlWithSearchParams: createUrlWithSearchParams,
2066
- getAllSearchParams: getAllSearchParams,
2067
- getSearchParam: getSearchParam,
2068
- mergeSearchParams: mergeSearchParams,
2069
- removeSearchParam: removeSearchParam,
2070
- updateSearchParam: updateSearchParam
2071
- });
2072
-
2073
- /**
2074
- * Utility for debugging search parameter handling
2075
- */
2076
- /**
2077
- * Logs the current search parameters with a custom message
2078
- * This is useful for debugging search parameter preservation across navigation
2079
- *
2080
- * @param message - A descriptive message to identify where the logging happens
2081
- * @param additionalData - Optional additional data to log
2082
- */
2083
- function logSearchParams(message, additionalData) {
2084
- if (process.env.NODE_ENV !== 'production') {
2085
- // Check if window exists (for SSR compatibility)
2086
- if (typeof window === 'undefined') {
2087
- return;
2088
- }
2089
- const searchParams = new URLSearchParams(window.location.search);
2090
- searchParams.forEach((value, key) => {
2091
- });
2092
- console.group(`🔍 Search Params: ${message}`);
2093
- console.groupEnd();
2015
+ clearCache() {
2016
+ UserCache.clear();
2094
2017
  }
2095
2018
  }
2096
- /**
2097
- * Creates a search parameter tracking hook that can be used in React components
2098
- * Logs search parameter changes when the component mounts/updates
2099
- *
2100
- * @param componentName - The name of the component (for logging)
2101
- */
2102
- function createSearchParamLogger(componentName) {
2103
- return () => {
2104
- logSearchParams(`${componentName} rendered`);
2105
- };
2106
- }
2107
-
2108
- /**
2109
- * Generates a RFC4122 version 4 compliant UUID
2110
- * @returns A randomly generated UUID
2111
- */
2112
- function generateUUID() {
2113
- return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
2114
- const r = Math.random() * 16 | 0;
2115
- const v = c === 'x' ? r : (r & 0x3 | 0x8);
2116
- return v.toString(16);
2117
- });
2118
- }
2119
- /**
2120
- * Create a semi-anonymous username for users
2121
- * @returns A username in the format "user_[random string]"
2122
- */
2123
- function generateAnonymousUsername() {
2124
- const randomPart = Math.random().toString(36).substring(2, 10);
2125
- return `user_${randomPart}`;
2126
- }
2127
- /**
2128
- * Create a guest username for the application with a random prefix
2129
- * @returns A guest email in the format "randomstring_guest@explorins.com"
2130
- */
2131
- function generateGuestUsername() {
2132
- const randomPart = Math.random().toString(36).substring(2, 8); // 6 character random string
2133
- return `${randomPart}_guest@explorins.com`;
2134
- }
2135
-
2136
- /**
2137
- * Utility functions for tenant-specific operations
2138
- */
2139
- /**
2140
- * Initialize tenant-specific analytics
2141
- * @param tenantId The tenant ID
2142
- * @param getTenantConfig Function to retrieve tenant configuration
2143
- */
2144
- const initTenantAnalytics = (tenantId, getTenantConfig) => {
2145
- const tenant = getTenantConfig(tenantId);
2146
- if (!tenant) {
2147
- console.warn(`Tenant ${tenantId} not found`);
2148
- return;
2149
- }
2150
- if (tenant.analytics && tenant.analytics.enabled && tenant.analytics.trackingId) ;
2151
- };
2152
2019
 
2153
2020
  /**
2154
2021
  * React Native-specific WebAuthn provider using DFNS React Native SDK
@@ -2222,20 +2089,6 @@ async function getReactNativeWebAuthnProvider() {
2222
2089
  * (e.g., Expo projects that serve both mobile and web)
2223
2090
  */
2224
2091
  // Re-export all shared functionality
2225
- /**
2226
- * Create a new AuthenticationService instance with React Native WebAuthn provider
2227
- */
2228
- async function createAuthenticationService() {
2229
- const webAuthnProvider = await getReactNativeWebAuthnProvider();
2230
- return new AuthenticationService(webAuthnProvider);
2231
- }
2232
- /**
2233
- * Create a new SigningService instance with React Native WebAuthn provider
2234
- */
2235
- async function createSigningService() {
2236
- const webAuthnProvider = await getReactNativeWebAuthnProvider();
2237
- return new SigningService(webAuthnProvider);
2238
- }
2239
2092
  /**
2240
2093
  * Create a new PersSignerSDK instance with React Native WebAuthn provider
2241
2094
  * @param config - SDK configuration
@@ -11809,26 +11662,11 @@ exports.TransactionValidator = TransactionValidator;
11809
11662
  exports.WalletService = WalletService;
11810
11663
  exports.WebAuthnCoordinator = WebAuthnCoordinator;
11811
11664
  exports.WebConfigProvider = WebConfigProvider;
11812
- exports.createAuthenticationService = createAuthenticationService;
11813
11665
  exports.createPersSignerSDK = createPersSignerSDK;
11814
- exports.createSearchParamLogger = createSearchParamLogger;
11815
- exports.createSearchString = createSearchString;
11816
- exports.createSigningService = createSigningService;
11817
- exports.createUrlWithSearchParams = createUrlWithSearchParams;
11818
- exports.generateAnonymousUsername = generateAnonymousUsername;
11819
- exports.generateGuestUsername = generateGuestUsername;
11820
- exports.generateUUID = generateUUID;
11821
- exports.getAllSearchParams = getAllSearchParams;
11822
11666
  exports.getConfigProvider = getConfigProvider;
11823
11667
  exports.getHttpClient = getHttpClient;
11824
- exports.getSearchParam = getSearchParam;
11825
11668
  exports.getServiceConfig = getServiceConfig;
11826
11669
  exports.getWebAuthnProvider = getReactNativeWebAuthnProvider;
11827
- exports.initTenantAnalytics = initTenantAnalytics;
11828
- exports.logSearchParams = logSearchParams;
11829
- exports.mergeSearchParams = mergeSearchParams;
11830
- exports.removeSearchParam = removeSearchParam;
11831
11670
  exports.setConfigProvider = setConfigProvider;
11832
11671
  exports.setHttpClient = setHttpClient;
11833
- exports.updateSearchParam = updateSearchParam;
11834
11672
  //# sourceMappingURL=react-native.cjs.js.map