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