@explorins/pers-signer 1.0.27 → 1.0.32
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/browser.cjs.js +77 -41
- package/dist/browser.cjs.js.map +1 -1
- package/dist/browser.esm.js +77 -41
- package/dist/browser.esm.js.map +1 -1
- package/dist/index.cjs.js +88 -57
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +88 -57
- package/dist/index.esm.js.map +1 -1
- package/dist/react-native.cjs.js +88 -57
- package/dist/react-native.cjs.js.map +1 -1
- package/dist/react-native.esm.js +88 -57
- package/dist/react-native.esm.js.map +1 -1
- package/package.json +1 -1
package/dist/react-native.cjs.js
CHANGED
|
@@ -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,
|
|
@@ -1587,7 +1600,7 @@ class AuthenticationService {
|
|
|
1587
1600
|
async combinedAuthentication(identifier, persAccessToken) {
|
|
1588
1601
|
try {
|
|
1589
1602
|
// Step 1: Try to login with PERS token first (this will get signer JWT if user exists)
|
|
1590
|
-
let signerToken;
|
|
1603
|
+
let signerToken = null;
|
|
1591
1604
|
try {
|
|
1592
1605
|
const loginResult = await this.loginWithPersToken(persAccessToken);
|
|
1593
1606
|
// Extract token from login result
|
|
@@ -1601,6 +1614,11 @@ class AuthenticationService {
|
|
|
1601
1614
|
catch (loginError) {
|
|
1602
1615
|
// Step 2: User doesn't exist - register with v1 API (Unified Flow)
|
|
1603
1616
|
try {
|
|
1617
|
+
// Only proceed to registration if the error is explicitly 'user_not_found'
|
|
1618
|
+
const errorMessage = loginError instanceof Error ? loginError.message : String(loginError);
|
|
1619
|
+
if (errorMessage !== 'user_not_found') {
|
|
1620
|
+
throw loginError;
|
|
1621
|
+
}
|
|
1604
1622
|
const registerResult = await this.registerUser(persAccessToken);
|
|
1605
1623
|
if (registerResult && registerResult.access_token) {
|
|
1606
1624
|
signerToken = registerResult.access_token;
|
|
@@ -1611,9 +1629,13 @@ class AuthenticationService {
|
|
|
1611
1629
|
}
|
|
1612
1630
|
catch (registerError) {
|
|
1613
1631
|
console.error(`[AuthenticationService] Registration failed for ${identifier}:`, registerError);
|
|
1632
|
+
// Explicitly rethrow to abort the entire authentication process
|
|
1614
1633
|
throw registerError;
|
|
1615
1634
|
}
|
|
1616
1635
|
}
|
|
1636
|
+
if (!signerToken) {
|
|
1637
|
+
throw new Error('Authentication failed: Unable to obtain signer token');
|
|
1638
|
+
}
|
|
1617
1639
|
const user = {
|
|
1618
1640
|
identifier: identifier,
|
|
1619
1641
|
signerAuthToken: signerToken,
|
|
@@ -1623,8 +1645,9 @@ class AuthenticationService {
|
|
|
1623
1645
|
return user;
|
|
1624
1646
|
}
|
|
1625
1647
|
catch (error) {
|
|
1626
|
-
|
|
1627
|
-
|
|
1648
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1649
|
+
console.error(`[PersSignerSDK] Combined authentication failed for ${identifier}:`, errorMessage);
|
|
1650
|
+
throw new Error(`Combined authentication failed: ${errorMessage}`);
|
|
1628
1651
|
}
|
|
1629
1652
|
}
|
|
1630
1653
|
}
|
|
@@ -1757,7 +1780,6 @@ class PersSignerSDK {
|
|
|
1757
1780
|
* @throws {Error} If required configuration is missing
|
|
1758
1781
|
*/
|
|
1759
1782
|
constructor(config) {
|
|
1760
|
-
// console.log('DEBUG: v1.2.0')
|
|
1761
1783
|
this.config = config;
|
|
1762
1784
|
setConfigProvider(new WebConfigProvider({
|
|
1763
1785
|
apiUrl: config.apiUrl || SIGNER_CONFIG.DEFAULT_SIGNER_API_URL,
|
|
@@ -1966,10 +1988,12 @@ class PersSignerSDK {
|
|
|
1966
1988
|
authTokens,
|
|
1967
1989
|
ethersProviderUrl: this.config.ethersProviderUrl ?? DEFAULT_FALLBACK_PROVIDER
|
|
1968
1990
|
}, signingData);
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1991
|
+
if (result.success) {
|
|
1992
|
+
this.triggerStatusUpdate(SigningStatus.COMPLETED, 'Transaction signed successfully', {
|
|
1993
|
+
transactionId: payload.transactionId,
|
|
1994
|
+
signature: result.signature
|
|
1995
|
+
}, statusCallback);
|
|
1996
|
+
}
|
|
1973
1997
|
return result;
|
|
1974
1998
|
}
|
|
1975
1999
|
catch (error) {
|
|
@@ -2113,6 +2137,28 @@ class PersSignerSDK {
|
|
|
2113
2137
|
}
|
|
2114
2138
|
}
|
|
2115
2139
|
|
|
2140
|
+
/**
|
|
2141
|
+
* Get the WebAuthn configuration based on the current environment.
|
|
2142
|
+
* This logic is shared between browser and React Native implementations
|
|
2143
|
+
* to ensure consistent behavior while respecting platform differences.
|
|
2144
|
+
*/
|
|
2145
|
+
function getWebAuthnConfig() {
|
|
2146
|
+
// Check if running in a browser environment with window.location available
|
|
2147
|
+
// We use a safe check that won't crash in React Native
|
|
2148
|
+
const isBrowser = typeof window !== 'undefined' && !!window.location && !!window.location.hostname;
|
|
2149
|
+
return {
|
|
2150
|
+
relyingParty: {
|
|
2151
|
+
// In browser, prefer the actual hostname to avoid RP ID mismatch errors
|
|
2152
|
+
// In React Native, use the configured default RP ID
|
|
2153
|
+
id: isBrowser ? window.location.hostname : SIGNER_CONFIG.DEFAULT_RELYING_PARTY_ID,
|
|
2154
|
+
name: SIGNER_CONFIG.DEFAULT_RELYING_PARTY_NAME,
|
|
2155
|
+
// In browser, use the actual origin
|
|
2156
|
+
// In React Native, fallback to the default RP ID (or a configured origin if added later)
|
|
2157
|
+
origin: isBrowser ? window.location.origin : SIGNER_CONFIG.DEFAULT_RELYING_PARTY_ID,
|
|
2158
|
+
}
|
|
2159
|
+
};
|
|
2160
|
+
}
|
|
2161
|
+
|
|
2116
2162
|
/**
|
|
2117
2163
|
* Base64URL encoding/decoding utilities
|
|
2118
2164
|
* Used for WebAuthn credential ID and challenge handling
|
|
@@ -2170,13 +2216,18 @@ function toBase64Url(str) {
|
|
|
2170
2216
|
*/
|
|
2171
2217
|
class ReactNativeWebAuthnProvider {
|
|
2172
2218
|
constructor(config) {
|
|
2219
|
+
this.passkeyLibrary = null;
|
|
2173
2220
|
this.config = config;
|
|
2174
2221
|
}
|
|
2175
2222
|
async getPasskeyLibrary() {
|
|
2223
|
+
if (this.passkeyLibrary) {
|
|
2224
|
+
return this.passkeyLibrary;
|
|
2225
|
+
}
|
|
2176
2226
|
try {
|
|
2177
2227
|
// Dynamic import to avoid hard dependency
|
|
2178
2228
|
// @ts-ignore - Optional dependency
|
|
2179
2229
|
const { Passkey } = await import('react-native-passkey');
|
|
2230
|
+
this.passkeyLibrary = Passkey;
|
|
2180
2231
|
return Passkey;
|
|
2181
2232
|
}
|
|
2182
2233
|
catch (error) {
|
|
@@ -2192,7 +2243,6 @@ class ReactNativeWebAuthnProvider {
|
|
|
2192
2243
|
}
|
|
2193
2244
|
}
|
|
2194
2245
|
async handleWebFallback(operation, challenge) {
|
|
2195
|
-
console.log(`Falling back to browser WebAuthn for React Native Web (${operation})...`);
|
|
2196
2246
|
try {
|
|
2197
2247
|
// Import and use browser WebAuthn provider
|
|
2198
2248
|
const { getBrowserWebAuthnProvider } = await Promise.resolve().then(function () { return WebAuthnProvider_browser; });
|
|
@@ -2215,8 +2265,8 @@ class ReactNativeWebAuthnProvider {
|
|
|
2215
2265
|
// If it's a very long string, it might be an issue.
|
|
2216
2266
|
// But let's focus on aligning the authenticatorSelection first.
|
|
2217
2267
|
const authenticatorSelection = {
|
|
2218
|
-
residentKey:
|
|
2219
|
-
userVerification: challenge.authenticatorSelection?.userVerification ||
|
|
2268
|
+
residentKey: WEBAUTHN_CONFIG.RESIDENT_KEY, // Force Passkey (discoverable credential)
|
|
2269
|
+
userVerification: challenge.authenticatorSelection?.userVerification || WEBAUTHN_CONFIG.USER_VERIFICATION,
|
|
2220
2270
|
// Explicitly undefined to avoid issues, though JS objects don't usually include undefined keys in JSON
|
|
2221
2271
|
// But react-native-passkey might handle it differently.
|
|
2222
2272
|
};
|
|
@@ -2245,11 +2295,9 @@ class ReactNativeWebAuthnProvider {
|
|
|
2245
2295
|
transports: v.transports
|
|
2246
2296
|
})), // Cast to any to avoid strict type mismatch with react-native-passkey types
|
|
2247
2297
|
authenticatorSelection,
|
|
2248
|
-
timeout: challenge.timeout ||
|
|
2298
|
+
timeout: challenge.timeout || WEBAUTHN_CONFIG.TIMEOUT,
|
|
2249
2299
|
};
|
|
2250
|
-
console.log('[RNWebAuthn] Calling Passkey.create with:', JSON.stringify(request, null, 2));
|
|
2251
2300
|
const result = await Passkey.create(request);
|
|
2252
|
-
console.log('[RNWebAuthn] Passkey.create success:', JSON.stringify(result, null, 2));
|
|
2253
2301
|
return {
|
|
2254
2302
|
id: result.id,
|
|
2255
2303
|
clientDataJSON: result.response.clientDataJSON,
|
|
@@ -2277,12 +2325,10 @@ class ReactNativeWebAuthnProvider {
|
|
|
2277
2325
|
// transports: cred.transports,
|
|
2278
2326
|
})),
|
|
2279
2327
|
rpId: challenge.rpId || this.config.relyingParty.id,
|
|
2280
|
-
userVerification: challenge.userVerification ||
|
|
2281
|
-
timeout: challenge.timeout ||
|
|
2328
|
+
userVerification: challenge.userVerification || WEBAUTHN_CONFIG.USER_VERIFICATION,
|
|
2329
|
+
timeout: challenge.timeout || WEBAUTHN_CONFIG.TIMEOUT,
|
|
2282
2330
|
};
|
|
2283
|
-
console.log('[RNWebAuthn] Calling Passkey.get (sign) with:', JSON.stringify(request, null, 2));
|
|
2284
2331
|
const credential = await Passkey.get(request);
|
|
2285
|
-
console.log('[RNWebAuthn] Passkey.get success:', JSON.stringify(credential, null, 2));
|
|
2286
2332
|
return {
|
|
2287
2333
|
kind: 'Fido2',
|
|
2288
2334
|
credentialAssertion: {
|
|
@@ -2307,12 +2353,7 @@ class ReactNativeWebAuthnProvider {
|
|
|
2307
2353
|
* Get WebAuthn provider for React Native environments
|
|
2308
2354
|
*/
|
|
2309
2355
|
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
|
-
};
|
|
2356
|
+
const config = getWebAuthnConfig();
|
|
2316
2357
|
return new ReactNativeWebAuthnProvider(config);
|
|
2317
2358
|
}
|
|
2318
2359
|
|
|
@@ -2393,9 +2434,8 @@ class BrowserWebAuthnProvider {
|
|
|
2393
2434
|
// 1. Prepare options for navigator.credentials.create
|
|
2394
2435
|
// We construct authenticatorSelection manually to ensure no conflicting legacy properties are present
|
|
2395
2436
|
const authenticatorSelection = {
|
|
2396
|
-
residentKey:
|
|
2397
|
-
|
|
2398
|
-
userVerification: challenge.authenticatorSelection?.userVerification || 'preferred',
|
|
2437
|
+
residentKey: WEBAUTHN_CONFIG.RESIDENT_KEY, // Force Passkey (discoverable credential)
|
|
2438
|
+
userVerification: challenge.authenticatorSelection?.userVerification || WEBAUTHN_CONFIG.USER_VERIFICATION,
|
|
2399
2439
|
};
|
|
2400
2440
|
const publicKey = {
|
|
2401
2441
|
challenge: base64UrlToBuffer(challenge.challenge),
|
|
@@ -2411,16 +2451,13 @@ class BrowserWebAuthnProvider {
|
|
|
2411
2451
|
pubKeyCredParams: challenge.pubKeyCredParams,
|
|
2412
2452
|
authenticatorSelection,
|
|
2413
2453
|
attestation: 'none',
|
|
2414
|
-
timeout: challenge.timeout ||
|
|
2454
|
+
timeout: challenge.timeout || WEBAUTHN_CONFIG.TIMEOUT,
|
|
2415
2455
|
excludeCredentials: challenge.excludeCredentials?.map((cred) => ({
|
|
2416
2456
|
id: base64UrlToBuffer(cred.id),
|
|
2417
2457
|
type: 'public-key',
|
|
2418
2458
|
transports: cred.transports,
|
|
2419
2459
|
})),
|
|
2420
2460
|
};
|
|
2421
|
-
// console.log('WebAuthn create options:', publicKey);
|
|
2422
|
-
// console.log('WebAuthn create challenge:', challenge);
|
|
2423
|
-
// console.log('this.config', this.config)
|
|
2424
2461
|
// 2. Call the browser's native WebAuthn API
|
|
2425
2462
|
const credential = await navigator.credentials.create({ publicKey });
|
|
2426
2463
|
if (!credential)
|
|
@@ -2444,8 +2481,8 @@ class BrowserWebAuthnProvider {
|
|
|
2444
2481
|
const publicKey = {
|
|
2445
2482
|
challenge: base64UrlToBuffer(challenge.challenge),
|
|
2446
2483
|
rpId: challenge.rpId || this.config.relyingParty.id,
|
|
2447
|
-
userVerification: challenge.userVerification ||
|
|
2448
|
-
timeout:
|
|
2484
|
+
userVerification: challenge.userVerification || WEBAUTHN_CONFIG.USER_VERIFICATION,
|
|
2485
|
+
timeout: WEBAUTHN_CONFIG.TIMEOUT,
|
|
2449
2486
|
allowCredentials: challenge.allowCredentials?.webauthn?.map((cred) => ({
|
|
2450
2487
|
id: base64UrlToBuffer(cred.id),
|
|
2451
2488
|
type: 'public-key',
|
|
@@ -2484,13 +2521,7 @@ class BrowserWebAuthnProvider {
|
|
|
2484
2521
|
* Get WebAuthn provider for browser environments
|
|
2485
2522
|
*/
|
|
2486
2523
|
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
|
-
};
|
|
2524
|
+
const config = getWebAuthnConfig();
|
|
2494
2525
|
return new BrowserWebAuthnProvider(config);
|
|
2495
2526
|
}
|
|
2496
2527
|
|