@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.
@@ -137,13 +137,11 @@ const SIGNER_CONFIG = {
137
137
  * Used as fallback when production signer API is unavailable
138
138
  */
139
139
  STAGING_SIGNER_API_URL: 'https://signer-api-staging.pers.ninja/v1',
140
- // STAGING_SIGNER_API_URL: 'http://localhost:8080/v1',
141
140
  /**
142
141
  * PERS Platform API URLs
143
142
  */
144
143
  DEFAULT_PERS_API_URL: 'https://api.pers.ninja/v2',
145
144
  STAGING_PERS_API_URL: 'https://dev.api.pers.ninja/v2',
146
- // STAGING_PERS_API_URL: 'https://explorins-loyalty.ngrok.io',
147
145
  /**
148
146
  * Default relying party name for WebAuthn operations
149
147
  * This appears in the browser's authentication prompts
@@ -155,8 +153,31 @@ const SIGNER_CONFIG = {
155
153
  */
156
154
  // Default fallback provider for Sepolia testnet (no API key required)
157
155
  const DEFAULT_FALLBACK_PROVIDER = 'https://ethereum-sepolia.publicnode.com';
156
+ /**
157
+ * WebAuthn configuration constants
158
+ */
159
+ const WEBAUTHN_CONFIG = {
160
+ /**
161
+ * Timeout for WebAuthn operations (in milliseconds)
162
+ */
163
+ TIMEOUT: 60000,
164
+ /**
165
+ * User verification requirement
166
+ * 'preferred' - ask for verification if available
167
+ * 'required' - require verification
168
+ * 'discouraged' - don't ask for verification
169
+ */
170
+ USER_VERIFICATION: 'preferred',
171
+ /**
172
+ * Resident key requirement (Passkeys)
173
+ */
174
+ RESIDENT_KEY: 'required',
175
+ /**
176
+ * Authenticator attachment preference
177
+ */
178
+ AUTHENTICATOR_ATTACHMENT: 'platform',
179
+ };
158
180
 
159
- // import serviceState from '../core/ServiceState';
160
181
  /**
161
182
  * Detects the environment (Staging vs Production) based on the JWT token issuer.
162
183
  * Updates the global service state accordingly.
@@ -170,16 +191,8 @@ function detectStagingEnvironmentFromToken(token) {
170
191
  // Check if issuer matches staging URL
171
192
  if (payload.iss === SIGNER_CONFIG.STAGING_PERS_API_URL
172
193
  || SIGNER_CONFIG.STAGING_PERS_API_URL.includes(payload.iss)
173
- || SIGNER_CONFIG.STAGING_SIGNER_API_URL.includes(payload.iss)
174
- // Temporary additional checks for known staging issuers
175
- // || 'https://signer-api-staging.pers.ninja'.includes(payload.iss)
176
- // || 'https://dev.api.pers.ninja/v2'.includes(payload.iss)
177
- ) {
194
+ || SIGNER_CONFIG.STAGING_SIGNER_API_URL.includes(payload.iss)) {
178
195
  return true;
179
- /* } else if (payload.iss === SIGNER_CONFIG.DEFAULT_PERS_API_URL ||
180
- SIGNER_CONFIG.DEFAULT_PERS_API_URL.includes(payload.iss)) {
181
- // Explicitly production
182
- return false; */
183
196
  }
184
197
  }
185
198
  return false;
@@ -1206,11 +1219,11 @@ class TransactionSigningService {
1206
1219
  let provider;
1207
1220
  try {
1208
1221
  // Try primary provider first
1209
- console.info(`[TransactionSigningService] Attempting to connect to primary provider: ${ethersProviderUrl}`);
1222
+ //console.info(`[TransactionSigningService] Attempting to connect to primary provider: ${ethersProviderUrl}`);
1210
1223
  provider = new ethers.JsonRpcProvider(ethersProviderUrl);
1211
1224
  // Test the connection with a simple call
1212
1225
  await provider.getNetwork();
1213
- console.info(`[TransactionSigningService] Successfully connected to primary provider`);
1226
+ //console.info(`[TransactionSigningService] Successfully connected to primary provider`);
1214
1227
  }
1215
1228
  catch (primaryError) {
1216
1229
  console.warn(`[TransactionSigningService] Primary provider failed, falling back to: ${DEFAULT_FALLBACK_PROVIDER}`, primaryError);
@@ -1218,7 +1231,7 @@ class TransactionSigningService {
1218
1231
  provider = new ethers.JsonRpcProvider(DEFAULT_FALLBACK_PROVIDER);
1219
1232
  // Test the fallback connection
1220
1233
  await provider.getNetwork();
1221
- console.info(`[TransactionSigningService] Successfully connected to fallback provider`);
1234
+ //console.info(`[TransactionSigningService] Successfully connected to fallback provider`);
1222
1235
  }
1223
1236
  catch (fallbackError) {
1224
1237
  console.error(`[TransactionSigningService] Both primary and fallback providers failed`, { primaryError, fallbackError });
@@ -1282,13 +1295,13 @@ class TransactionSigningService {
1282
1295
  // Validate input parameters first using TransactionValidator
1283
1296
  TransactionValidator.validateSigningParams(params);
1284
1297
  const { transactionId, authTokens, ethersProviderUrl } = params;
1285
- console.info(`[TransactionSigningService] Starting signature process for ${transactionId}`);
1298
+ // console.info(`[TransactionSigningService] Starting signature process for ${transactionId}`);
1286
1299
  try {
1287
1300
  // Step 2: Prepare wallet for signing
1288
1301
  const wallet = await this.prepareWallet(authTokens, ethersProviderUrl);
1289
1302
  // Step 3: Execute transaction signing
1290
1303
  const signature = await this.executeTransactionSigning(wallet, signingData);
1291
- console.info(`[TransactionSigningService] Completed signing successfully: ${transactionId}`);
1304
+ // console.info(`[TransactionSigningService] Completed signing successfully: ${transactionId}`);
1292
1305
  return {
1293
1306
  success: true,
1294
1307
  transactionId,
@@ -1757,7 +1770,6 @@ class PersSignerSDK {
1757
1770
  * @throws {Error} If required configuration is missing
1758
1771
  */
1759
1772
  constructor(config) {
1760
- // console.log('DEBUG: v1.2.0')
1761
1773
  this.config = config;
1762
1774
  setConfigProvider(new WebConfigProvider({
1763
1775
  apiUrl: config.apiUrl || SIGNER_CONFIG.DEFAULT_SIGNER_API_URL,
@@ -1966,10 +1978,12 @@ class PersSignerSDK {
1966
1978
  authTokens,
1967
1979
  ethersProviderUrl: this.config.ethersProviderUrl ?? DEFAULT_FALLBACK_PROVIDER
1968
1980
  }, signingData);
1969
- this.triggerStatusUpdate(SigningStatus.COMPLETED, 'Transaction signed successfully', {
1970
- transactionId: payload.transactionId,
1971
- signature: result.signature
1972
- }, statusCallback);
1981
+ if (result.success) {
1982
+ this.triggerStatusUpdate(SigningStatus.COMPLETED, 'Transaction signed successfully', {
1983
+ transactionId: payload.transactionId,
1984
+ signature: result.signature
1985
+ }, statusCallback);
1986
+ }
1973
1987
  return result;
1974
1988
  }
1975
1989
  catch (error) {
@@ -2113,6 +2127,28 @@ class PersSignerSDK {
2113
2127
  }
2114
2128
  }
2115
2129
 
2130
+ /**
2131
+ * Get the WebAuthn configuration based on the current environment.
2132
+ * This logic is shared between browser and React Native implementations
2133
+ * to ensure consistent behavior while respecting platform differences.
2134
+ */
2135
+ function getWebAuthnConfig() {
2136
+ // Check if running in a browser environment with window.location available
2137
+ // We use a safe check that won't crash in React Native
2138
+ const isBrowser = typeof window !== 'undefined' && !!window.location && !!window.location.hostname;
2139
+ return {
2140
+ relyingParty: {
2141
+ // In browser, prefer the actual hostname to avoid RP ID mismatch errors
2142
+ // In React Native, use the configured default RP ID
2143
+ id: isBrowser ? window.location.hostname : SIGNER_CONFIG.DEFAULT_RELYING_PARTY_ID,
2144
+ name: SIGNER_CONFIG.DEFAULT_RELYING_PARTY_NAME,
2145
+ // In browser, use the actual origin
2146
+ // In React Native, fallback to the default RP ID (or a configured origin if added later)
2147
+ origin: isBrowser ? window.location.origin : SIGNER_CONFIG.DEFAULT_RELYING_PARTY_ID,
2148
+ }
2149
+ };
2150
+ }
2151
+
2116
2152
  /**
2117
2153
  * Base64URL encoding/decoding utilities
2118
2154
  * Used for WebAuthn credential ID and challenge handling
@@ -2170,13 +2206,18 @@ function toBase64Url(str) {
2170
2206
  */
2171
2207
  class ReactNativeWebAuthnProvider {
2172
2208
  constructor(config) {
2209
+ this.passkeyLibrary = null;
2173
2210
  this.config = config;
2174
2211
  }
2175
2212
  async getPasskeyLibrary() {
2213
+ if (this.passkeyLibrary) {
2214
+ return this.passkeyLibrary;
2215
+ }
2176
2216
  try {
2177
2217
  // Dynamic import to avoid hard dependency
2178
2218
  // @ts-ignore - Optional dependency
2179
2219
  const { Passkey } = await import('react-native-passkey');
2220
+ this.passkeyLibrary = Passkey;
2180
2221
  return Passkey;
2181
2222
  }
2182
2223
  catch (error) {
@@ -2192,7 +2233,6 @@ class ReactNativeWebAuthnProvider {
2192
2233
  }
2193
2234
  }
2194
2235
  async handleWebFallback(operation, challenge) {
2195
- console.log(`Falling back to browser WebAuthn for React Native Web (${operation})...`);
2196
2236
  try {
2197
2237
  // Import and use browser WebAuthn provider
2198
2238
  const { getBrowserWebAuthnProvider } = await Promise.resolve().then(function () { return WebAuthnProvider_browser; });
@@ -2215,8 +2255,8 @@ class ReactNativeWebAuthnProvider {
2215
2255
  // If it's a very long string, it might be an issue.
2216
2256
  // But let's focus on aligning the authenticatorSelection first.
2217
2257
  const authenticatorSelection = {
2218
- residentKey: 'required', // Force Passkey (discoverable credential)
2219
- userVerification: challenge.authenticatorSelection?.userVerification || 'preferred',
2258
+ residentKey: WEBAUTHN_CONFIG.RESIDENT_KEY, // Force Passkey (discoverable credential)
2259
+ userVerification: challenge.authenticatorSelection?.userVerification || WEBAUTHN_CONFIG.USER_VERIFICATION,
2220
2260
  // Explicitly undefined to avoid issues, though JS objects don't usually include undefined keys in JSON
2221
2261
  // But react-native-passkey might handle it differently.
2222
2262
  };
@@ -2245,11 +2285,9 @@ class ReactNativeWebAuthnProvider {
2245
2285
  transports: v.transports
2246
2286
  })), // Cast to any to avoid strict type mismatch with react-native-passkey types
2247
2287
  authenticatorSelection,
2248
- timeout: challenge.timeout || 60000,
2288
+ timeout: challenge.timeout || WEBAUTHN_CONFIG.TIMEOUT,
2249
2289
  };
2250
- console.log('[RNWebAuthn] Calling Passkey.create with:', JSON.stringify(request, null, 2));
2251
2290
  const result = await Passkey.create(request);
2252
- console.log('[RNWebAuthn] Passkey.create success:', JSON.stringify(result, null, 2));
2253
2291
  return {
2254
2292
  id: result.id,
2255
2293
  clientDataJSON: result.response.clientDataJSON,
@@ -2277,12 +2315,10 @@ class ReactNativeWebAuthnProvider {
2277
2315
  // transports: cred.transports,
2278
2316
  })),
2279
2317
  rpId: challenge.rpId || this.config.relyingParty.id,
2280
- userVerification: challenge.userVerification || 'preferred',
2281
- timeout: challenge.timeout || 60000,
2318
+ userVerification: challenge.userVerification || WEBAUTHN_CONFIG.USER_VERIFICATION,
2319
+ timeout: challenge.timeout || WEBAUTHN_CONFIG.TIMEOUT,
2282
2320
  };
2283
- console.log('[RNWebAuthn] Calling Passkey.get (sign) with:', JSON.stringify(request, null, 2));
2284
2321
  const credential = await Passkey.get(request);
2285
- console.log('[RNWebAuthn] Passkey.get success:', JSON.stringify(credential, null, 2));
2286
2322
  return {
2287
2323
  kind: 'Fido2',
2288
2324
  credentialAssertion: {
@@ -2307,12 +2343,7 @@ class ReactNativeWebAuthnProvider {
2307
2343
  * Get WebAuthn provider for React Native environments
2308
2344
  */
2309
2345
  async function getReactNativeWebAuthnProvider() {
2310
- const config = {
2311
- relyingParty: {
2312
- id: SIGNER_CONFIG.DEFAULT_RELYING_PARTY_ID,
2313
- name: SIGNER_CONFIG.DEFAULT_RELYING_PARTY_NAME,
2314
- }
2315
- };
2346
+ const config = getWebAuthnConfig();
2316
2347
  return new ReactNativeWebAuthnProvider(config);
2317
2348
  }
2318
2349
 
@@ -2393,9 +2424,8 @@ class BrowserWebAuthnProvider {
2393
2424
  // 1. Prepare options for navigator.credentials.create
2394
2425
  // We construct authenticatorSelection manually to ensure no conflicting legacy properties are present
2395
2426
  const authenticatorSelection = {
2396
- residentKey: 'required', // Force Passkey (discoverable credential)
2397
- // requireResidentKey: true, // REMOVED: Legacy property that can cause conflicts with 1Password
2398
- userVerification: challenge.authenticatorSelection?.userVerification || 'preferred',
2427
+ residentKey: WEBAUTHN_CONFIG.RESIDENT_KEY, // Force Passkey (discoverable credential)
2428
+ userVerification: challenge.authenticatorSelection?.userVerification || WEBAUTHN_CONFIG.USER_VERIFICATION,
2399
2429
  };
2400
2430
  const publicKey = {
2401
2431
  challenge: base64UrlToBuffer(challenge.challenge),
@@ -2411,16 +2441,13 @@ class BrowserWebAuthnProvider {
2411
2441
  pubKeyCredParams: challenge.pubKeyCredParams,
2412
2442
  authenticatorSelection,
2413
2443
  attestation: 'none',
2414
- timeout: challenge.timeout || 60000,
2444
+ timeout: challenge.timeout || WEBAUTHN_CONFIG.TIMEOUT,
2415
2445
  excludeCredentials: challenge.excludeCredentials?.map((cred) => ({
2416
2446
  id: base64UrlToBuffer(cred.id),
2417
2447
  type: 'public-key',
2418
2448
  transports: cred.transports,
2419
2449
  })),
2420
2450
  };
2421
- // console.log('WebAuthn create options:', publicKey);
2422
- // console.log('WebAuthn create challenge:', challenge);
2423
- // console.log('this.config', this.config)
2424
2451
  // 2. Call the browser's native WebAuthn API
2425
2452
  const credential = await navigator.credentials.create({ publicKey });
2426
2453
  if (!credential)
@@ -2444,8 +2471,8 @@ class BrowserWebAuthnProvider {
2444
2471
  const publicKey = {
2445
2472
  challenge: base64UrlToBuffer(challenge.challenge),
2446
2473
  rpId: challenge.rpId || this.config.relyingParty.id,
2447
- userVerification: challenge.userVerification || 'preferred',
2448
- timeout: 60000,
2474
+ userVerification: challenge.userVerification || WEBAUTHN_CONFIG.USER_VERIFICATION,
2475
+ timeout: WEBAUTHN_CONFIG.TIMEOUT,
2449
2476
  allowCredentials: challenge.allowCredentials?.webauthn?.map((cred) => ({
2450
2477
  id: base64UrlToBuffer(cred.id),
2451
2478
  type: 'public-key',
@@ -2484,13 +2511,7 @@ class BrowserWebAuthnProvider {
2484
2511
  * Get WebAuthn provider for browser environments
2485
2512
  */
2486
2513
  async function getBrowserWebAuthnProvider() {
2487
- const config = {
2488
- relyingParty: {
2489
- id: typeof window !== 'undefined' ? window.location.hostname : SIGNER_CONFIG.DEFAULT_RELYING_PARTY_ID,
2490
- name: SIGNER_CONFIG.DEFAULT_RELYING_PARTY_NAME,
2491
- origin: typeof window !== 'undefined' ? window.location.origin : SIGNER_CONFIG.DEFAULT_RELYING_PARTY_ID,
2492
- }
2493
- };
2514
+ const config = getWebAuthnConfig();
2494
2515
  return new BrowserWebAuthnProvider(config);
2495
2516
  }
2496
2517