@explorins/pers-sdk-react-native 1.5.13 → 1.5.15

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@explorins/pers-sdk-react-native",
3
- "version": "1.5.13",
3
+ "version": "1.5.15",
4
4
  "description": "React Native SDK for PERS Platform - Tourism Loyalty System with Manager Pattern Architecture",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -1,9 +1,8 @@
1
1
  import { useCallback, useState, useRef, useEffect } from 'react';
2
2
  import { usePersSDK } from '../providers/PersSDKProvider';
3
- import type { UserDTO, AdminDTO } from '@explorins/pers-shared';
4
3
 
5
4
  // Dynamic import the signer SDK to avoid build issues with static dependencies
6
- let createPersSignerSDK: ((config: PersSignerConfig) => Promise<any>) | null = null;
5
+ let createPersSignerSDK: ((config: PersSignerConfig) => Promise<PersSignerSDKInstance>) | null = null;
7
6
  try {
8
7
  const signerModule = require('@explorins/pers-signer/react-native');
9
8
  createPersSignerSDK = signerModule.createPersSignerSDK;
@@ -13,25 +12,22 @@ try {
13
12
  console.warn('[useTransactionSigner] Real blockchain signing will not be available');
14
13
  }
15
14
 
15
+ // Simplified configuration matching web frontend
16
16
  interface PersSignerConfig {
17
- tenantId?: string; // Multi-tenant identifier
18
17
  ethersProviderUrl?: string; // Custom blockchain provider
19
- // webAuthnProvider: WebAuthnProvider; // Platform-specific WebAuthn provider
20
- // Optional overrides for advanced use cases
21
- apiUrl?: string; // Override default signer API URL (defaults to signer-api.pers.ninja)
22
- relyingPartyName?: string; // Override default relying party name (defaults to 'PERS Signer')
23
18
  }
24
19
 
20
+ // Simplified SDK interface matching web frontend
25
21
  interface PersSignerSDKInstance {
26
- authenticateUser: (userInfo: SignerUserInfo) => Promise<SignerAuthenticatedUser>;
27
- signPersTransaction: (user: SignerAuthenticatedUser, jwt: string) => Promise<SigningResult>;
22
+ authenticateWithJWT: (searchParams: URLSearchParams) => Promise<AuthenticationResult | null>;
23
+ authenticateUser: (identifier: string) => Promise<SignerAuthenticatedUser>;
24
+ signPersTransaction: (user: SignerAuthenticatedUser, transactionId: string) => Promise<SigningResult>;
28
25
  }
29
26
 
30
- interface SignerUserInfo {
31
- identifier: string;
32
- email?: string;
33
- id?: string;
34
- persAccessToken?: string;
27
+ // Types matching web frontend
28
+ interface AuthenticationResult {
29
+ user: SignerAuthenticatedUser;
30
+ isExpired: boolean;
35
31
  }
36
32
 
37
33
  interface SignerAuthenticatedUser {
@@ -43,37 +39,9 @@ interface SignerAuthenticatedUser {
43
39
  interface SigningResult {
44
40
  success: boolean;
45
41
  transactionHash?: string;
46
- signature?: string;
47
42
  error?: string;
48
43
  }
49
44
 
50
- /**
51
- * Utility to decode JWT and extract user information
52
- * This will be used to extract user data for the signer
53
- */
54
- const extractUserInfoFromJWT = async (jwt: string): Promise<{ userId?: string; email?: string; sub?: string }> => {
55
- try {
56
- // Dynamically import jwt-decode to avoid bundling issues
57
- const { jwtDecode } = await import('jwt-decode');
58
- const decoded = jwtDecode<{
59
- sub?: string;
60
- email?: string;
61
- user_id?: string;
62
- userId?: string;
63
- uid?: string;
64
- }>(jwt);
65
-
66
- return {
67
- userId: decoded.user_id || decoded.userId || decoded.uid || decoded.sub,
68
- email: decoded.email,
69
- sub: decoded.sub
70
- };
71
- } catch (error) {
72
- console.warn('[useTransactionSigner] Failed to decode JWT:', error);
73
- return {};
74
- }
75
- };
76
-
77
45
  // Define our own result type that's compatible with both SDK and our usage
78
46
  interface TransactionSigningResult {
79
47
  success: boolean;
@@ -88,24 +56,17 @@ const DEFAULT_ETHERS_PROVIDER = "https://sepolia.infura.io/v3/2781b4b5242343d5b0
88
56
  /**
89
57
  * React Native hook for blockchain transaction signing using PERS Signer SDK
90
58
  *
91
- * Provides WebAuthn-based transaction signing capabilities integrated with PERS ecosystem.
92
- * Automatically handles user authentication and transaction signing workflows.
59
+ * Simplified interface matching web frontend - uses high-level SDK methods
60
+ * that handle all complexity internally (tenant setup, project keys, etc.).
93
61
  *
94
62
  * @example
95
63
  * ```typescript
96
64
  * function TransactionComponent() {
97
- * const { signTransaction, initializeSigner, isSignerAvailable } = useTransactionSigner();
65
+ * const { signTransactionWithJWT, isSignerAvailable } = useTransactionSigner();
98
66
  *
99
- * useEffect(() => {
100
- * initializeSigner({
101
- * tenantId: 'my-tenant',
102
- * ethersProviderUrl: 'https://sepolia.infura.io/v3/...'
103
- * });
104
- * }, []);
105
- *
106
- * const handleSign = async () => {
67
+ * const handleSign = async (jwtFromRedemption: string) => {
107
68
  * try {
108
- * const result = await signTransaction('transaction-id-123');
69
+ * const result = await signTransactionWithJWT(jwtFromRedemption);
109
70
  * if (result.success) {
110
71
  * console.log('Transaction signed:', result.transactionHash);
111
72
  * }
@@ -123,294 +84,163 @@ const DEFAULT_ETHERS_PROVIDER = "https://sepolia.infura.io/v3/2781b4b5242343d5b0
123
84
  * ```
124
85
  */
125
86
  export const useTransactionSigner = () => {
126
- const { sdk, isInitialized, isAuthenticated, user, authProvider } = usePersSDK();
87
+ const { isInitialized, isAuthenticated, user } = usePersSDK();
127
88
  const [isSignerInitialized, setIsSignerInitialized] = useState(false);
128
89
  const signerSDKRef = useRef<PersSignerSDKInstance | null>(null);
129
90
 
130
- // Auto-initialize signer when user is authenticated and real SDK is available
91
+ // Auto-initialize signer SDK when PERS SDK is ready
131
92
  useEffect(() => {
132
- console.log('[useTransactionSigner] useEffect triggered:', {
133
- isInitialized,
134
- isAuthenticated,
135
- hasUser: !!user,
136
- isSignerInitialized,
137
- hasSDK: !!sdk,
138
- hasAuthProvider: !!authProvider,
139
- hasCreatePersSignerSDK: !!createPersSignerSDK
140
- });
93
+ console.log('[useTransactionSigner] Auto-initializing signer SDK...');
141
94
 
142
- if (isInitialized && isAuthenticated && user && !isSignerInitialized && sdk && authProvider) {
143
- console.log('[useTransactionSigner] Auto-initializing PERS transaction signer...');
144
-
145
- // Async auto-initialization function
146
- const autoInitialize = async () => {
147
- try {
148
- // Get configuration from the PERS SDK
149
- const sdkConfig = (sdk as any).config || {};
150
- console.log('[useTransactionSigner] SDK config:', sdkConfig);
151
-
152
- // Try to extract tenantId from JWT token if available
153
- let tenantIdFromJWT: string | undefined;
154
- let authToken: string | null | undefined;
155
- if (authProvider.getToken) {
156
- try {
157
- authToken = await authProvider.getToken();
158
- if (authToken) {
159
- // Decode JWT to get tenant information
160
- const tokenParts = authToken.split('.');
161
- if (tokenParts.length >= 2) {
162
- const payload = JSON.parse(atob(tokenParts[1]));
163
- console.log('[useTransactionSigner] JWT payload decoded:', payload);
164
-
165
- // Look for tenant ID in various possible fields
166
- tenantIdFromJWT = payload.tenantId || payload.tenant_id || payload.tenant;
167
- if (tenantIdFromJWT) {
168
- console.log('[useTransactionSigner] Found tenant ID in JWT:', tenantIdFromJWT);
169
- }
170
- }
171
- }
172
- } catch (e) {
173
- console.warn('[useTransactionSigner] Failed to decode JWT for tenant ID:', e);
174
- }
175
- }
176
-
177
- // For React Native, we should get tenantId from JWT, then user/auth context, then config
178
- const userTenantId = (user as any).tenantId || (user as any).tenant_id;
179
- const configTenantId = sdkConfig.tenantId || 'vq-demo';
180
- const tenantId = tenantIdFromJWT || userTenantId || configTenantId;
181
-
182
- console.log('[useTransactionSigner] Extracted tenant ID:', tenantId, 'from JWT:', tenantIdFromJWT, 'user:', userTenantId, 'or config:', configTenantId);
183
-
184
- // Don't use apiProjectKey since we'll get it via tenant initialization
185
- await initializeSigner({
186
- tenantId: tenantId,
187
- ethersProviderUrl: DEFAULT_ETHERS_PROVIDER
188
- });
189
- } catch (error) {
190
- console.error('[useTransactionSigner] Auto-initialization failed:', error);
191
- }
192
- };
193
-
194
- autoInitialize();
195
- }
196
- }, [isInitialized, isAuthenticated, user, isSignerInitialized, sdk, authProvider]);
197
-
198
- /**
199
- * Initialize the blockchain signer with configuration
200
- *
201
- * @param config - Signer configuration options
202
- * @param config.tenantId - Multi-tenant identifier for the signer
203
- * @param config.projectKey - PERS project API key for authentication
204
- * @param config.ethersProviderUrl - Custom blockchain provider URL
205
- *
206
- * @example
207
- * ```typescript
208
- * await initializeSigner({
209
- * tenantId: 'my-tenant-id',
210
- * projectKey: 'your-pers-api-key',
211
- * ethersProviderUrl: 'https://sepolia.infura.io/v3/your-key'
212
- * });
213
- * ```
214
- */
215
- const initializeSigner = useCallback(async (config?: {
216
- tenantId?: string;
217
- projectKey?: string;
218
- ethersProviderUrl?: string;
219
- }) => {
220
- if (!createPersSignerSDK) {
221
- throw new Error('PERS Signer SDK not available. Please ensure dependencies are properly installed.');
222
- }
95
+ const initializeSignerSDK = async () => {
96
+ if (!createPersSignerSDK) {
97
+ console.warn('[useTransactionSigner] Signer SDK not available');
98
+ return;
99
+ }
223
100
 
224
- try {
225
- console.log('[useTransactionSigner] Initializing PERS transaction signer...');
226
-
227
- // Configure the PERS service before creating SDK
228
- if (config?.projectKey) {
229
- console.log('[useTransactionSigner] Configuring PERS service with project key:', config.projectKey);
230
- try {
231
- // Import and configure the PERS service
232
- const { PersService } = await import('@explorins/pers-signer/react-native');
233
- console.log('[useTransactionSigner] PersService imported successfully');
234
- (PersService as any).configure({
235
- projectKey: config.projectKey
236
- });
237
- console.log('[useTransactionSigner] PERS service configured with project key');
238
-
239
- // Verify configuration
240
- const persConfig = (PersService as any).getConfig();
241
- console.log('[useTransactionSigner] PERS service config after configuration:', persConfig);
242
- } catch (configError) {
243
- console.error('[useTransactionSigner] Failed to configure PERS service:', configError);
244
- }
245
- } else if (config?.tenantId) {
246
- console.log('[useTransactionSigner] No project key provided, will initialize tenant with tenantId:', config.tenantId);
247
- try {
248
- // Import and initialize tenant environment to get project key
249
- const { PersService } = await import('@explorins/pers-signer/react-native');
250
- console.log('[useTransactionSigner] Initializing tenant for tenantId:', config.tenantId);
251
-
252
- // We need to set the auth token first for tenant initialization
253
- let authToken: string | null | undefined;
254
- if (authProvider) {
255
- try {
256
- authToken = await authProvider.getToken();
257
- if (authToken) {
258
- console.log('[useTransactionSigner] Setting auth token for tenant initialization');
259
- (PersService as any).configure({
260
- token: authToken
261
- });
262
- }
263
- } catch (tokenError) {
264
- console.warn('[useTransactionSigner] Could not get auth token for tenant initialization:', tokenError);
265
- }
266
- }
267
-
268
- await (PersService as any).initializeTenant(config.tenantId, authToken);
269
- console.log('[useTransactionSigner] Tenant initialized successfully');
270
-
271
- // Get tenant data to extract project key manually
272
- try {
273
- const tenantData = await (PersService as any).getTenantById(config.tenantId, authToken);
274
- console.log('[useTransactionSigner] Tenant data retrieved:', tenantData);
275
-
276
- // Extract and set project key manually if initializeTenant didn't set it
277
- const projectKey = tenantData.projectApiKey || tenantData.projectKey || tenantData.apiKey;
278
- if (projectKey) {
279
- console.log('[useTransactionSigner] Setting project key manually:', projectKey);
280
- (PersService as any).configure({
281
- token: authToken,
282
- projectKey: projectKey
283
- });
284
- } else {
285
- console.warn('[useTransactionSigner] No project key found in tenant data:', tenantData);
286
- }
287
- } catch (tenantError) {
288
- console.error('[useTransactionSigner] Failed to retrieve tenant data for project key:', tenantError);
289
- }
290
-
291
- // Verify configuration
292
- const persConfig = (PersService as any).getConfig();
293
- console.log('[useTransactionSigner] PERS service config after tenant initialization:', persConfig);
294
- } catch (configError) {
295
- console.error('[useTransactionSigner] Failed to initialize tenant:', configError);
296
- }
101
+ try {
102
+ console.log('[useTransactionSigner] Creating PERS Signer SDK...');
103
+ const signerSDK = await createPersSignerSDK({
104
+ ethersProviderUrl: DEFAULT_ETHERS_PROVIDER
105
+ });
106
+
107
+ signerSDKRef.current = signerSDK;
108
+ setIsSignerInitialized(true);
109
+ console.log('[useTransactionSigner] PERS Signer SDK initialized successfully');
110
+
111
+ } catch (error) {
112
+ console.error('[useTransactionSigner] Failed to initialize signer SDK:', error);
297
113
  }
298
-
299
- const signerSDK = await createPersSignerSDK({
300
- tenantId: config?.tenantId,
301
- ethersProviderUrl: config?.ethersProviderUrl || DEFAULT_ETHERS_PROVIDER
302
- });
303
-
304
- signerSDKRef.current = signerSDK;
305
- setIsSignerInitialized(true);
306
- console.log('[useTransactionSigner] PERS Signer SDK initialized successfully');
307
-
308
- } catch (error) {
309
- console.error('[useTransactionSigner] Failed to initialize transaction signer:', error);
310
- throw new Error(`Signer initialization failed: ${error}`);
114
+ };
115
+
116
+ if (!isSignerInitialized) {
117
+ initializeSignerSDK();
311
118
  }
312
- }, [authProvider]);
119
+ }, [isSignerInitialized]);
313
120
 
314
121
  /**
315
- * Sign a blockchain transaction using WebAuthn authentication
316
- *
317
- * @param jwt - JWT token containing transaction information and user context
318
- * @returns Promise resolving to signing result with transaction hash
122
+ * Sign a blockchain transaction using JWT token (matching web frontend pattern)
319
123
  *
320
- * @throws {Error} When SDK not initialized, user not authenticated, or signer not initialized
124
+ * This method replicates the web frontend flow:
125
+ * 1. Extract transaction info from JWT
126
+ * 2. Authenticate user with WebAuthn
127
+ * 3. Sign transaction with authenticated user
321
128
  *
322
- * @example
323
- * ```typescript
324
- * try {
325
- * const result = await signTransaction(jwtToken);
326
- * if (result.success) {
327
- * console.log('Signed transaction hash:', result.transactionHash);
328
- * } else {
329
- * console.error('Signing failed:', result.error);
330
- * }
331
- * } catch (error) {
332
- * console.error('Signing error:', error);
333
- * }
334
- * ```
129
+ * @param jwt - JWT token from redemption flow containing transaction ID
130
+ * @returns Promise resolving to signing result with transaction hash
335
131
  */
336
- const signTransaction = useCallback(async (jwt: string): Promise<TransactionSigningResult> => {
337
- if (!isInitialized || !sdk) {
338
- throw new Error('SDK not initialized. Call initialize() first.');
339
- }
340
- if (!isAuthenticated || !user) {
341
- throw new Error('User must be authenticated to sign transactions.');
342
- }
132
+ const signTransactionWithJWT = useCallback(async (jwt: string): Promise<TransactionSigningResult> => {
343
133
  if (!isSignerInitialized || !signerSDKRef.current) {
344
- throw new Error('Transaction signer not initialized. Call initializeSigner() first.');
134
+ throw new Error('Transaction signer not initialized');
345
135
  }
346
136
  if (!createPersSignerSDK) {
347
137
  throw new Error('PERS Signer SDK not available. Blockchain signing is not supported.');
348
138
  }
349
139
 
350
140
  try {
351
- console.log('[useTransactionSigner] Extracting user info from JWT for transaction signing...');
141
+ console.log('[useTransactionSigner] Starting JWT-based transaction signing...');
352
142
 
353
- // Extract user information from JWT
354
- const jwtUserInfo = await extractUserInfoFromJWT(jwt);
143
+ // Create URLSearchParams from JWT token (matching web frontend)
144
+ const searchParams = new URLSearchParams();
145
+ searchParams.set('jwt', jwt);
355
146
 
356
- // Create user info for signer, prioritizing current user data
357
- const currentUser = user as UserDTO;
358
- const signerUserInfo: SignerUserInfo = {
359
- identifier: currentUser.email || currentUser.id || jwtUserInfo.userId || jwtUserInfo.sub || `user-${Date.now()}`,
360
- email: currentUser.email || jwtUserInfo.email,
361
- id: currentUser.id || jwtUserInfo.userId
362
- };
147
+ // Step 1: Authenticate with JWT (handles tenant setup, project keys automatically)
148
+ console.log('[useTransactionSigner] Authenticating with JWT...');
149
+ const authResult = await signerSDKRef.current.authenticateWithJWT(searchParams);
363
150
 
364
- // Get PERS access token from the auth provider
365
- console.log('[useTransactionSigner] Auth provider found:', !!authProvider);
366
- const persAccessToken = authProvider ? await authProvider.getToken() : '';
367
- console.log('[useTransactionSigner] PERS access token extracted:', persAccessToken ? 'Token found' : 'No token');
151
+ if (!authResult) {
152
+ throw new Error('JWT authentication failed - no valid token found');
153
+ }
368
154
 
369
- // Authenticate user with blockchain signer and pass PERS token
370
- console.log('[useTransactionSigner] Authenticating user with signer:', signerUserInfo.identifier);
371
- const signerUser = await signerSDKRef.current.authenticateUser({
372
- ...signerUserInfo,
373
- persAccessToken: persAccessToken || undefined
374
- });
155
+ if (authResult.isExpired) {
156
+ throw new Error('JWT token is expired');
157
+ }
375
158
 
376
- // Sign the PERS transaction using JWT
377
- console.log('[useTransactionSigner] Signing PERS transaction with JWT containing transaction data');
378
- const result = await signerSDKRef.current.signPersTransaction(signerUser, jwt);
159
+ console.log('[useTransactionSigner] JWT authentication successful:', authResult.user.identifier);
379
160
 
380
- // Convert PERS SDK result to our format
381
- const convertedResult: TransactionSigningResult = {
382
- success: result.success || false,
383
- transactionHash: result.transactionHash,
384
- signature: result.signature,
385
- error: result.error
386
- };
161
+ // Step 2: Extract transaction ID from JWT
162
+ const jwtParts = jwt.split('.');
163
+ if (jwtParts.length !== 3) {
164
+ throw new Error('Invalid JWT token format');
165
+ }
387
166
 
388
- if (convertedResult.success) {
389
- console.log('[useTransactionSigner] Transaction signed successfully:', convertedResult.transactionHash);
390
- } else {
391
- console.warn('[useTransactionSigner] Transaction signing completed with warnings:', convertedResult.error);
167
+ const payload = JSON.parse(atob(jwtParts[1]));
168
+ const transactionId = payload.transactionId;
169
+
170
+ if (!transactionId) {
171
+ throw new Error('No transaction ID found in JWT token');
392
172
  }
393
173
 
394
- return convertedResult;
174
+ console.log('[useTransactionSigner] Found transaction ID:', transactionId);
175
+
176
+ // Step 3: Sign transaction using authenticated user
177
+ console.log('[useTransactionSigner] Signing transaction...');
178
+ const signingResult = await signerSDKRef.current.signPersTransaction(
179
+ authResult.user,
180
+ transactionId
181
+ );
182
+
183
+ console.log('[useTransactionSigner] Transaction signing result:', signingResult);
184
+
185
+ return {
186
+ success: signingResult.success,
187
+ transactionHash: signingResult.transactionHash,
188
+ error: signingResult.error
189
+ };
190
+
395
191
  } catch (error) {
396
- console.error('[useTransactionSigner] Failed to sign transaction:', error);
192
+ console.error('[useTransactionSigner] JWT transaction signing failed:', error);
397
193
  return {
398
194
  success: false,
399
195
  error: `Transaction signing failed: ${error}`
400
196
  };
401
197
  }
402
- }, [sdk, isInitialized, isAuthenticated, user, isSignerInitialized]);
198
+ }, [isSignerInitialized]);
199
+
200
+ /**
201
+ * Manual user authentication (fallback method)
202
+ */
203
+ const authenticateUser = useCallback(async (identifier: string): Promise<SignerAuthenticatedUser | null> => {
204
+ if (!isSignerInitialized || !signerSDKRef.current) {
205
+ throw new Error('Transaction signer not initialized');
206
+ }
207
+
208
+ try {
209
+ console.log('[useTransactionSigner] Manual user authentication:', identifier);
210
+ const user = await signerSDKRef.current.authenticateUser(identifier);
211
+ console.log('[useTransactionSigner] Manual authentication successful');
212
+ return user;
213
+ } catch (error) {
214
+ console.error('[useTransactionSigner] Manual authentication failed:', error);
215
+ return null;
216
+ }
217
+ }, [isSignerInitialized]);
218
+
219
+ /**
220
+ * Legacy method for backward compatibility
221
+ * @deprecated Use signTransactionWithJWT instead
222
+ */
223
+ const signTransaction = useCallback(async (jwt: string): Promise<TransactionSigningResult> => {
224
+ console.warn('[useTransactionSigner] signTransaction is deprecated, use signTransactionWithJWT');
225
+ return signTransactionWithJWT(jwt);
226
+ }, [signTransactionWithJWT]);
403
227
 
404
228
  return {
405
229
  /**
406
- * Sign a blockchain transaction with WebAuthn authentication
230
+ * Sign a blockchain transaction using JWT token (recommended method)
231
+ */
232
+ signTransactionWithJWT,
233
+
234
+ /**
235
+ * Legacy transaction signing method
236
+ * @deprecated Use signTransactionWithJWT instead
407
237
  */
408
238
  signTransaction,
409
239
 
410
240
  /**
411
- * Initialize the transaction signer with configuration
241
+ * Manual user authentication (for advanced use cases)
412
242
  */
413
- initializeSigner,
243
+ authenticateUser,
414
244
 
415
245
  /**
416
246
  * Whether the transaction signer has been initialized
@@ -420,7 +250,7 @@ export const useTransactionSigner = () => {
420
250
  /**
421
251
  * Whether transaction signing is available (all requirements met)
422
252
  */
423
- isSignerAvailable: isInitialized && isAuthenticated && isSignerInitialized,
253
+ isSignerAvailable: isSignerInitialized && !!createPersSignerSDK,
424
254
  };
425
255
  };
426
256
 
@@ -432,4 +262,4 @@ export type TransactionSignerHook = ReturnType<typeof useTransactionSigner>;
432
262
  /**
433
263
  * Export the transaction signing result type for external usage
434
264
  */
435
- export type { TransactionSigningResult };
265
+ export type { TransactionSigningResult, SignerAuthenticatedUser, AuthenticationResult };