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