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