@truly-you/trulyyou-web-sdk 0.1.16 → 0.1.18

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.
@@ -17,8 +17,14 @@ export declare class TrulyYouSDK {
17
17
  private getKeyIdByAuthFlowId;
18
18
  /**
19
19
  * Fetch app branding from SDK backend
20
+ * Returns true if authFlowId was successfully loaded
20
21
  */
21
22
  private fetchBranding;
23
+ /**
24
+ * Ensure authFlowId is loaded before proceeding with signing operations
25
+ * Throws error if authFlowId cannot be loaded
26
+ */
27
+ private ensureAuthFlowIdLoaded;
22
28
  /**
23
29
  * Generate QR code with app icon overlaid in center
24
30
  */
@@ -45,6 +51,7 @@ export declare class TrulyYouSDK {
45
51
  /**
46
52
  * Probe iframe to check if key exists in iframe's localStorage
47
53
  * Also checks backend to verify key exists and is active
54
+ * REQUIRES authFlowId to be loaded first
48
55
  */
49
56
  private probeIframeForKey;
50
57
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@truly-you/trulyyou-web-sdk",
3
- "version": "0.1.16",
3
+ "version": "0.1.18",
4
4
  "description": "TrulyYou Web SDK for secure authentication and payload signing",
5
5
  "type": "module",
6
6
  "main": "dist/index.esm.js",
@@ -63,9 +63,13 @@ export class TrulyYouSDK {
63
63
 
64
64
  /**
65
65
  * Fetch app branding from SDK backend
66
+ * Returns true if authFlowId was successfully loaded
66
67
  */
67
- private async fetchBranding(): Promise<void> {
68
- if (!this.authAppId) return
68
+ private async fetchBranding(): Promise<boolean> {
69
+ if (!this.authAppId) {
70
+ console.warn('[SDK]: Cannot fetch branding - authAppId not configured')
71
+ return false
72
+ }
69
73
 
70
74
  try {
71
75
  // Call SDK backend API directly
@@ -88,13 +92,37 @@ export class TrulyYouSDK {
88
92
  authFlowId: app.authFlowId
89
93
  }
90
94
  console.log('[SDK]: Branding and authFlowId fetched and cached:', this.brandingCache)
95
+ return !!app.authFlowId
91
96
  }
92
97
  }
98
+ console.warn('[SDK]: Failed to fetch branding - response not ok or missing app data')
99
+ return false
93
100
  } catch (error) {
94
101
  console.warn('[SDK]: Error fetching branding:', error)
102
+ return false
95
103
  }
96
104
  }
97
105
 
106
+ /**
107
+ * Ensure authFlowId is loaded before proceeding with signing operations
108
+ * Throws error if authFlowId cannot be loaded
109
+ */
110
+ private async ensureAuthFlowIdLoaded(): Promise<void> {
111
+ if (this.brandingCache?.authFlowId) {
112
+ console.log('[SDK]: authFlowId already loaded:', this.brandingCache.authFlowId)
113
+ return
114
+ }
115
+
116
+ console.log('[SDK]: authFlowId not yet loaded, fetching branding...')
117
+ const success = await this.fetchBranding()
118
+
119
+ if (!success || !this.brandingCache?.authFlowId) {
120
+ throw new Error('authFlowId is required for signing but not available. Please check that your app configuration includes an authFlowId.')
121
+ }
122
+
123
+ console.log('[SDK]: authFlowId loaded successfully:', this.brandingCache.authFlowId)
124
+ }
125
+
98
126
  /**
99
127
  * Generate QR code with app icon overlaid in center
100
128
  */
@@ -279,8 +307,17 @@ export class TrulyYouSDK {
279
307
  /**
280
308
  * Probe iframe to check if key exists in iframe's localStorage
281
309
  * Also checks backend to verify key exists and is active
310
+ * REQUIRES authFlowId to be loaded first
282
311
  */
283
312
  private async probeIframeForKey(): Promise<string | null> {
313
+ // Ensure authFlowId is loaded before probing
314
+ await this.ensureAuthFlowIdLoaded()
315
+
316
+ if (!this.brandingCache?.authFlowId) {
317
+ console.error('[SDK-PROBE]: authFlowId is required but not available after fetch')
318
+ return null
319
+ }
320
+
284
321
  return new Promise((resolve) => {
285
322
  try {
286
323
  const origin = window.location.origin
@@ -292,103 +329,58 @@ export class TrulyYouSDK {
292
329
  probeUrl.searchParams.set('probe', 'true')
293
330
  probeUrl.searchParams.set('origin', origin)
294
331
 
295
- // Add authFlowId if available from branding cache
296
- if (this.brandingCache?.authFlowId) {
297
- probeUrl.searchParams.set('authFlowId', this.brandingCache.authFlowId)
298
- console.log('[SDK-PROBE]: Adding authFlowId to probe:', this.brandingCache.authFlowId)
299
- } else {
300
- console.log('[SDK-PROBE]: No authFlowId available in cache, will check for any key')
301
- }
332
+ // Add authFlowId (guaranteed to exist now after ensureAuthFlowIdLoaded)
333
+ const authFlowId = this.brandingCache!.authFlowId!
334
+ probeUrl.searchParams.set('authFlowId', authFlowId)
335
+ console.log('[SDK-PROBE]: Adding authFlowId to probe:', authFlowId)
302
336
 
303
337
  iframe.src = probeUrl.toString()
304
-
305
- console.log('[SDK-PROBE]: Creating probe iframe with URL:', probeUrl.toString())
306
- console.log('[SDK-PROBE]: Current origin:', origin)
307
- console.log('[SDK-PROBE]: Frontend URL:', this.frontendUrl)
308
338
 
309
339
  let timeout: NodeJS.Timeout | undefined
310
- let globalTimeout: NodeJS.Timeout | undefined
311
340
  let backendCheckPromise: Promise<boolean> | null = null
312
- let globalListener: ((e: MessageEvent) => void) | undefined
313
341
 
314
342
  const cleanup = () => {
315
343
  window.removeEventListener('message', handleMessage)
316
- if (globalListener) {
317
- window.removeEventListener('message', globalListener)
318
- }
319
344
  if (iframe.parentNode) {
320
345
  iframe.parentNode.removeChild(iframe)
321
346
  }
322
347
  if (timeout) {
323
348
  clearTimeout(timeout)
324
349
  }
325
- if (globalTimeout) {
326
- clearTimeout(globalTimeout)
327
- }
328
350
  }
329
351
 
330
352
  const handleMessage = async (event: MessageEvent) => {
331
353
  const data = event.data as any
332
354
 
333
- // Log ALL messages during probe to debug
334
- console.log('[SDK-PROBE-DEBUG]: Message received - type:', data?.type, 'origin:', event.origin, 'data:', data)
335
-
336
355
  // Only process probe-related messages
337
356
  if (data?.type === 'KEY_CHECK_RESULT' || data?.type === 'KEY_CHECK_FAILED') {
338
- console.log('[SDK-PROBE]: Processing probe message:', event.data, 'from origin:', event.origin)
339
-
340
357
  try {
341
358
  const frontendOrigin = new URL(this.frontendUrl).origin
342
- console.log('[SDK-PROBE]: Checking origin - expected:', frontendOrigin, 'received:', event.origin)
343
359
  if (event.origin !== frontendOrigin) {
344
- console.log('[SDK-PROBE]: Origin mismatch, ignoring message')
345
360
  return
346
361
  }
347
-
348
362
  } catch (e) {
349
- console.log('[SDK-PROBE]: Origin check failed, using hostname fallback')
350
363
  if (!event.origin.includes(window.location.hostname)) {
351
- console.log('[SDK-PROBE]: Hostname not in origin, ignoring message')
352
364
  return
353
365
  }
354
366
  }
355
-
356
- console.log('[SDK-PROBE]: Message passed origin check, type:', data?.type)
357
367
 
358
368
  if (data?.type === 'KEY_CHECK_RESULT') {
359
- console.log('[SDK-PROBE]: KEY_CHECK_RESULT received - hasKey:', data.hasKey, 'keyId:', data.keyId)
360
369
  cleanup()
361
370
  if (data.hasKey && data.keyId) {
362
- // Key found in localStorage - it will be validated when used in /api/signatures/create
363
371
  resolve(data.keyId)
364
372
  } else {
365
373
  resolve(null)
366
374
  }
367
375
  } else if (data?.type === 'KEY_CHECK_FAILED') {
368
- console.log('[SDK-PROBE]: KEY_CHECK_FAILED received')
369
376
  cleanup()
370
377
  resolve(null)
371
378
  }
372
379
  }
373
380
  }
374
381
 
375
- console.log('[SDK-PROBE]: Registering message listener on window')
376
382
  window.addEventListener('message', handleMessage)
377
- console.log('[SDK-PROBE]: Message listener registered, appending iframe')
378
383
  document.body.appendChild(iframe)
379
- console.log('[SDK-PROBE]: Iframe appended to body, waiting for response...')
380
-
381
- // Also add a temporary global listener to see if ANY messages arrive
382
- globalListener = (e: MessageEvent) => {
383
- console.log('[SDK-PROBE-GLOBAL]: Any message received:', e.data, 'from:', e.origin)
384
- }
385
- window.addEventListener('message', globalListener)
386
- globalTimeout = setTimeout(() => {
387
- if (globalListener) {
388
- window.removeEventListener('message', globalListener)
389
- console.log('[SDK-PROBE-GLOBAL]: Removed temporary global listener')
390
- }
391
- }, 5000)
392
384
 
393
385
  // Timeout to allow for React hydration on sign page
394
386
  timeout = setTimeout(() => {
@@ -1705,7 +1697,7 @@ export class TrulyYouSDK {
1705
1697
  }
1706
1698
 
1707
1699
  // Step 2: Mobile device - use existing keyId if provided, otherwise probe
1708
- let keyId = existingKeyId
1700
+ let keyId: string | null | undefined = existingKeyId
1709
1701
 
1710
1702
  if (!keyId) {
1711
1703
  console.log('[SDK]: Mobile device detected, probing iframe for existing key...')
@@ -1743,6 +1735,9 @@ export class TrulyYouSDK {
1743
1735
  url: string,
1744
1736
  options: FetchOptions = {}
1745
1737
  ): Promise<FetchResult> {
1738
+ // Ensure authFlowId is loaded before any signing operations
1739
+ await this.ensureAuthFlowIdLoaded()
1740
+
1746
1741
  // Declare signatureId at function scope so it's accessible in catch block
1747
1742
  let signatureId: string | undefined = undefined
1748
1743
  try {