@explorins/pers-signer 1.0.26 → 1.0.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.esm.js CHANGED
@@ -67,13 +67,11 @@ const SIGNER_CONFIG = {
67
67
  * Used as fallback when production signer API is unavailable
68
68
  */
69
69
  STAGING_SIGNER_API_URL: 'https://signer-api-staging.pers.ninja/v1',
70
- // STAGING_SIGNER_API_URL: 'http://localhost:8080/v1',
71
70
  /**
72
71
  * PERS Platform API URLs
73
72
  */
74
73
  DEFAULT_PERS_API_URL: 'https://api.pers.ninja/v2',
75
- // STAGING_PERS_API_URL: 'https://dev.api.pers.ninja/v2',
76
- STAGING_PERS_API_URL: 'https://explorins-loyalty.ngrok.io',
74
+ STAGING_PERS_API_URL: 'https://dev.api.pers.ninja/v2',
77
75
  /**
78
76
  * Default relying party name for WebAuthn operations
79
77
  * This appears in the browser's authentication prompts
@@ -85,8 +83,31 @@ const SIGNER_CONFIG = {
85
83
  */
86
84
  // Default fallback provider for Sepolia testnet (no API key required)
87
85
  const DEFAULT_FALLBACK_PROVIDER = 'https://ethereum-sepolia.publicnode.com';
86
+ /**
87
+ * WebAuthn configuration constants
88
+ */
89
+ const WEBAUTHN_CONFIG = {
90
+ /**
91
+ * Timeout for WebAuthn operations (in milliseconds)
92
+ */
93
+ TIMEOUT: 60000,
94
+ /**
95
+ * User verification requirement
96
+ * 'preferred' - ask for verification if available
97
+ * 'required' - require verification
98
+ * 'discouraged' - don't ask for verification
99
+ */
100
+ USER_VERIFICATION: 'preferred',
101
+ /**
102
+ * Resident key requirement (Passkeys)
103
+ */
104
+ RESIDENT_KEY: 'required',
105
+ /**
106
+ * Authenticator attachment preference
107
+ */
108
+ AUTHENTICATOR_ATTACHMENT: 'platform',
109
+ };
88
110
 
89
- // import serviceState from '../core/ServiceState';
90
111
  /**
91
112
  * Detects the environment (Staging vs Production) based on the JWT token issuer.
92
113
  * Updates the global service state accordingly.
@@ -100,15 +121,8 @@ function detectStagingEnvironmentFromToken(token) {
100
121
  // Check if issuer matches staging URL
101
122
  if (payload.iss === SIGNER_CONFIG.STAGING_PERS_API_URL
102
123
  || SIGNER_CONFIG.STAGING_PERS_API_URL.includes(payload.iss)
103
- || SIGNER_CONFIG.STAGING_SIGNER_API_URL.includes(payload.iss)
104
- // Temporary additional checks for known staging issuers
105
- // || 'https://signer-api-staging.pers.ninja'.includes(payload.iss)
106
- || 'https://dev.api.pers.ninja/v2'.includes(payload.iss)) {
124
+ || SIGNER_CONFIG.STAGING_SIGNER_API_URL.includes(payload.iss)) {
107
125
  return true;
108
- /* } else if (payload.iss === SIGNER_CONFIG.DEFAULT_PERS_API_URL ||
109
- SIGNER_CONFIG.DEFAULT_PERS_API_URL.includes(payload.iss)) {
110
- // Explicitly production
111
- return false; */
112
126
  }
113
127
  }
114
128
  return false;
@@ -1451,11 +1465,11 @@ class TransactionSigningService {
1451
1465
  let provider;
1452
1466
  try {
1453
1467
  // Try primary provider first
1454
- console.info(`[TransactionSigningService] Attempting to connect to primary provider: ${ethersProviderUrl}`);
1468
+ //console.info(`[TransactionSigningService] Attempting to connect to primary provider: ${ethersProviderUrl}`);
1455
1469
  provider = new JsonRpcProvider(ethersProviderUrl);
1456
1470
  // Test the connection with a simple call
1457
1471
  await provider.getNetwork();
1458
- console.info(`[TransactionSigningService] Successfully connected to primary provider`);
1472
+ //console.info(`[TransactionSigningService] Successfully connected to primary provider`);
1459
1473
  }
1460
1474
  catch (primaryError) {
1461
1475
  console.warn(`[TransactionSigningService] Primary provider failed, falling back to: ${DEFAULT_FALLBACK_PROVIDER}`, primaryError);
@@ -1463,7 +1477,7 @@ class TransactionSigningService {
1463
1477
  provider = new JsonRpcProvider(DEFAULT_FALLBACK_PROVIDER);
1464
1478
  // Test the fallback connection
1465
1479
  await provider.getNetwork();
1466
- console.info(`[TransactionSigningService] Successfully connected to fallback provider`);
1480
+ //console.info(`[TransactionSigningService] Successfully connected to fallback provider`);
1467
1481
  }
1468
1482
  catch (fallbackError) {
1469
1483
  console.error(`[TransactionSigningService] Both primary and fallback providers failed`, { primaryError, fallbackError });
@@ -1527,13 +1541,13 @@ class TransactionSigningService {
1527
1541
  // Validate input parameters first using TransactionValidator
1528
1542
  TransactionValidator.validateSigningParams(params);
1529
1543
  const { transactionId, authTokens, ethersProviderUrl } = params;
1530
- console.info(`[TransactionSigningService] Starting signature process for ${transactionId}`);
1544
+ // console.info(`[TransactionSigningService] Starting signature process for ${transactionId}`);
1531
1545
  try {
1532
1546
  // Step 2: Prepare wallet for signing
1533
1547
  const wallet = await this.prepareWallet(authTokens, ethersProviderUrl);
1534
1548
  // Step 3: Execute transaction signing
1535
1549
  const signature = await this.executeTransactionSigning(wallet, signingData);
1536
- console.info(`[TransactionSigningService] Completed signing successfully: ${transactionId}`);
1550
+ // console.info(`[TransactionSigningService] Completed signing successfully: ${transactionId}`);
1537
1551
  return {
1538
1552
  success: true,
1539
1553
  transactionId,
@@ -1666,6 +1680,28 @@ function getServiceConfig() {
1666
1680
  return getConfigProvider().getServiceConfig();
1667
1681
  }
1668
1682
 
1683
+ /**
1684
+ * Get the WebAuthn configuration based on the current environment.
1685
+ * This logic is shared between browser and React Native implementations
1686
+ * to ensure consistent behavior while respecting platform differences.
1687
+ */
1688
+ function getWebAuthnConfig() {
1689
+ // Check if running in a browser environment with window.location available
1690
+ // We use a safe check that won't crash in React Native
1691
+ const isBrowser = typeof window !== 'undefined' && !!window.location && !!window.location.hostname;
1692
+ return {
1693
+ relyingParty: {
1694
+ // In browser, prefer the actual hostname to avoid RP ID mismatch errors
1695
+ // In React Native, use the configured default RP ID
1696
+ id: isBrowser ? window.location.hostname : SIGNER_CONFIG.DEFAULT_RELYING_PARTY_ID,
1697
+ name: SIGNER_CONFIG.DEFAULT_RELYING_PARTY_NAME,
1698
+ // In browser, use the actual origin
1699
+ // In React Native, fallback to the default RP ID (or a configured origin if added later)
1700
+ origin: isBrowser ? window.location.origin : SIGNER_CONFIG.DEFAULT_RELYING_PARTY_ID,
1701
+ }
1702
+ };
1703
+ }
1704
+
1669
1705
  /**
1670
1706
  * Base64URL encoding/decoding utilities
1671
1707
  * Used for WebAuthn credential ID and challenge handling
@@ -1738,9 +1774,8 @@ class BrowserWebAuthnProvider {
1738
1774
  // 1. Prepare options for navigator.credentials.create
1739
1775
  // We construct authenticatorSelection manually to ensure no conflicting legacy properties are present
1740
1776
  const authenticatorSelection = {
1741
- residentKey: 'required', // Force Passkey (discoverable credential)
1742
- // requireResidentKey: true, // REMOVED: Legacy property that can cause conflicts with 1Password
1743
- userVerification: challenge.authenticatorSelection?.userVerification || 'preferred',
1777
+ residentKey: WEBAUTHN_CONFIG.RESIDENT_KEY, // Force Passkey (discoverable credential)
1778
+ userVerification: challenge.authenticatorSelection?.userVerification || WEBAUTHN_CONFIG.USER_VERIFICATION,
1744
1779
  };
1745
1780
  const publicKey = {
1746
1781
  challenge: base64UrlToBuffer(challenge.challenge),
@@ -1756,16 +1791,13 @@ class BrowserWebAuthnProvider {
1756
1791
  pubKeyCredParams: challenge.pubKeyCredParams,
1757
1792
  authenticatorSelection,
1758
1793
  attestation: 'none',
1759
- timeout: challenge.timeout || 60000,
1794
+ timeout: challenge.timeout || WEBAUTHN_CONFIG.TIMEOUT,
1760
1795
  excludeCredentials: challenge.excludeCredentials?.map((cred) => ({
1761
1796
  id: base64UrlToBuffer(cred.id),
1762
1797
  type: 'public-key',
1763
1798
  transports: cred.transports,
1764
1799
  })),
1765
1800
  };
1766
- // console.log('WebAuthn create options:', publicKey);
1767
- // console.log('WebAuthn create challenge:', challenge);
1768
- // console.log('this.config', this.config)
1769
1801
  // 2. Call the browser's native WebAuthn API
1770
1802
  const credential = await navigator.credentials.create({ publicKey });
1771
1803
  if (!credential)
@@ -1789,8 +1821,8 @@ class BrowserWebAuthnProvider {
1789
1821
  const publicKey = {
1790
1822
  challenge: base64UrlToBuffer(challenge.challenge),
1791
1823
  rpId: challenge.rpId || this.config.relyingParty.id,
1792
- userVerification: challenge.userVerification || 'preferred',
1793
- timeout: 60000,
1824
+ userVerification: challenge.userVerification || WEBAUTHN_CONFIG.USER_VERIFICATION,
1825
+ timeout: WEBAUTHN_CONFIG.TIMEOUT,
1794
1826
  allowCredentials: challenge.allowCredentials?.webauthn?.map((cred) => ({
1795
1827
  id: base64UrlToBuffer(cred.id),
1796
1828
  type: 'public-key',
@@ -1829,13 +1861,7 @@ class BrowserWebAuthnProvider {
1829
1861
  * Get WebAuthn provider for browser environments
1830
1862
  */
1831
1863
  async function getBrowserWebAuthnProvider() {
1832
- const config = {
1833
- relyingParty: {
1834
- id: typeof window !== 'undefined' ? window.location.hostname : SIGNER_CONFIG.DEFAULT_RELYING_PARTY_ID,
1835
- name: SIGNER_CONFIG.DEFAULT_RELYING_PARTY_NAME,
1836
- origin: typeof window !== 'undefined' ? window.location.origin : SIGNER_CONFIG.DEFAULT_RELYING_PARTY_ID,
1837
- }
1838
- };
1864
+ const config = getWebAuthnConfig();
1839
1865
  return new BrowserWebAuthnProvider(config);
1840
1866
  }
1841
1867
 
@@ -1851,13 +1877,18 @@ var WebAuthnProvider_browser = /*#__PURE__*/Object.freeze({
1851
1877
  */
1852
1878
  class ReactNativeWebAuthnProvider {
1853
1879
  constructor(config) {
1880
+ this.passkeyLibrary = null;
1854
1881
  this.config = config;
1855
1882
  }
1856
1883
  async getPasskeyLibrary() {
1884
+ if (this.passkeyLibrary) {
1885
+ return this.passkeyLibrary;
1886
+ }
1857
1887
  try {
1858
1888
  // Dynamic import to avoid hard dependency
1859
1889
  // @ts-ignore - Optional dependency
1860
1890
  const { Passkey } = await import('react-native-passkey');
1891
+ this.passkeyLibrary = Passkey;
1861
1892
  return Passkey;
1862
1893
  }
1863
1894
  catch (error) {
@@ -1873,7 +1904,6 @@ class ReactNativeWebAuthnProvider {
1873
1904
  }
1874
1905
  }
1875
1906
  async handleWebFallback(operation, challenge) {
1876
- console.log(`Falling back to browser WebAuthn for React Native Web (${operation})...`);
1877
1907
  try {
1878
1908
  // Import and use browser WebAuthn provider
1879
1909
  const { getBrowserWebAuthnProvider } = await Promise.resolve().then(function () { return WebAuthnProvider_browser; });
@@ -1896,8 +1926,8 @@ class ReactNativeWebAuthnProvider {
1896
1926
  // If it's a very long string, it might be an issue.
1897
1927
  // But let's focus on aligning the authenticatorSelection first.
1898
1928
  const authenticatorSelection = {
1899
- residentKey: 'required', // Force Passkey (discoverable credential)
1900
- userVerification: challenge.authenticatorSelection?.userVerification || 'preferred',
1929
+ residentKey: WEBAUTHN_CONFIG.RESIDENT_KEY, // Force Passkey (discoverable credential)
1930
+ userVerification: challenge.authenticatorSelection?.userVerification || WEBAUTHN_CONFIG.USER_VERIFICATION,
1901
1931
  // Explicitly undefined to avoid issues, though JS objects don't usually include undefined keys in JSON
1902
1932
  // But react-native-passkey might handle it differently.
1903
1933
  };
@@ -1926,11 +1956,9 @@ class ReactNativeWebAuthnProvider {
1926
1956
  transports: v.transports
1927
1957
  })), // Cast to any to avoid strict type mismatch with react-native-passkey types
1928
1958
  authenticatorSelection,
1929
- timeout: challenge.timeout || 60000,
1959
+ timeout: challenge.timeout || WEBAUTHN_CONFIG.TIMEOUT,
1930
1960
  };
1931
- console.log('[RNWebAuthn] Calling Passkey.create with:', JSON.stringify(request, null, 2));
1932
1961
  const result = await Passkey.create(request);
1933
- console.log('[RNWebAuthn] Passkey.create success:', JSON.stringify(result, null, 2));
1934
1962
  return {
1935
1963
  id: result.id,
1936
1964
  clientDataJSON: result.response.clientDataJSON,
@@ -1958,12 +1986,10 @@ class ReactNativeWebAuthnProvider {
1958
1986
  // transports: cred.transports,
1959
1987
  })),
1960
1988
  rpId: challenge.rpId || this.config.relyingParty.id,
1961
- userVerification: challenge.userVerification || 'preferred',
1962
- timeout: challenge.timeout || 60000,
1989
+ userVerification: challenge.userVerification || WEBAUTHN_CONFIG.USER_VERIFICATION,
1990
+ timeout: challenge.timeout || WEBAUTHN_CONFIG.TIMEOUT,
1963
1991
  };
1964
- console.log('[RNWebAuthn] Calling Passkey.get (sign) with:', JSON.stringify(request, null, 2));
1965
1992
  const credential = await Passkey.get(request);
1966
- console.log('[RNWebAuthn] Passkey.get success:', JSON.stringify(credential, null, 2));
1967
1993
  return {
1968
1994
  kind: 'Fido2',
1969
1995
  credentialAssertion: {
@@ -1988,12 +2014,7 @@ class ReactNativeWebAuthnProvider {
1988
2014
  * Get WebAuthn provider for React Native environments
1989
2015
  */
1990
2016
  async function getReactNativeWebAuthnProvider() {
1991
- const config = {
1992
- relyingParty: {
1993
- id: SIGNER_CONFIG.DEFAULT_RELYING_PARTY_ID,
1994
- name: SIGNER_CONFIG.DEFAULT_RELYING_PARTY_NAME,
1995
- }
1996
- };
2017
+ const config = getWebAuthnConfig();
1997
2018
  return new ReactNativeWebAuthnProvider(config);
1998
2019
  }
1999
2020
 
@@ -2125,7 +2146,6 @@ class PersSignerSDK {
2125
2146
  * @throws {Error} If required configuration is missing
2126
2147
  */
2127
2148
  constructor(config) {
2128
- console.log('DEBUG: v1.2.0');
2129
2149
  this.config = config;
2130
2150
  setConfigProvider(new WebConfigProvider({
2131
2151
  apiUrl: config.apiUrl || SIGNER_CONFIG.DEFAULT_SIGNER_API_URL,
@@ -2334,10 +2354,12 @@ class PersSignerSDK {
2334
2354
  authTokens,
2335
2355
  ethersProviderUrl: this.config.ethersProviderUrl ?? DEFAULT_FALLBACK_PROVIDER
2336
2356
  }, signingData);
2337
- this.triggerStatusUpdate(SigningStatus.COMPLETED, 'Transaction signed successfully', {
2338
- transactionId: payload.transactionId,
2339
- signature: result.signature
2340
- }, statusCallback);
2357
+ if (result.success) {
2358
+ this.triggerStatusUpdate(SigningStatus.COMPLETED, 'Transaction signed successfully', {
2359
+ transactionId: payload.transactionId,
2360
+ signature: result.signature
2361
+ }, statusCallback);
2362
+ }
2341
2363
  return result;
2342
2364
  }
2343
2365
  catch (error) {