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