@glideidentity/web-client-sdk 4.4.10 → 5.0.1-beta.0

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.
Files changed (102) hide show
  1. package/README.md +167 -19
  2. package/dist/adapters/angular/index.js +0 -1
  3. package/dist/adapters/angular/phone-auth.service.d.ts +0 -18
  4. package/dist/adapters/angular/phone-auth.service.js +0 -26
  5. package/dist/adapters/react/index.d.ts +1 -1
  6. package/dist/adapters/react/index.js +0 -3
  7. package/dist/adapters/react/useClient.js +0 -1
  8. package/dist/adapters/react/usePhoneAuth.js +1 -16
  9. package/dist/adapters/vanilla/client.js +0 -1
  10. package/dist/adapters/vanilla/index.js +0 -1
  11. package/dist/adapters/vanilla/phone-auth.js +0 -31
  12. package/dist/adapters/vue/index.d.ts +1 -1
  13. package/dist/adapters/vue/index.js +0 -4
  14. package/dist/adapters/vue/useClient.js +0 -5
  15. package/dist/adapters/vue/usePhoneAuth.js +1 -20
  16. package/dist/browser/web-client-sdk.min.js +1 -1
  17. package/dist/browser/web-client-sdk.min.js.LICENSE.txt +1 -1
  18. package/dist/browser.d.ts +1 -1
  19. package/dist/browser.js +0 -6
  20. package/dist/core/client.js +0 -12
  21. package/dist/core/logger.js +1 -81
  22. package/dist/core/phone-auth/api-types.d.ts +0 -13
  23. package/dist/core/phone-auth/api-types.js +0 -83
  24. package/dist/core/phone-auth/client.js +27 -374
  25. package/dist/core/phone-auth/error-utils.js +1 -83
  26. package/dist/core/phone-auth/index.d.ts +1 -1
  27. package/dist/core/phone-auth/index.js +1 -3
  28. package/dist/core/phone-auth/status-types.d.ts +0 -78
  29. package/dist/core/phone-auth/status-types.js +0 -17
  30. package/dist/core/phone-auth/strategies/desktop.js +8 -126
  31. package/dist/core/phone-auth/strategies/index.d.ts +0 -4
  32. package/dist/core/phone-auth/strategies/index.js +0 -4
  33. package/dist/core/phone-auth/strategies/link.js +10 -88
  34. package/dist/core/phone-auth/strategies/ts43.d.ts +0 -19
  35. package/dist/core/phone-auth/strategies/ts43.js +2 -33
  36. package/dist/core/phone-auth/strategies/types.js +0 -4
  37. package/dist/core/phone-auth/type-guards.js +0 -131
  38. package/dist/core/phone-auth/types.js +0 -32
  39. package/dist/core/phone-auth/ui/mobile-debug-console.js +2 -28
  40. package/dist/core/phone-auth/ui/modal.d.ts +33 -55
  41. package/dist/core/phone-auth/ui/modal.js +889 -422
  42. package/dist/core/phone-auth/validation-utils.d.ts +0 -13
  43. package/dist/core/phone-auth/validation-utils.js +2 -81
  44. package/dist/core/version.js +1 -2
  45. package/dist/esm/adapters/angular/index.js +0 -1
  46. package/dist/esm/adapters/angular/phone-auth.service.d.ts +0 -18
  47. package/dist/esm/adapters/angular/phone-auth.service.js +0 -26
  48. package/dist/esm/adapters/react/index.d.ts +1 -1
  49. package/dist/esm/adapters/react/index.js +0 -3
  50. package/dist/esm/adapters/react/useClient.js +0 -1
  51. package/dist/esm/adapters/react/usePhoneAuth.js +1 -16
  52. package/dist/esm/adapters/vanilla/client.js +0 -1
  53. package/dist/esm/adapters/vanilla/index.js +0 -1
  54. package/dist/esm/adapters/vanilla/phone-auth.d.ts +0 -24
  55. package/dist/esm/adapters/vanilla/phone-auth.js +0 -31
  56. package/dist/esm/adapters/vue/index.d.ts +1 -1
  57. package/dist/esm/adapters/vue/index.js +0 -4
  58. package/dist/esm/adapters/vue/useClient.js +0 -5
  59. package/dist/esm/adapters/vue/usePhoneAuth.js +1 -20
  60. package/dist/esm/browser.d.ts +1 -1
  61. package/dist/esm/browser.js +0 -6
  62. package/dist/esm/core/client.d.ts +0 -10
  63. package/dist/esm/core/client.js +0 -12
  64. package/dist/esm/core/logger.d.ts +0 -53
  65. package/dist/esm/core/logger.js +1 -81
  66. package/dist/esm/core/phone-auth/api-types.d.ts +0 -328
  67. package/dist/esm/core/phone-auth/api-types.js +0 -83
  68. package/dist/esm/core/phone-auth/client.d.ts +0 -144
  69. package/dist/esm/core/phone-auth/client.js +28 -375
  70. package/dist/esm/core/phone-auth/error-utils.d.ts +0 -29
  71. package/dist/esm/core/phone-auth/error-utils.js +1 -83
  72. package/dist/esm/core/phone-auth/index.d.ts +1 -1
  73. package/dist/esm/core/phone-auth/index.js +2 -4
  74. package/dist/esm/core/phone-auth/status-types.d.ts +0 -78
  75. package/dist/esm/core/phone-auth/status-types.js +0 -17
  76. package/dist/esm/core/phone-auth/strategies/desktop.d.ts +0 -63
  77. package/dist/esm/core/phone-auth/strategies/desktop.js +8 -126
  78. package/dist/esm/core/phone-auth/strategies/index.d.ts +0 -4
  79. package/dist/esm/core/phone-auth/strategies/index.js +0 -4
  80. package/dist/esm/core/phone-auth/strategies/link.d.ts +0 -48
  81. package/dist/esm/core/phone-auth/strategies/link.js +10 -88
  82. package/dist/esm/core/phone-auth/strategies/ts43.d.ts +0 -19
  83. package/dist/esm/core/phone-auth/strategies/ts43.js +2 -33
  84. package/dist/esm/core/phone-auth/strategies/types.d.ts +0 -13
  85. package/dist/esm/core/phone-auth/strategies/types.js +0 -4
  86. package/dist/esm/core/phone-auth/type-guards.d.ts +0 -128
  87. package/dist/esm/core/phone-auth/type-guards.js +0 -131
  88. package/dist/esm/core/phone-auth/types.d.ts +0 -108
  89. package/dist/esm/core/phone-auth/types.js +0 -32
  90. package/dist/esm/core/phone-auth/ui/mobile-debug-console.d.ts +0 -4
  91. package/dist/esm/core/phone-auth/ui/mobile-debug-console.js +2 -28
  92. package/dist/esm/core/phone-auth/ui/modal.d.ts +27 -68
  93. package/dist/esm/core/phone-auth/ui/modal.js +889 -422
  94. package/dist/esm/core/phone-auth/validation-utils.d.ts +0 -44
  95. package/dist/esm/core/phone-auth/validation-utils.js +2 -80
  96. package/dist/esm/core/types.d.ts +0 -35
  97. package/dist/esm/core/version.js +1 -2
  98. package/dist/esm/index.d.ts +1 -1
  99. package/dist/esm/index.js +1 -9
  100. package/dist/index.d.ts +1 -1
  101. package/dist/index.js +0 -7
  102. package/package.json +2 -2
@@ -7,11 +7,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
- // Import API types for API communication
11
10
  import * as API from './api-types';
12
11
  import { BrowserError, BrowserErrorCode, BrowserName } from './types';
13
12
  import { PhoneAuthErrorCode, isPhoneAuthError, parseBackendError, getUserMessage, isUserError, serializeError, createErrorBreadcrumb } from './error-utils';
14
- import { validatePhoneNumber, validatePlmn, validateConsentData, validateNonce } from './validation-utils';
13
+ import { validatePhoneNumber, validatePlmn, validateNonce } from './validation-utils';
15
14
  import { LoggerFactory } from '../logger';
16
15
  import { DesktopHandler } from './strategies/desktop';
17
16
  import { LinkHandler } from './strategies/link';
@@ -22,36 +21,31 @@ export class PhoneAuthClient {
22
21
  this.crossDeviceActive = false;
23
22
  this.retryCount = 0;
24
23
  this.sessionCache = new Map();
25
- // Store base timeout for normal operations
26
24
  this.baseTimeout = config.timeout || 30000;
27
- // Default configuration with cross-device support
28
25
  this.config = {
29
26
  endpoints: {
30
27
  prepare: ((_a = config.endpoints) === null || _a === void 0 ? void 0 : _a.prepare) || '/api/magic-auth/prepare',
31
28
  process: ((_b = config.endpoints) === null || _b === void 0 ? void 0 : _b.process) || '/api/magic-auth/process',
32
- polling: (_c = config.endpoints) === null || _c === void 0 ? void 0 : _c.polling // Pass through the polling endpoint
29
+ polling: (_c = config.endpoints) === null || _c === void 0 ? void 0 : _c.polling
33
30
  },
34
31
  timeout: config.timeout || 30000,
35
- pollingInterval: config.pollingInterval || 2000, // Default 2 seconds
36
- maxPollingAttempts: config.maxPollingAttempts || 30, // Changed from 150 to 30 (1 minute total)
32
+ pollingInterval: config.pollingInterval || 2000,
33
+ maxPollingAttempts: config.maxPollingAttempts || 30,
37
34
  debug: config.debug || false,
38
35
  aggregatorId: config.aggregatorId || 'default',
39
36
  devtools: config.devtools
40
37
  };
41
38
  this.debug = this.config.debug;
42
- // Store callbacks
43
39
  this.callbacks = {
44
40
  onCrossDeviceDetected: config.onCrossDeviceDetected,
45
41
  onRetryAttempt: config.onRetryAttempt
46
42
  };
47
- // Initialize logger based on config
48
43
  this.logger = LoggerFactory.create({
49
44
  level: config.logLevel,
50
45
  prefix: '[PhoneAuth]',
51
46
  remote: config.remoteLogging,
52
47
  custom: config.logger
53
48
  });
54
- // Initialize developer tools if configured
55
49
  if (((_d = config.devtools) === null || _d === void 0 ? void 0 : _d.showMobileConsole) && typeof window !== 'undefined') {
56
50
  import('./ui/mobile-debug-console').then(({ MobileDebugConsole }) => {
57
51
  MobileDebugConsole.init();
@@ -60,32 +54,21 @@ export class PhoneAuthClient {
60
54
  console.error('[PhoneAuth] Failed to load mobile debug console:', err);
61
55
  });
62
56
  }
63
- // Set up session cache cleanup
64
57
  this.setupCacheCleanup();
65
58
  }
66
- /**
67
- * Get user-friendly error message using error utilities
68
- */
69
59
  getUserFriendlyMessage(error) {
70
60
  if (typeof error === 'string') {
71
- // For legacy string error codes
72
61
  return getUserMessage({ code: error });
73
62
  }
74
63
  return getUserMessage(error);
75
64
  }
76
- /**
77
- * Log error with proper context and sanitization
78
- */
79
65
  logError(error, context) {
80
- // Create breadcrumb for error tracking
81
66
  const breadcrumb = createErrorBreadcrumb(error);
82
- // Serialize error for logging (sanitized)
83
67
  const serialized = serializeError(error);
84
68
  if (this.debug || !isUserError(error)) {
85
69
  console.error('[PhoneAuth] Error:', Object.assign(Object.assign({}, serialized), { breadcrumb,
86
70
  context }));
87
71
  }
88
- // Log trace context for distributed tracing (if available)
89
72
  if (error.traceId) {
90
73
  console.debug('[PhoneAuth] Trace Context:', {
91
74
  traceId: error.traceId,
@@ -94,20 +77,11 @@ export class PhoneAuthClient {
94
77
  });
95
78
  }
96
79
  }
97
- /**
98
- * Check if the browser supports secure phone authentication
99
- */
100
80
  isSupported() {
101
- // Only check on client side
102
81
  if (typeof window === 'undefined')
103
82
  return false;
104
- // Check for the DigitalCredential constructor specifically
105
- // This is more accurate than checking credentials.get which exists for other credential types
106
83
  return 'DigitalCredential' in window;
107
84
  }
108
- /**
109
- * Get detailed browser support information
110
- */
111
85
  getBrowserSupportInfo() {
112
86
  if (typeof window === 'undefined') {
113
87
  return {
@@ -126,7 +100,6 @@ export class PhoneAuthClient {
126
100
  browser: isChrome ? BrowserName.CHROME : isEdge ? BrowserName.EDGE : BrowserName.OTHER
127
101
  };
128
102
  }
129
- // Provide specific guidance based on browser
130
103
  if (isChrome || isEdge) {
131
104
  return {
132
105
  supported: false,
@@ -143,16 +116,11 @@ export class PhoneAuthClient {
143
116
  message: 'Your browser doesn\'t support the Digital Credentials API. Please use Chrome or Edge with the #web-identity-digital-credentials flag enabled.'
144
117
  };
145
118
  }
146
- /**
147
- * Main verification method with silent retry support
148
- */
149
119
  verify(options) {
150
120
  return __awaiter(this, void 0, void 0, function* () {
151
- // Reset retry count for new verification
152
121
  this.retryCount = 0;
153
122
  this.lastRequest = options;
154
- const maxRetries = 2; // Default max retries
155
- // Try verification with silent retries
123
+ const maxRetries = 2;
156
124
  return this.verifyWithRetry(options, maxRetries);
157
125
  });
158
126
  }
@@ -160,55 +128,38 @@ export class PhoneAuthClient {
160
128
  return __awaiter(this, void 0, void 0, function* () {
161
129
  var _a, _b;
162
130
  try {
163
- // Step 1: Prepare the phone verification request
164
131
  const preparedRequest = yield this.preparePhoneRequest(options);
165
- // Step 2: Invoke secure prompt for user consent (always in UI mode for high-level API)
166
132
  const credentialResponse = yield this.invokeSecurePrompt(preparedRequest);
167
- // Check if headless result was returned (this shouldn't happen in high-level API)
168
133
  if (credentialResponse && typeof credentialResponse === 'object' && 'strategy' in credentialResponse) {
169
134
  throw this.createError(PhoneAuthErrorCode.INVALID_RESPONSE, 'Headless mode is not supported in authenticatePhoneNumber. Use preparePhoneRequest and invokeSecurePrompt directly for headless mode.');
170
135
  }
171
- // Step 3: Process the response through appropriate endpoint
172
136
  const credential = credentialResponse;
173
- // Validate use_case is provided for endpoint selection
174
137
  if (!options.use_case) {
175
138
  throw this.createError(PhoneAuthErrorCode.MISSING_PARAMETERS, 'use_case is required', { field: 'use_case' });
176
139
  }
177
140
  const result = options.use_case === API.USE_CASE.GET_PHONE_NUMBER
178
141
  ? yield this.getPhoneNumber(credential, preparedRequest.session)
179
142
  : yield this.verifyPhoneNumber(credential, preparedRequest.session);
180
- // Return the result directly - it's already the correct type
181
- // Cache successful result with session info for later use
182
143
  this.cacheSession(options, result);
183
144
  return result;
184
145
  }
185
146
  catch (error) {
186
147
  const authError = isPhoneAuthError(error) ? error : parseBackendError(error);
187
- // Check if we should retry (silent retry - don't throw yet)
188
- // Note: We cannot automatically retry USER_DENIED errors because the Digital Credentials API
189
- // requires user interaction (transient activation). Automatic retries would fail with
190
- // "The 'digital-credentials-get' feature requires transient activation" error.
191
148
  if (this.shouldRetry(authError) && this.retryCount < maxRetries) {
192
149
  this.retryCount++;
193
- // Notify about retry attempt (but don't show error to user)
194
150
  (_b = (_a = this.callbacks).onRetryAttempt) === null || _b === void 0 ? void 0 : _b.call(_a, this.retryCount, maxRetries);
195
151
  if (this.debug) {
196
152
  console.log(`[PhoneAuth] Retrying verification (attempt ${this.retryCount + 1}/${maxRetries + 1})`);
197
153
  }
198
- // Wait before retry
199
- yield this.delay(Math.min(1000 * Math.pow(2, this.retryCount - 1), 5000)); // Exponential backoff
200
- // Check cache for recent successful session
154
+ yield this.delay(Math.min(1000 * Math.pow(2, this.retryCount - 1), 5000));
201
155
  const cachedResult = this.getCachedSession(options);
202
156
  if (cachedResult) {
203
157
  if (this.debug)
204
158
  console.log('[PhoneAuth] Using cached session result');
205
159
  return cachedResult;
206
160
  }
207
- // Retry the verification
208
161
  return this.verifyWithRetry(options, maxRetries);
209
162
  }
210
- // All retries exhausted or non-retryable error - now throw
211
- // Add context
212
163
  authError.context = {
213
164
  step: 'complete',
214
165
  useCase: options.use_case,
@@ -217,15 +168,11 @@ export class PhoneAuthClient {
217
168
  attemptNumber: this.retryCount + 1,
218
169
  maxAttempts: maxRetries + 1
219
170
  };
220
- // Log error with proper sanitization
221
171
  this.logError(authError, { options });
222
- // Re-throw the structured error
223
172
  if (isPhoneAuthError(error)) {
224
- // If it already has context, throw as-is
225
173
  if (error.context) {
226
174
  throw error;
227
175
  }
228
- // Otherwise, create a new error with context
229
176
  const enhancedError = {
230
177
  code: authError.code,
231
178
  message: error.message,
@@ -250,67 +197,35 @@ export class PhoneAuthClient {
250
197
  }
251
198
  });
252
199
  }
253
- /**
254
- * High-level method to get phone number (complete flow)
255
- * Handles prepare, credential prompt, and get phone number in one call
256
- */
257
200
  getPhoneNumberComplete(options) {
258
201
  return __awaiter(this, void 0, void 0, function* () {
259
202
  return this.verify(Object.assign({ use_case: API.USE_CASE.GET_PHONE_NUMBER }, options));
260
203
  });
261
204
  }
262
- /**
263
- * High-level method to verify phone number (complete flow)
264
- * Handles prepare, credential prompt, and verification in one call
265
- */
266
205
  verifyPhoneNumberComplete(phoneNumber, options) {
267
206
  return __awaiter(this, void 0, void 0, function* () {
268
207
  return this.verify(Object.assign({ use_case: API.USE_CASE.VERIFY_PHONE_NUMBER, phone_number: phoneNumber }, options));
269
208
  });
270
209
  }
271
- /**
272
- * Step 1: Prepare phone verification request
273
- *
274
- * This method prepares a secure request for phone verification.
275
- * You can use this with your own backend or the glide-sdk-node.
276
- *
277
- * @example
278
- * ```typescript
279
- * const request = await phoneAuthClient.preparePhoneRequest({ useCase: 'GetPhoneNumber' });
280
- * // Handle the request with custom logic
281
- * ```
282
- */
283
210
  preparePhoneRequest(options) {
284
211
  return __awaiter(this, void 0, void 0, function* () {
285
212
  var _a, _b, _c;
286
- // Validate phone number if provided
287
213
  if (options.phone_number) {
288
214
  const phoneValidation = validatePhoneNumber(options.phone_number);
289
215
  if (!phoneValidation.valid) {
290
216
  throw this.createError(PhoneAuthErrorCode.INVALID_PHONE_NUMBER, phoneValidation.error, { field: 'phone_number' });
291
217
  }
292
218
  }
293
- // Validate PLMN if provided
294
219
  if (options.plmn) {
295
220
  const plmnValidation = validatePlmn(options.plmn);
296
221
  if (!plmnValidation.valid) {
297
222
  throw this.createError(PhoneAuthErrorCode.BAD_REQUEST, plmnValidation.error, { field: 'plmn' });
298
223
  }
299
224
  }
300
- // Validate consent data if provided
301
- if (options.consent_data) {
302
- const consentValidation = validateConsentData(options.consent_data);
303
- if (!consentValidation.valid) {
304
- throw this.createError(PhoneAuthErrorCode.BAD_REQUEST, consentValidation.error, { field: 'consent_data' });
305
- }
306
- }
307
- // Validate use_case is provided (unless only parent_session_id is given)
308
225
  if (!options.use_case && !(((_a = options.options) === null || _a === void 0 ? void 0 : _a.parent_session_id) && !options.phone_number && !options.plmn)) {
309
226
  throw this.createError(PhoneAuthErrorCode.MISSING_PARAMETERS, 'use_case is required', { field: 'use_case' });
310
227
  }
311
- // Validate required parameters based on use case
312
228
  if (!options.phone_number && !options.plmn && !((_b = options.options) === null || _b === void 0 ? void 0 : _b.parent_session_id)) {
313
- // Provide specific error message based on use case
314
229
  if (options.use_case === API.USE_CASE.GET_PHONE_NUMBER) {
315
230
  throw this.createError(PhoneAuthErrorCode.MISSING_PARAMETERS, 'PLMN (MCC/MNC) is required for GetPhoneNumber. Please provide carrier network information.', { field: 'plmn', useCase: 'GetPhoneNumber' });
316
231
  }
@@ -318,49 +233,36 @@ export class PhoneAuthClient {
318
233
  throw this.createError(PhoneAuthErrorCode.MISSING_PARAMETERS, 'Phone number is required for VerifyPhoneNumber', { field: 'phoneNumber', useCase: 'VerifyPhoneNumber' });
319
234
  }
320
235
  else {
321
- // Fallback for other use cases
322
236
  throw this.createError(PhoneAuthErrorCode.MISSING_PARAMETERS, 'Either phone number or PLMN (MCC/MNC) must be provided', { field: 'phoneNumber,plmn' });
323
237
  }
324
238
  }
325
- // Log parent session usage
326
239
  if (((_c = options.options) === null || _c === void 0 ? void 0 : _c.parent_session_id) && !options.phone_number && !options.plmn) {
327
240
  if (this.debug) {
328
241
  console.log('[PhoneAuth] Using parent_session_id: %s, use_case: %s', options.options.parent_session_id, options.use_case || 'not provided');
329
242
  }
330
243
  }
331
244
  const requestId = `web-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
332
- // Generate cryptographically secure nonce
333
245
  const nonce = btoa(String.fromCharCode(...crypto.getRandomValues(new Uint8Array(32))))
334
246
  .replace(/\+/g, '-')
335
247
  .replace(/\//g, '_')
336
248
  .replace(/=/g, '');
337
- // Validate generated nonce
338
249
  const nonceValidation = validateNonce(nonce);
339
250
  if (!nonceValidation.valid) {
340
251
  throw this.createError(PhoneAuthErrorCode.INTERNAL_SERVER_ERROR, 'Failed to generate valid nonce', { field: 'nonce' });
341
252
  }
342
- // Build properly typed request body according to API specification
343
- // Be permissive - backend will ignore extra fields if not needed
344
253
  const requestBody = {
345
- // Include use_case if provided (optional when parent_session_id is given)
346
254
  use_case: options.use_case,
347
- // Include phone_number if provided (backend ignores if not needed)
348
255
  phone_number: options.phone_number,
349
- // Include PLMN if provided
350
256
  plmn: options.plmn ? {
351
257
  mcc: options.plmn.mcc,
352
258
  mnc: options.plmn.mnc
353
259
  } : undefined,
354
- // Auto-generated fields (SDK always provides these)
355
260
  nonce: nonce,
356
261
  id: requestId,
357
- // Optional fields
358
- consent_data: options.consent_data,
359
262
  client_info: {
360
263
  user_agent: navigator.userAgent,
361
264
  platform: navigator.platform
362
265
  },
363
- // Advanced options (for desktop-mobile binding and future features)
364
266
  options: options.options
365
267
  };
366
268
  this.log('Preparing phone verification request', requestBody);
@@ -371,20 +273,15 @@ export class PhoneAuthClient {
371
273
  body: JSON.stringify(requestBody)
372
274
  });
373
275
  if (!response.ok) {
374
- // Try to get error details from response body
375
276
  let errorDetails = null;
376
277
  try {
377
278
  errorDetails = yield response.json();
378
- // Always include the HTTP status from the response
379
279
  errorDetails = Object.assign(Object.assign({}, errorDetails), { status: response.status });
380
280
  }
381
281
  catch (_d) {
382
- // If JSON parsing fails, use status text
383
282
  errorDetails = { status: response.status, statusText: response.statusText };
384
283
  }
385
- // Parse the backend error response (handles both structured and unstructured errors)
386
284
  const parsedError = parseBackendError(errorDetails);
387
- // Enhance with additional context
388
285
  parsedError.context = {
389
286
  step: 'prepare',
390
287
  useCase: options.use_case,
@@ -392,7 +289,6 @@ export class PhoneAuthClient {
392
289
  userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : undefined,
393
290
  url: typeof window !== 'undefined' ? window.location.href : undefined
394
291
  };
395
- // Add endpoint info
396
292
  parsedError.details = Object.assign(Object.assign({}, parsedError.details), { endpoint: 'prepare', status: response.status });
397
293
  throw parsedError;
398
294
  }
@@ -401,16 +297,12 @@ export class PhoneAuthClient {
401
297
  if (!data.authentication_strategy || !data.data || !data.session) {
402
298
  throw this.createError(PhoneAuthErrorCode.INVALID_RESPONSE, 'Invalid response format from backend');
403
299
  }
404
- // Return the full response as-is
405
- // The invoke method will handle it based on authentication_strategy
406
300
  return data;
407
301
  }
408
302
  catch (error) {
409
- // If it's already an AuthError, re-throw it
410
303
  if (this.isAuthError(error)) {
411
304
  throw error;
412
305
  }
413
- // Otherwise, wrap it as a network error
414
306
  throw this.createError(PhoneAuthErrorCode.NETWORK_ERROR, 'Failed to prepare verification request', {
415
307
  originalError: error,
416
308
  context: {
@@ -424,68 +316,16 @@ export class PhoneAuthClient {
424
316
  }
425
317
  });
426
318
  }
427
- /**
428
- * Step 2: Invoke secure prompt for user consent
429
- *
430
- * This method can work in two modes:
431
- * 1. **UI Mode (default)**: Shows built-in UI components (modals/buttons)
432
- * 2. **Headless Mode**: Returns raw data for custom UI implementation
433
- *
434
- * **Important**: This method automatically handles reactive objects from frameworks
435
- * like Vue.js and React by deep cloning the input. This ensures compatibility with
436
- * browser APIs that expect plain objects.
437
- *
438
- * @example UI Mode (shows modal/button)
439
- * ```typescript
440
- * // Shows SDK's built-in UI
441
- * const credential = await phoneAuth.invokeSecurePrompt(prepareResult);
442
- *
443
- * // Customize the UI
444
- * const credential = await phoneAuth.invokeSecurePrompt(prepareResult, {
445
- * modalOptions: {
446
- * title: 'Verify Your Identity',
447
- * buttonText: 'Continue with Verizon'
448
- * }
449
- * });
450
- * ```
451
- *
452
- * @example Extended Mode (returns control methods)
453
- * ```typescript
454
- * // Get control methods for custom implementation
455
- * const result = await phoneAuth.invokeSecurePrompt(prepareResult, {
456
- * executionMode: 'extended',
457
- * preventDefaultUI: true // Desktop: no modal
458
- * });
459
- *
460
- * if (result.strategy === 'desktop') {
461
- * // Show custom QR UI
462
- * showCustomQR(result.qr_code_data);
463
- * // Start polling
464
- * await result.start_polling();
465
- * }
466
- * ```
467
- *
468
- * @param prepareResponse - Response from prepare() with strategy and data
469
- * @param options - Control UI behavior and response type
470
- * @returns Credential or ExtendedResponse based on executionMode
471
- */
472
319
  invokeSecurePrompt(prepareResponse, options) {
473
320
  return __awaiter(this, void 0, void 0, function* () {
474
- // Deep clone to avoid issues with reactive objects (Vue/React)
475
- // This ensures we work with plain objects for browser APIs
476
- // Vue's reactivity system wraps objects in Proxies which can interfere
477
- // with browser APIs like Digital Credentials API that expect plain objects
478
321
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
479
- // Try structuredClone first (modern browsers), but catch errors and fallback to JSON method
480
322
  let plainResponse;
481
323
  try {
482
- // structuredClone might throw if object contains non-cloneable properties
483
324
  plainResponse = typeof structuredClone !== 'undefined'
484
325
  ? structuredClone(prepareResponse)
485
326
  : JSON.parse(JSON.stringify(prepareResponse));
486
327
  }
487
328
  catch (cloneError) {
488
- // Fallback to JSON method if structuredClone fails
489
329
  if (this.debug) {
490
330
  console.log('[PhoneAuth] structuredClone failed, using JSON fallback:', cloneError);
491
331
  }
@@ -495,16 +335,11 @@ export class PhoneAuthClient {
495
335
  console.log('[PhoneAuth] Session cache size:', this.sessionCache.size);
496
336
  console.log('[PhoneAuth] Retry count:', this.retryCount);
497
337
  console.log('[PhoneAuth] PrepareResponse received:', JSON.stringify(plainResponse, null, 2));
498
- // Treat options as InvokeOptions (the modern format)
499
- // Legacy DesktopAuthOptions properties will still work through property access
500
338
  const opts = options;
501
- // Get configuration from options - access properties directly
502
339
  const strategy = plainResponse.authentication_strategy;
503
340
  const preventDefaultUI = (_a = opts === null || opts === void 0 ? void 0 : opts.preventDefaultUI) !== null && _a !== void 0 ? _a : false;
504
341
  const executionMode = (_b = opts === null || opts === void 0 ? void 0 : opts.executionMode) !== null && _b !== void 0 ? _b : 'standard';
505
- // Handle based on authentication strategy
506
342
  if (plainResponse.authentication_strategy === API.AUTHENTICATION_STRATEGY.TS43) {
507
- // Check browser support for TS43 strategy which requires Digital Credentials API
508
343
  if (!this.isSupported()) {
509
344
  throw this.createError(PhoneAuthErrorCode.BROWSER_NOT_SUPPORTED, 'Your browser does not support the Digital Credentials API required for TS43 authentication');
510
345
  }
@@ -518,18 +353,13 @@ export class PhoneAuthClient {
518
353
  }
519
354
  };
520
355
  this.log('Invoking TS43 secure authentication prompt', secureCredentialRequest);
521
- // Function to trigger TS43 authentication
522
356
  const triggerTS43 = () => __awaiter(this, void 0, void 0, function* () {
523
357
  var _a, _b;
524
358
  try {
525
- // This is the browser API call for TS43
526
- // Cast to CredentialRequestOptions with digital field (TS43 specific)
527
359
  const credentialOptions = secureCredentialRequest;
528
360
  const credentialResponse = yield navigator.credentials.get(credentialOptions);
529
- // Type guard for Digital Credential response
530
361
  const digitalResponse = credentialResponse;
531
362
  if (!digitalResponse || !('data' in digitalResponse) || !digitalResponse.data) {
532
- // Check if this is likely due to the browser flag being disabled
533
363
  const supportInfo = this.getBrowserSupportInfo();
534
364
  if (supportInfo.browser === BrowserName.CHROME || supportInfo.browser === BrowserName.EDGE) {
535
365
  throw new Error(`Digital Credentials API returned no response. This usually means the browser feature flag is not enabled. Please ensure the ${supportInfo.helpUrl || '#web-identity-digital-credentials flag'} is set to "Enabled" (not "Default" or "Disabled") and restart your browser.`);
@@ -541,7 +371,6 @@ export class PhoneAuthClient {
541
371
  return credentialData.vp_token;
542
372
  }
543
373
  catch (error) {
544
- // Capture detailed browser error information
545
374
  const errorObj = error;
546
375
  const browserErrorDetails = {
547
376
  name: errorObj.name || 'UnknownError',
@@ -554,11 +383,9 @@ export class PhoneAuthClient {
554
383
  timestamp: new Date().toISOString(),
555
384
  userAgent: navigator.userAgent,
556
385
  url: window.location.href,
557
- // Include request details for debugging
558
386
  authentication_strategy: plainResponse.authentication_strategy,
559
387
  hasSession: !!plainResponse.session
560
388
  };
561
- // Handle specific browser errors
562
389
  if (errorObj.name === BrowserError.NOT_ALLOWED) {
563
390
  throw this.createError(PhoneAuthErrorCode.USER_DENIED, 'User denied the credential request or the request timed out', {
564
391
  originalError: error,
@@ -566,7 +393,6 @@ export class PhoneAuthClient {
566
393
  context: errorContext
567
394
  });
568
395
  }
569
- // NetworkError with code 19 specifically indicates user cancellation in Digital Credentials API
570
396
  if (errorObj.name === BrowserError.NETWORK && errorObj.code === BrowserErrorCode.USER_CANCELLED_DC_API) {
571
397
  throw this.createError(PhoneAuthErrorCode.USER_DENIED, 'Authentication cancelled by user', {
572
398
  originalError: error,
@@ -574,7 +400,6 @@ export class PhoneAuthClient {
574
400
  context: errorContext
575
401
  });
576
402
  }
577
- // NetworkError without code 19 is a real network error
578
403
  if (errorObj.name === BrowserError.NETWORK) {
579
404
  throw this.createError(PhoneAuthErrorCode.NETWORK_ERROR, 'Network error occurred while retrieving credentials', {
580
405
  originalError: error,
@@ -596,7 +421,6 @@ export class PhoneAuthClient {
596
421
  context: errorContext
597
422
  });
598
423
  }
599
- // Check for other cancellation patterns
600
424
  if (errorObj.name === BrowserError.ABORT ||
601
425
  ((_a = browserErrorDetails.message) === null || _a === void 0 ? void 0 : _a.includes('The operation was aborted')) ||
602
426
  ((_b = browserErrorDetails.message) === null || _b === void 0 ? void 0 : _b.includes('User cancelled'))) {
@@ -606,7 +430,6 @@ export class PhoneAuthClient {
606
430
  context: errorContext
607
431
  });
608
432
  }
609
- // For any other errors, capture all details
610
433
  throw this.createError(PhoneAuthErrorCode.INTERNAL_SERVER_ERROR, `Digital Credentials API error: ${errorObj.message || 'Unknown error'}`, {
611
434
  originalError: error,
612
435
  browserError: browserErrorDetails,
@@ -614,11 +437,6 @@ export class PhoneAuthClient {
614
437
  });
615
438
  }
616
439
  });
617
- // IMPORTANT: For TS43, we ALWAYS call the API directly without any modal
618
- // The Digital Credentials API provides its own OS-level UI (drawer/bottom sheet)
619
- // Adding our own modal would be redundant and confusing for users
620
- // Unlike Link (which needs a button for iOS App Clips), TS43 just needs direct invocation
621
- // Enhanced trigger function with callback support
622
440
  const enhancedTriggerTS43 = () => __awaiter(this, void 0, void 0, function* () {
623
441
  var _a, _b;
624
442
  try {
@@ -638,16 +456,11 @@ export class PhoneAuthClient {
638
456
  throw error;
639
457
  }
640
458
  });
641
- // TS43 always auto-triggers (no SDK UI ever)
642
- // The Digital Credentials API provides its own OS-level UI
643
- // Use a wrapper object to allow updating the promise reference
644
459
  const credentialWrapper = {
645
460
  promise: null
646
461
  };
647
462
  try {
648
- // Always try to trigger immediately
649
463
  const vpToken = yield enhancedTriggerTS43();
650
- // Convert to AuthCredential format
651
464
  credentialWrapper.promise = Promise.resolve({
652
465
  credential: typeof vpToken === 'string' ? vpToken : Object.values(vpToken)[0],
653
466
  session: plainResponse.session,
@@ -655,34 +468,25 @@ export class PhoneAuthClient {
655
468
  });
656
469
  }
657
470
  catch (error) {
658
- // If auto-trigger fails, create a rejected promise
659
471
  credentialWrapper.promise = Promise.reject(error);
660
472
  }
661
- // Handle based on execution mode
662
473
  if (executionMode === 'extended') {
663
- // Extended mode - return control methods
664
474
  const response = {
665
475
  strategy: 'ts43',
666
476
  session: plainResponse.session,
667
- credential: credentialWrapper.promise, // Initial value
668
- // Re-trigger credential request
477
+ credential: credentialWrapper.promise,
669
478
  trigger: () => __awaiter(this, void 0, void 0, function* () {
670
479
  const vpToken = yield enhancedTriggerTS43();
671
- // Update the credential promise in the wrapper
672
480
  credentialWrapper.promise = Promise.resolve({
673
481
  credential: typeof vpToken === 'string' ? vpToken : Object.values(vpToken)[0],
674
482
  session: plainResponse.session,
675
483
  authenticated: true
676
484
  });
677
- // Return void as per interface
678
485
  }),
679
486
  cancel: () => {
680
- // TS43 doesn't have a way to cancel once triggered
681
- // but we can reject the promise
682
487
  credentialWrapper.promise = Promise.reject(this.createError(PhoneAuthErrorCode.USER_DENIED, 'Authentication cancelled'));
683
488
  }
684
489
  };
685
- // Define credential as a getter that always returns the current promise
686
490
  Object.defineProperty(response, 'credential', {
687
491
  get() {
688
492
  return credentialWrapper.promise;
@@ -693,20 +497,15 @@ export class PhoneAuthClient {
693
497
  return response;
694
498
  }
695
499
  else {
696
- // Standard mode - just return credential
697
- // Wait for and return the credential
698
500
  const credential = yield credentialWrapper.promise;
699
- // Return in standard format
700
501
  return {
701
502
  [plainResponse.session.session_key]: credential.credential
702
503
  };
703
504
  }
704
505
  }
705
506
  else if (plainResponse.authentication_strategy === API.AUTHENTICATION_STRATEGY.DESKTOP) {
706
- // Desktop strategy - QR code based authentication
707
507
  const desktopData = plainResponse.data;
708
508
  const handler = new DesktopHandler();
709
- // Extract QR code data - convert to QRCodeData format for modal
710
509
  const qrCodeData = {
711
510
  iosQRCode: ((_c = desktopData.data) === null || _c === void 0 ? void 0 : _c.ios_qr_image) || desktopData.ios_qr_image ||
712
511
  ((_d = desktopData.data) === null || _d === void 0 ? void 0 : _d.qr_code_image) || desktopData.qr_code_image || desktopData.qr_code || '',
@@ -714,18 +513,15 @@ export class PhoneAuthClient {
714
513
  iosUrl: ((_f = desktopData.data) === null || _f === void 0 ? void 0 : _f.ios_url) || desktopData.ios_url,
715
514
  androidUrl: ((_g = desktopData.data) === null || _g === void 0 ? void 0 : _g.android_url) || desktopData.android_url
716
515
  };
717
- // Also keep snake_case format for extended response
718
516
  const qrCodeDataSnakeCase = {
719
517
  ios_qr_image: ((_h = desktopData.data) === null || _h === void 0 ? void 0 : _h.ios_qr_image) || desktopData.ios_qr_image,
720
518
  android_qr_image: ((_j = desktopData.data) === null || _j === void 0 ? void 0 : _j.android_qr_image) || desktopData.android_qr_image,
721
519
  qr_code: ((_k = desktopData.data) === null || _k === void 0 ? void 0 : _k.qr_code_image) || desktopData.qr_code_image || desktopData.qr_code,
722
520
  challenge: (_l = desktopData.data) === null || _l === void 0 ? void 0 : _l.challenge
723
521
  };
724
- // Polling options - gather from any options format
725
522
  const pollingEndpointToUse = (opts === null || opts === void 0 ? void 0 : opts.pollingEndpoint) ||
726
523
  ((_m = this.config.endpoints) === null || _m === void 0 ? void 0 : _m.polling);
727
524
  const pollingOptions = Object.assign(Object.assign({}, opts), { pollingEndpoint: pollingEndpointToUse, pollingInterval: (opts === null || opts === void 0 ? void 0 : opts.pollingInterval) || this.config.pollingInterval || 2000, maxPollingAttempts: (opts === null || opts === void 0 ? void 0 : opts.maxPollingAttempts) || this.config.maxPollingAttempts || 30, onQRCodeReady: undefined, onStatusUpdate: undefined });
728
- // Decide whether to show modal based on preventDefaultUI
729
525
  const showModal = !preventDefaultUI;
730
526
  let modal;
731
527
  let modalRef = undefined;
@@ -737,40 +533,27 @@ export class PhoneAuthClient {
737
533
  });
738
534
  if (showModal) {
739
535
  console.log('[Desktop] Creating modal with QR data:', qrCodeData);
740
- // Create and setup modal
741
536
  modal = new AuthModal(opts === null || opts === void 0 ? void 0 : opts.modalOptions, opts === null || opts === void 0 ? void 0 : opts.callbacks);
742
537
  modal.setCloseCallback(() => {
743
538
  this.log('Desktop QR modal closed by user, cancelling polling');
744
539
  handler.cancel();
745
540
  });
746
- // Add UI callbacks to polling options
747
541
  pollingOptions.onQRCodeReady = (qrData) => {
748
542
  console.log('[Desktop] onQRCodeReady callback triggered:', qrData);
749
543
  modal.showQRCode(qrData, 'Scan with your mobile device');
750
544
  };
751
545
  pollingOptions.onStatusUpdate = (status) => {
752
- if (status.status === 'pending') {
753
- modal.updateStatus('Waiting for authentication...');
754
- }
755
- else if (status.status === 'authenticated') {
756
- modal.updateStatus('Authentication successful!');
757
- setTimeout(() => modal.close(), 1500);
758
- }
759
- else if (status.status === 'expired') {
760
- modal.updateStatus('QR code expired', true);
761
- }
762
- else if (status.status === 'error') {
763
- modal.updateStatus('Authentication failed', true);
546
+ if (status.status === 'authenticated' ||
547
+ status.status === 'error' ||
548
+ status.status === 'expired') {
549
+ modal.close();
764
550
  }
765
551
  };
766
- // Note: We don't show the QR code here. It will be shown by the onQRCodeReady callback
767
- // that gets triggered immediately when handler.invoke() is called
768
552
  modalRef = modal;
769
553
  }
770
554
  else {
771
555
  console.log('[Desktop] Modal not shown - preventDefaultUI is true');
772
556
  }
773
- // Create credential promise
774
557
  const startPolling = () => handler.invoke(plainResponse, pollingOptions).then(result => {
775
558
  if (result.authenticated && result.credential) {
776
559
  return {
@@ -783,22 +566,13 @@ export class PhoneAuthClient {
783
566
  throw this.createError(PhoneAuthErrorCode.USER_DENIED, result.error || 'Desktop authentication failed');
784
567
  }
785
568
  });
786
- // Handle based on execution mode
787
569
  if (executionMode === 'extended') {
788
- // Extended mode - return control methods
789
- // Create a promise and always start polling immediately
790
570
  const credentialPromise = new Promise((resolve, reject) => {
791
- // Always start polling immediately in extended mode (consistent with Link)
792
- // This prevents the "unresolved promise trap" where developers might await
793
- // the credential before calling start_polling()
794
571
  startPolling()
795
572
  .then(resolve)
796
573
  .catch(reject);
797
574
  });
798
- // Create wrapped functions
799
575
  const wrappedStartPolling = () => __awaiter(this, void 0, void 0, function* () {
800
- // Polling has already started automatically in extended mode
801
- // This method just returns the existing credential promise for consistency
802
576
  return credentialPromise;
803
577
  });
804
578
  const wrappedStopPolling = () => {
@@ -811,9 +585,7 @@ export class PhoneAuthClient {
811
585
  handler.cleanup();
812
586
  if (modal)
813
587
  modal.close();
814
- // The credential promise will be rejected by the handler.cancel() call
815
588
  };
816
- // Create the response object with a getter for is_polling
817
589
  const response = {
818
590
  strategy: 'desktop',
819
591
  session: plainResponse.session,
@@ -823,10 +595,8 @@ export class PhoneAuthClient {
823
595
  start_polling: wrappedStartPolling,
824
596
  stop_polling: wrappedStopPolling,
825
597
  cancel: wrappedCancel,
826
- // This will be replaced with a getter
827
- is_polling: false // Initial value, will be overridden by getter
598
+ is_polling: false
828
599
  };
829
- // Define is_polling as a getter that returns current state
830
600
  Object.defineProperty(response, 'is_polling', {
831
601
  get() {
832
602
  return handler.isPolling();
@@ -837,11 +607,8 @@ export class PhoneAuthClient {
837
607
  return response;
838
608
  }
839
609
  else {
840
- // Standard mode - return credential when complete
841
- // Start polling and wait for result
842
610
  try {
843
611
  const credential = yield startPolling();
844
- // Extract session ID for compatibility
845
612
  let sessionId = 'default';
846
613
  if (desktopData && typeof desktopData === 'object') {
847
614
  if (desktopData.data && typeof desktopData.data === 'object') {
@@ -861,10 +628,8 @@ export class PhoneAuthClient {
861
628
  }
862
629
  }
863
630
  else if (plainResponse.authentication_strategy === API.AUTHENTICATION_STRATEGY.LINK) {
864
- // Link strategy - app-based authentication (iOS/Android)
865
631
  const linkData = plainResponse.data;
866
632
  const handler = new LinkHandler();
867
- // Create reusable trigger function that ONLY opens the App Clip
868
633
  const triggerLink = () => {
869
634
  var _a, _b;
870
635
  try {
@@ -884,12 +649,9 @@ export class PhoneAuthClient {
884
649
  });
885
650
  }
886
651
  };
887
- // Link always auto-opens the app (no SDK UI by default)
888
- // Open immediately to preserve user gesture context
889
652
  if ((opts === null || opts === void 0 ? void 0 : opts.autoTrigger) !== false) {
890
653
  triggerLink();
891
654
  }
892
- // Start polling in the background
893
655
  console.log('[PhoneAuth Client] Link polling config:', {
894
656
  fromOptions: opts === null || opts === void 0 ? void 0 : opts.pollingEndpoint,
895
657
  fromClientConfig: (_o = this.config.endpoints) === null || _o === void 0 ? void 0 : _o.polling,
@@ -903,15 +665,11 @@ export class PhoneAuthClient {
903
665
  onStatusUpdate: undefined
904
666
  };
905
667
  console.log('[PhoneAuth Client] Final Link polling options:', pollingOptions);
906
- // Handle based on execution mode
907
668
  if (executionMode === 'extended') {
908
- // Extended mode - return control methods and start polling immediately
909
669
  let pollingStarted = false;
910
670
  let pollingPromise = null;
911
- // Start polling immediately (Link always polls automatically unlike Desktop)
912
671
  pollingPromise = handler.invoke(plainResponse, pollingOptions);
913
672
  pollingStarted = true;
914
- // Create credential promise from the polling result
915
673
  const credentialPromise = pollingPromise.then(result => {
916
674
  if (result.authenticated && result.credential) {
917
675
  return {
@@ -924,10 +682,8 @@ export class PhoneAuthClient {
924
682
  throw this.createError(PhoneAuthErrorCode.USER_DENIED, result.error || 'Link authentication failed');
925
683
  }
926
684
  });
927
- // Function to restart polling (for retry scenarios)
928
685
  const startPolling = () => __awaiter(this, void 0, void 0, function* () {
929
686
  if (!pollingStarted) {
930
- // This is here for API consistency, but for Link it's already polling
931
687
  pollingStarted = true;
932
688
  if (!pollingPromise) {
933
689
  pollingPromise = handler.invoke(plainResponse, pollingOptions);
@@ -944,7 +700,6 @@ export class PhoneAuthClient {
944
700
  throw this.createError(PhoneAuthErrorCode.USER_DENIED, result.error || 'Link authentication failed');
945
701
  }
946
702
  }
947
- // If already polling, just return the existing promise result
948
703
  const result = yield pollingPromise;
949
704
  if (result.authenticated && result.credential) {
950
705
  return {
@@ -964,21 +719,15 @@ export class PhoneAuthClient {
964
719
  data: {
965
720
  app_url: linkData.url
966
721
  },
967
- // Re-open the app link
968
722
  trigger: triggerLink,
969
- // Polling control - now prevents double polling
970
723
  start_polling: startPolling,
971
724
  stop_polling: () => handler.cleanup(),
972
725
  cancel: () => {
973
726
  handler.cancel();
974
727
  handler.cleanup();
975
- // Note: For Link, polling is already started, so the promise
976
- // will be rejected by the handler.cancel() call
977
728
  },
978
- // This will be replaced with a getter
979
- is_polling: false // Initial value, will be overridden by getter
729
+ is_polling: false
980
730
  };
981
- // Define is_polling as a getter that returns current state
982
731
  Object.defineProperty(response, 'is_polling', {
983
732
  get() {
984
733
  return handler.isPolling();
@@ -989,7 +738,6 @@ export class PhoneAuthClient {
989
738
  return response;
990
739
  }
991
740
  else {
992
- // Standard mode - start polling immediately and wait for completion
993
741
  const credentialPromise = handler.invoke(plainResponse, pollingOptions).then(result => {
994
742
  if (result.authenticated && result.credential) {
995
743
  return {
@@ -1002,44 +750,28 @@ export class PhoneAuthClient {
1002
750
  throw this.createError(PhoneAuthErrorCode.USER_DENIED, result.error || 'Link authentication failed');
1003
751
  }
1004
752
  });
1005
- // Wait for credential and return in standard format
1006
753
  const credential = yield credentialPromise;
1007
754
  const aggregatorId = this.config.aggregatorId || 'default';
1008
755
  return { [aggregatorId]: credential.credential };
1009
756
  }
1010
757
  }
1011
758
  else {
1012
- // Unknown strategy
1013
759
  throw this.createError(PhoneAuthErrorCode.UNSUPPORTED_STRATEGY, `Unknown authentication strategy: ${plainResponse.authentication_strategy}`);
1014
760
  }
1015
761
  });
1016
762
  }
1017
- /**
1018
- * Step 3A: Get phone number from credential
1019
- *
1020
- * @example
1021
- * ```typescript
1022
- * const prepareResp = await phoneAuthClient.preparePhoneRequest({ useCase: 'GetPhoneNumber', plmn: {...} });
1023
- * const credential = await phoneAuthClient.invokeSecurePrompt(prepareResp);
1024
- * const result = await phoneAuthClient.getPhoneNumber(credential, prepareResp.session);
1025
- * console.log(result.phone_number); // +1234567890
1026
- * ```
1027
- */
1028
763
  getPhoneNumber(credentialResponse, session) {
1029
764
  return __awaiter(this, void 0, void 0, function* () {
1030
- // Extract credential string
1031
765
  const credentialString = this.extractCredentialString(credentialResponse);
1032
- // Build request body for GetPhoneNumber
1033
766
  const requestBody = {
1034
767
  session: session,
1035
768
  credential: credentialString,
1036
- use_case: API.USE_CASE.GET_PHONE_NUMBER // Required for server routing
769
+ use_case: API.USE_CASE.GET_PHONE_NUMBER
1037
770
  };
1038
- // Only show full details in debug mode, mask sensitive data otherwise
1039
771
  if (this.config.debug) {
1040
772
  this.log('Getting phone number from credential', {
1041
773
  session: session,
1042
- credential: credentialString ? credentialString.substring(0, 50) + '...' : 'undefined', // Show partial for debugging
774
+ credential: credentialString ? credentialString.substring(0, 50) + '...' : 'undefined',
1043
775
  endpoint: this.config.endpoints.process || '/api/phone-auth/process'
1044
776
  });
1045
777
  }
@@ -1083,35 +815,18 @@ export class PhoneAuthClient {
1083
815
  }
1084
816
  });
1085
817
  }
1086
- /**
1087
- * Step 3B: Verify phone number with credential
1088
- *
1089
- * @example
1090
- * ```typescript
1091
- * const prepareResp = await phoneAuthClient.preparePhoneRequest({
1092
- * useCase: 'VerifyPhoneNumber',
1093
- * phoneNumber: '+1234567890'
1094
- * });
1095
- * const credential = await phoneAuthClient.invokeSecurePrompt(prepareResp);
1096
- * const result = await phoneAuthClient.verifyPhoneNumber(credential, prepareResp.session);
1097
- * console.log(result.verified); // true
1098
- * ```
1099
- */
1100
818
  verifyPhoneNumber(credentialResponse, session) {
1101
819
  return __awaiter(this, void 0, void 0, function* () {
1102
- // Extract credential string
1103
820
  const credentialString = this.extractCredentialString(credentialResponse);
1104
- // Build request body for VerifyPhoneNumber
1105
821
  const requestBody = {
1106
822
  session: session,
1107
823
  credential: credentialString,
1108
- use_case: API.USE_CASE.VERIFY_PHONE_NUMBER // Required for server routing
824
+ use_case: API.USE_CASE.VERIFY_PHONE_NUMBER
1109
825
  };
1110
- // Only show full details in debug mode, mask sensitive data otherwise
1111
826
  if (this.config.debug) {
1112
827
  this.log('Verifying phone number with credential', {
1113
828
  session: session,
1114
- credential: credentialString ? credentialString.substring(0, 50) + '...' : 'undefined', // Show partial for debugging
829
+ credential: credentialString ? credentialString.substring(0, 50) + '...' : 'undefined',
1115
830
  endpoint: this.config.endpoints.process || '/api/phone-auth/process'
1116
831
  });
1117
832
  }
@@ -1158,53 +873,36 @@ export class PhoneAuthClient {
1158
873
  }
1159
874
  });
1160
875
  }
1161
- /**
1162
- * Helper to extract credential string from various formats
1163
- */
1164
876
  extractCredentialString(credentialResponse) {
1165
- // If already a string, return it
1166
877
  if (typeof credentialResponse === 'string') {
1167
878
  return credentialResponse;
1168
879
  }
1169
- // Extract from vp_token object - try configured aggregatorId first, then 'glide', then 'default'
1170
880
  let credential = credentialResponse[this.config.aggregatorId || 'glide'];
1171
- // Fallback to 'glide' if not found
1172
881
  if (!credential && credentialResponse['glide']) {
1173
882
  credential = credentialResponse['glide'];
1174
883
  }
1175
- // Fallback to 'default' if still not found
1176
884
  if (!credential && credentialResponse['default']) {
1177
885
  credential = credentialResponse['default'];
1178
886
  }
1179
- // If still not found, try to get the first available key
1180
887
  if (!credential) {
1181
888
  const keys = Object.keys(credentialResponse);
1182
889
  if (keys.length > 0) {
1183
890
  credential = credentialResponse[keys[0]];
1184
891
  }
1185
892
  }
1186
- // Convert array to string if needed
1187
893
  return Array.isArray(credential) ? credential[0] : credential;
1188
894
  }
1189
- /**
1190
- * Helper to extract error details from response
1191
- */
1192
895
  extractErrorDetails(response) {
1193
896
  return __awaiter(this, void 0, void 0, function* () {
1194
897
  try {
1195
898
  const errorData = yield response.json();
1196
- // Always include the HTTP status from the response
1197
- return Object.assign(Object.assign({}, errorData), { status: response.status // Ensure HTTP status is included
1198
- });
899
+ return Object.assign(Object.assign({}, errorData), { status: response.status });
1199
900
  }
1200
901
  catch (_a) {
1201
902
  return { status: response.status, statusText: response.statusText };
1202
903
  }
1203
904
  });
1204
905
  }
1205
- /**
1206
- * Fetch with timeout
1207
- */
1208
906
  fetchWithTimeout(url, options) {
1209
907
  return __awaiter(this, void 0, void 0, function* () {
1210
908
  const controller = new AbortController();
@@ -1217,25 +915,18 @@ export class PhoneAuthClient {
1217
915
  }
1218
916
  });
1219
917
  }
1220
- /**
1221
- * Create an AuthError
1222
- */
1223
918
  createError(code, message, details) {
1224
919
  const error = {
1225
920
  code,
1226
921
  message,
1227
- details: (details === null || details === void 0 ? void 0 : details.originalError) ? Object.assign(Object.assign({}, details), { originalError: undefined // Remove the original error object
1228
- }) : details
922
+ details: (details === null || details === void 0 ? void 0 : details.originalError) ? Object.assign(Object.assign({}, details), { originalError: undefined }) : details
1229
923
  };
1230
- // Add browser error details if present
1231
924
  if (details === null || details === void 0 ? void 0 : details.browserError) {
1232
925
  error.browserError = details.browserError;
1233
926
  }
1234
- // Add context if present
1235
927
  if (details === null || details === void 0 ? void 0 : details.context) {
1236
928
  error.context = details.context;
1237
929
  }
1238
- // Add other specific fields
1239
930
  if (details === null || details === void 0 ? void 0 : details.status) {
1240
931
  error.status = details.status;
1241
932
  }
@@ -1250,51 +941,37 @@ export class PhoneAuthClient {
1250
941
  }
1251
942
  return error;
1252
943
  }
1253
- /**
1254
- * Type guard for AuthError
1255
- */
1256
944
  isAuthError(error) {
1257
945
  return error && typeof error.code === 'string' && typeof error.message === 'string';
1258
946
  }
1259
- /**
1260
- * Debug logging
1261
- */
1262
947
  log(...args) {
1263
948
  if (this.debug) {
1264
949
  console.log('[PhoneAuth]', ...args);
1265
950
  }
1266
951
  }
1267
- /**
1268
- * Determine if an error should trigger a retry
1269
- */
1270
952
  shouldRetry(error) {
1271
953
  var _a, _b, _c;
1272
- // Don't retry on 4xx client errors (these are not transient)
1273
954
  if (error.status && error.status >= 400 && error.status < 500) {
1274
955
  return false;
1275
956
  }
1276
- // Don't retry on explicit user denial or unsupported browser
1277
- // USER_DENIED cannot be retried automatically because the Digital Credentials API
1278
- // requires user interaction (transient activation)
1279
957
  const nonRetryableCodes = [
1280
958
  'USER_DENIED',
1281
959
  'BROWSER_NOT_SUPPORTED',
1282
960
  'INVALID_PHONE_NUMBER',
1283
961
  'INVALID_PARAMETERS',
1284
962
  'MISSING_PARAMETERS',
1285
- 'UNPROCESSABLE_ENTITY', // 422 errors
1286
- 'USE_CASE_MISMATCH', // Use case validation errors
1287
- 'PHONE_NUMBER_MISMATCH', // Phone verification failures
1288
- 'VERIFICATION_FAILED', // Verification failures
1289
- 'INVALID_CREDENTIAL', // Bad credentials
1290
- 'CARRIER_NOT_ELIGIBLE', // Carrier not supported
1291
- 'SESSION_EXPIRED', // Session expired
1292
- 'INVALID_SESSION' // Invalid session
963
+ 'UNPROCESSABLE_ENTITY',
964
+ 'USE_CASE_MISMATCH',
965
+ 'PHONE_NUMBER_MISMATCH',
966
+ 'VERIFICATION_FAILED',
967
+ 'INVALID_CREDENTIAL',
968
+ 'CARRIER_NOT_ELIGIBLE',
969
+ 'SESSION_EXPIRED',
970
+ 'INVALID_SESSION'
1293
971
  ];
1294
972
  if (nonRetryableCodes.includes(error.code)) {
1295
973
  return false;
1296
974
  }
1297
- // Only retry on network and server errors (5xx)
1298
975
  const retryableCodes = [
1299
976
  'NETWORK_ERROR',
1300
977
  'REQUEST_TIMEOUT',
@@ -1303,7 +980,6 @@ export class PhoneAuthClient {
1303
980
  'BAD_GATEWAY',
1304
981
  'INTERNAL_SERVER_ERROR'
1305
982
  ];
1306
- // Also check for cross-device error types in details
1307
983
  const isCrossDeviceError = ((_a = error.details) === null || _a === void 0 ? void 0 : _a.errorType) && [
1308
984
  'CROSS_DEVICE_TIMEOUT',
1309
985
  'CROSS_DEVICE_CONNECTION_LOST',
@@ -1313,17 +989,13 @@ export class PhoneAuthClient {
1313
989
  isCrossDeviceError ||
1314
990
  (((_b = error.browserError) === null || _b === void 0 ? void 0 : _b.name) === 'NetworkError' && ((_c = error.browserError) === null || _c === void 0 ? void 0 : _c.code) !== 19);
1315
991
  }
1316
- /**
1317
- * Analyze and enhance errors specific to cross-device flows
1318
- */
1319
992
  analyzeCrossDeviceError(error, prepareResponse) {
1320
993
  var _a;
1321
994
  const errorObj = error;
1322
- // Check for specific cross-device error patterns
1323
995
  const isCrossDeviceTimeout = (errorObj.name === 'AbortError' && this.crossDeviceActive) ||
1324
996
  (((_a = errorObj.message) === null || _a === void 0 ? void 0 : _a.includes('timeout')) && this.crossDeviceActive);
1325
997
  const isCrossDeviceNetworkIssue = errorObj.name === 'NetworkError' &&
1326
- errorObj.code !== 19 && // Not user cancellation
998
+ errorObj.code !== 19 &&
1327
999
  this.crossDeviceActive;
1328
1000
  if (isCrossDeviceTimeout) {
1329
1001
  return {
@@ -1351,12 +1023,8 @@ export class PhoneAuthClient {
1351
1023
  browserError: error.browserError
1352
1024
  };
1353
1025
  }
1354
- // Return the original error if not cross-device specific
1355
1026
  return error;
1356
1027
  }
1357
- /**
1358
- * Cache successful session for retry scenarios
1359
- */
1360
1028
  cacheSession(options, result) {
1361
1029
  const cacheKey = this.getCacheKey(options);
1362
1030
  this.sessionCache.set(cacheKey, {
@@ -1364,15 +1032,11 @@ export class PhoneAuthClient {
1364
1032
  data: result
1365
1033
  });
1366
1034
  }
1367
- /**
1368
- * Retrieve cached session if available and recent
1369
- */
1370
1035
  getCachedSession(options) {
1371
1036
  const cacheKey = this.getCacheKey(options);
1372
1037
  const cached = this.sessionCache.get(cacheKey);
1373
1038
  if (!cached)
1374
1039
  return null;
1375
- // Cache valid for 5 minutes
1376
1040
  const cacheValidMs = 5 * 60 * 1000;
1377
1041
  if (Date.now() - cached.timestamp > cacheValidMs) {
1378
1042
  this.sessionCache.delete(cacheKey);
@@ -1380,22 +1044,14 @@ export class PhoneAuthClient {
1380
1044
  }
1381
1045
  return cached.data;
1382
1046
  }
1383
- /**
1384
- * Generate cache key for session storage
1385
- */
1386
1047
  getCacheKey(options) {
1387
1048
  var _a, _b;
1388
1049
  return `${options.use_case}-${options.phone_number || 'no-phone'}-${((_a = options.plmn) === null || _a === void 0 ? void 0 : _a.mcc) || ''}-${((_b = options.plmn) === null || _b === void 0 ? void 0 : _b.mnc) || ''}`;
1389
1050
  }
1390
- /**
1391
- * Set up periodic cache cleanup
1392
- */
1393
1051
  setupCacheCleanup() {
1394
- // Only run cleanup in browser environment (not during SSR)
1395
1052
  if (typeof window === 'undefined') {
1396
1053
  return;
1397
1054
  }
1398
- // Clean up expired cache entries every minute
1399
1055
  setInterval(() => {
1400
1056
  const now = Date.now();
1401
1057
  const cacheValidMs = 5 * 60 * 1000;
@@ -1406,9 +1062,6 @@ export class PhoneAuthClient {
1406
1062
  }
1407
1063
  }, 60000);
1408
1064
  }
1409
- /**
1410
- * Utility delay function
1411
- */
1412
1065
  delay(ms) {
1413
1066
  return new Promise(resolve => setTimeout(resolve, ms));
1414
1067
  }