@glideidentity/web-client-sdk 5.0.0 → 5.0.1

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
@@ -7,7 +7,6 @@ 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';
@@ -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,60 +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 use_case is provided (unless only parent_session_id is given)
301
225
  if (!options.use_case && !(((_a = options.options) === null || _a === void 0 ? void 0 : _a.parent_session_id) && !options.phone_number && !options.plmn)) {
302
226
  throw this.createError(PhoneAuthErrorCode.MISSING_PARAMETERS, 'use_case is required', { field: 'use_case' });
303
227
  }
304
- // Validate required parameters based on use case
305
228
  if (!options.phone_number && !options.plmn && !((_b = options.options) === null || _b === void 0 ? void 0 : _b.parent_session_id)) {
306
- // Provide specific error message based on use case
307
229
  if (options.use_case === API.USE_CASE.GET_PHONE_NUMBER) {
308
230
  throw this.createError(PhoneAuthErrorCode.MISSING_PARAMETERS, 'PLMN (MCC/MNC) is required for GetPhoneNumber. Please provide carrier network information.', { field: 'plmn', useCase: 'GetPhoneNumber' });
309
231
  }
@@ -311,48 +233,36 @@ export class PhoneAuthClient {
311
233
  throw this.createError(PhoneAuthErrorCode.MISSING_PARAMETERS, 'Phone number is required for VerifyPhoneNumber', { field: 'phoneNumber', useCase: 'VerifyPhoneNumber' });
312
234
  }
313
235
  else {
314
- // Fallback for other use cases
315
236
  throw this.createError(PhoneAuthErrorCode.MISSING_PARAMETERS, 'Either phone number or PLMN (MCC/MNC) must be provided', { field: 'phoneNumber,plmn' });
316
237
  }
317
238
  }
318
- // Log parent session usage
319
239
  if (((_c = options.options) === null || _c === void 0 ? void 0 : _c.parent_session_id) && !options.phone_number && !options.plmn) {
320
240
  if (this.debug) {
321
241
  console.log('[PhoneAuth] Using parent_session_id: %s, use_case: %s', options.options.parent_session_id, options.use_case || 'not provided');
322
242
  }
323
243
  }
324
244
  const requestId = `web-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
325
- // Generate cryptographically secure nonce
326
245
  const nonce = btoa(String.fromCharCode(...crypto.getRandomValues(new Uint8Array(32))))
327
246
  .replace(/\+/g, '-')
328
247
  .replace(/\//g, '_')
329
248
  .replace(/=/g, '');
330
- // Validate generated nonce
331
249
  const nonceValidation = validateNonce(nonce);
332
250
  if (!nonceValidation.valid) {
333
251
  throw this.createError(PhoneAuthErrorCode.INTERNAL_SERVER_ERROR, 'Failed to generate valid nonce', { field: 'nonce' });
334
252
  }
335
- // Build properly typed request body according to API specification
336
- // Be permissive - backend will ignore extra fields if not needed
337
253
  const requestBody = {
338
- // Include use_case if provided (optional when parent_session_id is given)
339
254
  use_case: options.use_case,
340
- // Include phone_number if provided (backend ignores if not needed)
341
255
  phone_number: options.phone_number,
342
- // Include PLMN if provided
343
256
  plmn: options.plmn ? {
344
257
  mcc: options.plmn.mcc,
345
258
  mnc: options.plmn.mnc
346
259
  } : undefined,
347
- // Auto-generated fields (SDK always provides these)
348
260
  nonce: nonce,
349
261
  id: requestId,
350
- // Optional fields
351
262
  client_info: {
352
263
  user_agent: navigator.userAgent,
353
264
  platform: navigator.platform
354
265
  },
355
- // Advanced options (for desktop-mobile binding and future features)
356
266
  options: options.options
357
267
  };
358
268
  this.log('Preparing phone verification request', requestBody);
@@ -363,20 +273,15 @@ export class PhoneAuthClient {
363
273
  body: JSON.stringify(requestBody)
364
274
  });
365
275
  if (!response.ok) {
366
- // Try to get error details from response body
367
276
  let errorDetails = null;
368
277
  try {
369
278
  errorDetails = yield response.json();
370
- // Always include the HTTP status from the response
371
279
  errorDetails = Object.assign(Object.assign({}, errorDetails), { status: response.status });
372
280
  }
373
281
  catch (_d) {
374
- // If JSON parsing fails, use status text
375
282
  errorDetails = { status: response.status, statusText: response.statusText };
376
283
  }
377
- // Parse the backend error response (handles both structured and unstructured errors)
378
284
  const parsedError = parseBackendError(errorDetails);
379
- // Enhance with additional context
380
285
  parsedError.context = {
381
286
  step: 'prepare',
382
287
  useCase: options.use_case,
@@ -384,7 +289,6 @@ export class PhoneAuthClient {
384
289
  userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : undefined,
385
290
  url: typeof window !== 'undefined' ? window.location.href : undefined
386
291
  };
387
- // Add endpoint info
388
292
  parsedError.details = Object.assign(Object.assign({}, parsedError.details), { endpoint: 'prepare', status: response.status });
389
293
  throw parsedError;
390
294
  }
@@ -393,16 +297,12 @@ export class PhoneAuthClient {
393
297
  if (!data.authentication_strategy || !data.data || !data.session) {
394
298
  throw this.createError(PhoneAuthErrorCode.INVALID_RESPONSE, 'Invalid response format from backend');
395
299
  }
396
- // Return the full response as-is
397
- // The invoke method will handle it based on authentication_strategy
398
300
  return data;
399
301
  }
400
302
  catch (error) {
401
- // If it's already an AuthError, re-throw it
402
303
  if (this.isAuthError(error)) {
403
304
  throw error;
404
305
  }
405
- // Otherwise, wrap it as a network error
406
306
  throw this.createError(PhoneAuthErrorCode.NETWORK_ERROR, 'Failed to prepare verification request', {
407
307
  originalError: error,
408
308
  context: {
@@ -416,68 +316,16 @@ export class PhoneAuthClient {
416
316
  }
417
317
  });
418
318
  }
419
- /**
420
- * Step 2: Invoke secure prompt for user consent
421
- *
422
- * This method can work in two modes:
423
- * 1. **UI Mode (default)**: Shows built-in UI components (modals/buttons)
424
- * 2. **Headless Mode**: Returns raw data for custom UI implementation
425
- *
426
- * **Important**: This method automatically handles reactive objects from frameworks
427
- * like Vue.js and React by deep cloning the input. This ensures compatibility with
428
- * browser APIs that expect plain objects.
429
- *
430
- * @example UI Mode (shows modal/button)
431
- * ```typescript
432
- * // Shows SDK's built-in UI
433
- * const credential = await phoneAuth.invokeSecurePrompt(prepareResult);
434
- *
435
- * // Customize the UI
436
- * const credential = await phoneAuth.invokeSecurePrompt(prepareResult, {
437
- * modalOptions: {
438
- * title: 'Verify Your Identity',
439
- * buttonText: 'Continue with Verizon'
440
- * }
441
- * });
442
- * ```
443
- *
444
- * @example Extended Mode (returns control methods)
445
- * ```typescript
446
- * // Get control methods for custom implementation
447
- * const result = await phoneAuth.invokeSecurePrompt(prepareResult, {
448
- * executionMode: 'extended',
449
- * preventDefaultUI: true // Desktop: no modal
450
- * });
451
- *
452
- * if (result.strategy === 'desktop') {
453
- * // Show custom QR UI
454
- * showCustomQR(result.qr_code_data);
455
- * // Start polling
456
- * await result.start_polling();
457
- * }
458
- * ```
459
- *
460
- * @param prepareResponse - Response from prepare() with strategy and data
461
- * @param options - Control UI behavior and response type
462
- * @returns Credential or ExtendedResponse based on executionMode
463
- */
464
319
  invokeSecurePrompt(prepareResponse, options) {
465
320
  return __awaiter(this, void 0, void 0, function* () {
466
- // Deep clone to avoid issues with reactive objects (Vue/React)
467
- // This ensures we work with plain objects for browser APIs
468
- // Vue's reactivity system wraps objects in Proxies which can interfere
469
- // with browser APIs like Digital Credentials API that expect plain objects
470
321
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
471
- // Try structuredClone first (modern browsers), but catch errors and fallback to JSON method
472
322
  let plainResponse;
473
323
  try {
474
- // structuredClone might throw if object contains non-cloneable properties
475
324
  plainResponse = typeof structuredClone !== 'undefined'
476
325
  ? structuredClone(prepareResponse)
477
326
  : JSON.parse(JSON.stringify(prepareResponse));
478
327
  }
479
328
  catch (cloneError) {
480
- // Fallback to JSON method if structuredClone fails
481
329
  if (this.debug) {
482
330
  console.log('[PhoneAuth] structuredClone failed, using JSON fallback:', cloneError);
483
331
  }
@@ -487,16 +335,11 @@ export class PhoneAuthClient {
487
335
  console.log('[PhoneAuth] Session cache size:', this.sessionCache.size);
488
336
  console.log('[PhoneAuth] Retry count:', this.retryCount);
489
337
  console.log('[PhoneAuth] PrepareResponse received:', JSON.stringify(plainResponse, null, 2));
490
- // Treat options as InvokeOptions (the modern format)
491
- // Legacy DesktopAuthOptions properties will still work through property access
492
338
  const opts = options;
493
- // Get configuration from options - access properties directly
494
339
  const strategy = plainResponse.authentication_strategy;
495
340
  const preventDefaultUI = (_a = opts === null || opts === void 0 ? void 0 : opts.preventDefaultUI) !== null && _a !== void 0 ? _a : false;
496
341
  const executionMode = (_b = opts === null || opts === void 0 ? void 0 : opts.executionMode) !== null && _b !== void 0 ? _b : 'standard';
497
- // Handle based on authentication strategy
498
342
  if (plainResponse.authentication_strategy === API.AUTHENTICATION_STRATEGY.TS43) {
499
- // Check browser support for TS43 strategy which requires Digital Credentials API
500
343
  if (!this.isSupported()) {
501
344
  throw this.createError(PhoneAuthErrorCode.BROWSER_NOT_SUPPORTED, 'Your browser does not support the Digital Credentials API required for TS43 authentication');
502
345
  }
@@ -510,18 +353,13 @@ export class PhoneAuthClient {
510
353
  }
511
354
  };
512
355
  this.log('Invoking TS43 secure authentication prompt', secureCredentialRequest);
513
- // Function to trigger TS43 authentication
514
356
  const triggerTS43 = () => __awaiter(this, void 0, void 0, function* () {
515
357
  var _a, _b;
516
358
  try {
517
- // This is the browser API call for TS43
518
- // Cast to CredentialRequestOptions with digital field (TS43 specific)
519
359
  const credentialOptions = secureCredentialRequest;
520
360
  const credentialResponse = yield navigator.credentials.get(credentialOptions);
521
- // Type guard for Digital Credential response
522
361
  const digitalResponse = credentialResponse;
523
362
  if (!digitalResponse || !('data' in digitalResponse) || !digitalResponse.data) {
524
- // Check if this is likely due to the browser flag being disabled
525
363
  const supportInfo = this.getBrowserSupportInfo();
526
364
  if (supportInfo.browser === BrowserName.CHROME || supportInfo.browser === BrowserName.EDGE) {
527
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.`);
@@ -533,7 +371,6 @@ export class PhoneAuthClient {
533
371
  return credentialData.vp_token;
534
372
  }
535
373
  catch (error) {
536
- // Capture detailed browser error information
537
374
  const errorObj = error;
538
375
  const browserErrorDetails = {
539
376
  name: errorObj.name || 'UnknownError',
@@ -546,11 +383,9 @@ export class PhoneAuthClient {
546
383
  timestamp: new Date().toISOString(),
547
384
  userAgent: navigator.userAgent,
548
385
  url: window.location.href,
549
- // Include request details for debugging
550
386
  authentication_strategy: plainResponse.authentication_strategy,
551
387
  hasSession: !!plainResponse.session
552
388
  };
553
- // Handle specific browser errors
554
389
  if (errorObj.name === BrowserError.NOT_ALLOWED) {
555
390
  throw this.createError(PhoneAuthErrorCode.USER_DENIED, 'User denied the credential request or the request timed out', {
556
391
  originalError: error,
@@ -558,7 +393,6 @@ export class PhoneAuthClient {
558
393
  context: errorContext
559
394
  });
560
395
  }
561
- // NetworkError with code 19 specifically indicates user cancellation in Digital Credentials API
562
396
  if (errorObj.name === BrowserError.NETWORK && errorObj.code === BrowserErrorCode.USER_CANCELLED_DC_API) {
563
397
  throw this.createError(PhoneAuthErrorCode.USER_DENIED, 'Authentication cancelled by user', {
564
398
  originalError: error,
@@ -566,7 +400,6 @@ export class PhoneAuthClient {
566
400
  context: errorContext
567
401
  });
568
402
  }
569
- // NetworkError without code 19 is a real network error
570
403
  if (errorObj.name === BrowserError.NETWORK) {
571
404
  throw this.createError(PhoneAuthErrorCode.NETWORK_ERROR, 'Network error occurred while retrieving credentials', {
572
405
  originalError: error,
@@ -588,7 +421,6 @@ export class PhoneAuthClient {
588
421
  context: errorContext
589
422
  });
590
423
  }
591
- // Check for other cancellation patterns
592
424
  if (errorObj.name === BrowserError.ABORT ||
593
425
  ((_a = browserErrorDetails.message) === null || _a === void 0 ? void 0 : _a.includes('The operation was aborted')) ||
594
426
  ((_b = browserErrorDetails.message) === null || _b === void 0 ? void 0 : _b.includes('User cancelled'))) {
@@ -598,7 +430,6 @@ export class PhoneAuthClient {
598
430
  context: errorContext
599
431
  });
600
432
  }
601
- // For any other errors, capture all details
602
433
  throw this.createError(PhoneAuthErrorCode.INTERNAL_SERVER_ERROR, `Digital Credentials API error: ${errorObj.message || 'Unknown error'}`, {
603
434
  originalError: error,
604
435
  browserError: browserErrorDetails,
@@ -606,11 +437,6 @@ export class PhoneAuthClient {
606
437
  });
607
438
  }
608
439
  });
609
- // IMPORTANT: For TS43, we ALWAYS call the API directly without any modal
610
- // The Digital Credentials API provides its own OS-level UI (drawer/bottom sheet)
611
- // Adding our own modal would be redundant and confusing for users
612
- // Unlike Link (which needs a button for iOS App Clips), TS43 just needs direct invocation
613
- // Enhanced trigger function with callback support
614
440
  const enhancedTriggerTS43 = () => __awaiter(this, void 0, void 0, function* () {
615
441
  var _a, _b;
616
442
  try {
@@ -630,16 +456,11 @@ export class PhoneAuthClient {
630
456
  throw error;
631
457
  }
632
458
  });
633
- // TS43 always auto-triggers (no SDK UI ever)
634
- // The Digital Credentials API provides its own OS-level UI
635
- // Use a wrapper object to allow updating the promise reference
636
459
  const credentialWrapper = {
637
460
  promise: null
638
461
  };
639
462
  try {
640
- // Always try to trigger immediately
641
463
  const vpToken = yield enhancedTriggerTS43();
642
- // Convert to AuthCredential format
643
464
  credentialWrapper.promise = Promise.resolve({
644
465
  credential: typeof vpToken === 'string' ? vpToken : Object.values(vpToken)[0],
645
466
  session: plainResponse.session,
@@ -647,34 +468,25 @@ export class PhoneAuthClient {
647
468
  });
648
469
  }
649
470
  catch (error) {
650
- // If auto-trigger fails, create a rejected promise
651
471
  credentialWrapper.promise = Promise.reject(error);
652
472
  }
653
- // Handle based on execution mode
654
473
  if (executionMode === 'extended') {
655
- // Extended mode - return control methods
656
474
  const response = {
657
475
  strategy: 'ts43',
658
476
  session: plainResponse.session,
659
- credential: credentialWrapper.promise, // Initial value
660
- // Re-trigger credential request
477
+ credential: credentialWrapper.promise,
661
478
  trigger: () => __awaiter(this, void 0, void 0, function* () {
662
479
  const vpToken = yield enhancedTriggerTS43();
663
- // Update the credential promise in the wrapper
664
480
  credentialWrapper.promise = Promise.resolve({
665
481
  credential: typeof vpToken === 'string' ? vpToken : Object.values(vpToken)[0],
666
482
  session: plainResponse.session,
667
483
  authenticated: true
668
484
  });
669
- // Return void as per interface
670
485
  }),
671
486
  cancel: () => {
672
- // TS43 doesn't have a way to cancel once triggered
673
- // but we can reject the promise
674
487
  credentialWrapper.promise = Promise.reject(this.createError(PhoneAuthErrorCode.USER_DENIED, 'Authentication cancelled'));
675
488
  }
676
489
  };
677
- // Define credential as a getter that always returns the current promise
678
490
  Object.defineProperty(response, 'credential', {
679
491
  get() {
680
492
  return credentialWrapper.promise;
@@ -685,20 +497,15 @@ export class PhoneAuthClient {
685
497
  return response;
686
498
  }
687
499
  else {
688
- // Standard mode - just return credential
689
- // Wait for and return the credential
690
500
  const credential = yield credentialWrapper.promise;
691
- // Return in standard format
692
501
  return {
693
502
  [plainResponse.session.session_key]: credential.credential
694
503
  };
695
504
  }
696
505
  }
697
506
  else if (plainResponse.authentication_strategy === API.AUTHENTICATION_STRATEGY.DESKTOP) {
698
- // Desktop strategy - QR code based authentication
699
507
  const desktopData = plainResponse.data;
700
508
  const handler = new DesktopHandler();
701
- // Extract QR code data - convert to QRCodeData format for modal
702
509
  const qrCodeData = {
703
510
  iosQRCode: ((_c = desktopData.data) === null || _c === void 0 ? void 0 : _c.ios_qr_image) || desktopData.ios_qr_image ||
704
511
  ((_d = desktopData.data) === null || _d === void 0 ? void 0 : _d.qr_code_image) || desktopData.qr_code_image || desktopData.qr_code || '',
@@ -706,18 +513,15 @@ export class PhoneAuthClient {
706
513
  iosUrl: ((_f = desktopData.data) === null || _f === void 0 ? void 0 : _f.ios_url) || desktopData.ios_url,
707
514
  androidUrl: ((_g = desktopData.data) === null || _g === void 0 ? void 0 : _g.android_url) || desktopData.android_url
708
515
  };
709
- // Also keep snake_case format for extended response
710
516
  const qrCodeDataSnakeCase = {
711
517
  ios_qr_image: ((_h = desktopData.data) === null || _h === void 0 ? void 0 : _h.ios_qr_image) || desktopData.ios_qr_image,
712
518
  android_qr_image: ((_j = desktopData.data) === null || _j === void 0 ? void 0 : _j.android_qr_image) || desktopData.android_qr_image,
713
519
  qr_code: ((_k = desktopData.data) === null || _k === void 0 ? void 0 : _k.qr_code_image) || desktopData.qr_code_image || desktopData.qr_code,
714
520
  challenge: (_l = desktopData.data) === null || _l === void 0 ? void 0 : _l.challenge
715
521
  };
716
- // Polling options - gather from any options format
717
522
  const pollingEndpointToUse = (opts === null || opts === void 0 ? void 0 : opts.pollingEndpoint) ||
718
523
  ((_m = this.config.endpoints) === null || _m === void 0 ? void 0 : _m.polling);
719
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 });
720
- // Decide whether to show modal based on preventDefaultUI
721
525
  const showModal = !preventDefaultUI;
722
526
  let modal;
723
527
  let modalRef = undefined;
@@ -729,40 +533,27 @@ export class PhoneAuthClient {
729
533
  });
730
534
  if (showModal) {
731
535
  console.log('[Desktop] Creating modal with QR data:', qrCodeData);
732
- // Create and setup modal
733
536
  modal = new AuthModal(opts === null || opts === void 0 ? void 0 : opts.modalOptions, opts === null || opts === void 0 ? void 0 : opts.callbacks);
734
537
  modal.setCloseCallback(() => {
735
538
  this.log('Desktop QR modal closed by user, cancelling polling');
736
539
  handler.cancel();
737
540
  });
738
- // Add UI callbacks to polling options
739
541
  pollingOptions.onQRCodeReady = (qrData) => {
740
542
  console.log('[Desktop] onQRCodeReady callback triggered:', qrData);
741
543
  modal.showQRCode(qrData, 'Scan with your mobile device');
742
544
  };
743
545
  pollingOptions.onStatusUpdate = (status) => {
744
- if (status.status === 'pending') {
745
- modal.updateStatus('Waiting for authentication...');
746
- }
747
- else if (status.status === 'authenticated') {
748
- modal.updateStatus('Authentication successful!');
749
- setTimeout(() => modal.close(), 1500);
750
- }
751
- else if (status.status === 'expired') {
752
- modal.updateStatus('QR code expired', true);
753
- }
754
- else if (status.status === 'error') {
755
- modal.updateStatus('Authentication failed', true);
546
+ if (status.status === 'authenticated' ||
547
+ status.status === 'error' ||
548
+ status.status === 'expired') {
549
+ modal.close();
756
550
  }
757
551
  };
758
- // Note: We don't show the QR code here. It will be shown by the onQRCodeReady callback
759
- // that gets triggered immediately when handler.invoke() is called
760
552
  modalRef = modal;
761
553
  }
762
554
  else {
763
555
  console.log('[Desktop] Modal not shown - preventDefaultUI is true');
764
556
  }
765
- // Create credential promise
766
557
  const startPolling = () => handler.invoke(plainResponse, pollingOptions).then(result => {
767
558
  if (result.authenticated && result.credential) {
768
559
  return {
@@ -775,22 +566,13 @@ export class PhoneAuthClient {
775
566
  throw this.createError(PhoneAuthErrorCode.USER_DENIED, result.error || 'Desktop authentication failed');
776
567
  }
777
568
  });
778
- // Handle based on execution mode
779
569
  if (executionMode === 'extended') {
780
- // Extended mode - return control methods
781
- // Create a promise and always start polling immediately
782
570
  const credentialPromise = new Promise((resolve, reject) => {
783
- // Always start polling immediately in extended mode (consistent with Link)
784
- // This prevents the "unresolved promise trap" where developers might await
785
- // the credential before calling start_polling()
786
571
  startPolling()
787
572
  .then(resolve)
788
573
  .catch(reject);
789
574
  });
790
- // Create wrapped functions
791
575
  const wrappedStartPolling = () => __awaiter(this, void 0, void 0, function* () {
792
- // Polling has already started automatically in extended mode
793
- // This method just returns the existing credential promise for consistency
794
576
  return credentialPromise;
795
577
  });
796
578
  const wrappedStopPolling = () => {
@@ -803,9 +585,7 @@ export class PhoneAuthClient {
803
585
  handler.cleanup();
804
586
  if (modal)
805
587
  modal.close();
806
- // The credential promise will be rejected by the handler.cancel() call
807
588
  };
808
- // Create the response object with a getter for is_polling
809
589
  const response = {
810
590
  strategy: 'desktop',
811
591
  session: plainResponse.session,
@@ -815,10 +595,8 @@ export class PhoneAuthClient {
815
595
  start_polling: wrappedStartPolling,
816
596
  stop_polling: wrappedStopPolling,
817
597
  cancel: wrappedCancel,
818
- // This will be replaced with a getter
819
- is_polling: false // Initial value, will be overridden by getter
598
+ is_polling: false
820
599
  };
821
- // Define is_polling as a getter that returns current state
822
600
  Object.defineProperty(response, 'is_polling', {
823
601
  get() {
824
602
  return handler.isPolling();
@@ -829,11 +607,8 @@ export class PhoneAuthClient {
829
607
  return response;
830
608
  }
831
609
  else {
832
- // Standard mode - return credential when complete
833
- // Start polling and wait for result
834
610
  try {
835
611
  const credential = yield startPolling();
836
- // Extract session ID for compatibility
837
612
  let sessionId = 'default';
838
613
  if (desktopData && typeof desktopData === 'object') {
839
614
  if (desktopData.data && typeof desktopData.data === 'object') {
@@ -853,10 +628,8 @@ export class PhoneAuthClient {
853
628
  }
854
629
  }
855
630
  else if (plainResponse.authentication_strategy === API.AUTHENTICATION_STRATEGY.LINK) {
856
- // Link strategy - app-based authentication (iOS/Android)
857
631
  const linkData = plainResponse.data;
858
632
  const handler = new LinkHandler();
859
- // Create reusable trigger function that ONLY opens the App Clip
860
633
  const triggerLink = () => {
861
634
  var _a, _b;
862
635
  try {
@@ -876,12 +649,9 @@ export class PhoneAuthClient {
876
649
  });
877
650
  }
878
651
  };
879
- // Link always auto-opens the app (no SDK UI by default)
880
- // Open immediately to preserve user gesture context
881
652
  if ((opts === null || opts === void 0 ? void 0 : opts.autoTrigger) !== false) {
882
653
  triggerLink();
883
654
  }
884
- // Start polling in the background
885
655
  console.log('[PhoneAuth Client] Link polling config:', {
886
656
  fromOptions: opts === null || opts === void 0 ? void 0 : opts.pollingEndpoint,
887
657
  fromClientConfig: (_o = this.config.endpoints) === null || _o === void 0 ? void 0 : _o.polling,
@@ -895,15 +665,11 @@ export class PhoneAuthClient {
895
665
  onStatusUpdate: undefined
896
666
  };
897
667
  console.log('[PhoneAuth Client] Final Link polling options:', pollingOptions);
898
- // Handle based on execution mode
899
668
  if (executionMode === 'extended') {
900
- // Extended mode - return control methods and start polling immediately
901
669
  let pollingStarted = false;
902
670
  let pollingPromise = null;
903
- // Start polling immediately (Link always polls automatically unlike Desktop)
904
671
  pollingPromise = handler.invoke(plainResponse, pollingOptions);
905
672
  pollingStarted = true;
906
- // Create credential promise from the polling result
907
673
  const credentialPromise = pollingPromise.then(result => {
908
674
  if (result.authenticated && result.credential) {
909
675
  return {
@@ -916,10 +682,8 @@ export class PhoneAuthClient {
916
682
  throw this.createError(PhoneAuthErrorCode.USER_DENIED, result.error || 'Link authentication failed');
917
683
  }
918
684
  });
919
- // Function to restart polling (for retry scenarios)
920
685
  const startPolling = () => __awaiter(this, void 0, void 0, function* () {
921
686
  if (!pollingStarted) {
922
- // This is here for API consistency, but for Link it's already polling
923
687
  pollingStarted = true;
924
688
  if (!pollingPromise) {
925
689
  pollingPromise = handler.invoke(plainResponse, pollingOptions);
@@ -936,7 +700,6 @@ export class PhoneAuthClient {
936
700
  throw this.createError(PhoneAuthErrorCode.USER_DENIED, result.error || 'Link authentication failed');
937
701
  }
938
702
  }
939
- // If already polling, just return the existing promise result
940
703
  const result = yield pollingPromise;
941
704
  if (result.authenticated && result.credential) {
942
705
  return {
@@ -956,21 +719,15 @@ export class PhoneAuthClient {
956
719
  data: {
957
720
  app_url: linkData.url
958
721
  },
959
- // Re-open the app link
960
722
  trigger: triggerLink,
961
- // Polling control - now prevents double polling
962
723
  start_polling: startPolling,
963
724
  stop_polling: () => handler.cleanup(),
964
725
  cancel: () => {
965
726
  handler.cancel();
966
727
  handler.cleanup();
967
- // Note: For Link, polling is already started, so the promise
968
- // will be rejected by the handler.cancel() call
969
728
  },
970
- // This will be replaced with a getter
971
- is_polling: false // Initial value, will be overridden by getter
729
+ is_polling: false
972
730
  };
973
- // Define is_polling as a getter that returns current state
974
731
  Object.defineProperty(response, 'is_polling', {
975
732
  get() {
976
733
  return handler.isPolling();
@@ -981,7 +738,6 @@ export class PhoneAuthClient {
981
738
  return response;
982
739
  }
983
740
  else {
984
- // Standard mode - start polling immediately and wait for completion
985
741
  const credentialPromise = handler.invoke(plainResponse, pollingOptions).then(result => {
986
742
  if (result.authenticated && result.credential) {
987
743
  return {
@@ -994,44 +750,28 @@ export class PhoneAuthClient {
994
750
  throw this.createError(PhoneAuthErrorCode.USER_DENIED, result.error || 'Link authentication failed');
995
751
  }
996
752
  });
997
- // Wait for credential and return in standard format
998
753
  const credential = yield credentialPromise;
999
754
  const aggregatorId = this.config.aggregatorId || 'default';
1000
755
  return { [aggregatorId]: credential.credential };
1001
756
  }
1002
757
  }
1003
758
  else {
1004
- // Unknown strategy
1005
759
  throw this.createError(PhoneAuthErrorCode.UNSUPPORTED_STRATEGY, `Unknown authentication strategy: ${plainResponse.authentication_strategy}`);
1006
760
  }
1007
761
  });
1008
762
  }
1009
- /**
1010
- * Step 3A: Get phone number from credential
1011
- *
1012
- * @example
1013
- * ```typescript
1014
- * const prepareResp = await phoneAuthClient.preparePhoneRequest({ useCase: 'GetPhoneNumber', plmn: {...} });
1015
- * const credential = await phoneAuthClient.invokeSecurePrompt(prepareResp);
1016
- * const result = await phoneAuthClient.getPhoneNumber(credential, prepareResp.session);
1017
- * console.log(result.phone_number); // +1234567890
1018
- * ```
1019
- */
1020
763
  getPhoneNumber(credentialResponse, session) {
1021
764
  return __awaiter(this, void 0, void 0, function* () {
1022
- // Extract credential string
1023
765
  const credentialString = this.extractCredentialString(credentialResponse);
1024
- // Build request body for GetPhoneNumber
1025
766
  const requestBody = {
1026
767
  session: session,
1027
768
  credential: credentialString,
1028
- use_case: API.USE_CASE.GET_PHONE_NUMBER // Required for server routing
769
+ use_case: API.USE_CASE.GET_PHONE_NUMBER
1029
770
  };
1030
- // Only show full details in debug mode, mask sensitive data otherwise
1031
771
  if (this.config.debug) {
1032
772
  this.log('Getting phone number from credential', {
1033
773
  session: session,
1034
- credential: credentialString ? credentialString.substring(0, 50) + '...' : 'undefined', // Show partial for debugging
774
+ credential: credentialString ? credentialString.substring(0, 50) + '...' : 'undefined',
1035
775
  endpoint: this.config.endpoints.process || '/api/phone-auth/process'
1036
776
  });
1037
777
  }
@@ -1075,35 +815,18 @@ export class PhoneAuthClient {
1075
815
  }
1076
816
  });
1077
817
  }
1078
- /**
1079
- * Step 3B: Verify phone number with credential
1080
- *
1081
- * @example
1082
- * ```typescript
1083
- * const prepareResp = await phoneAuthClient.preparePhoneRequest({
1084
- * useCase: 'VerifyPhoneNumber',
1085
- * phoneNumber: '+1234567890'
1086
- * });
1087
- * const credential = await phoneAuthClient.invokeSecurePrompt(prepareResp);
1088
- * const result = await phoneAuthClient.verifyPhoneNumber(credential, prepareResp.session);
1089
- * console.log(result.verified); // true
1090
- * ```
1091
- */
1092
818
  verifyPhoneNumber(credentialResponse, session) {
1093
819
  return __awaiter(this, void 0, void 0, function* () {
1094
- // Extract credential string
1095
820
  const credentialString = this.extractCredentialString(credentialResponse);
1096
- // Build request body for VerifyPhoneNumber
1097
821
  const requestBody = {
1098
822
  session: session,
1099
823
  credential: credentialString,
1100
- use_case: API.USE_CASE.VERIFY_PHONE_NUMBER // Required for server routing
824
+ use_case: API.USE_CASE.VERIFY_PHONE_NUMBER
1101
825
  };
1102
- // Only show full details in debug mode, mask sensitive data otherwise
1103
826
  if (this.config.debug) {
1104
827
  this.log('Verifying phone number with credential', {
1105
828
  session: session,
1106
- credential: credentialString ? credentialString.substring(0, 50) + '...' : 'undefined', // Show partial for debugging
829
+ credential: credentialString ? credentialString.substring(0, 50) + '...' : 'undefined',
1107
830
  endpoint: this.config.endpoints.process || '/api/phone-auth/process'
1108
831
  });
1109
832
  }
@@ -1150,53 +873,36 @@ export class PhoneAuthClient {
1150
873
  }
1151
874
  });
1152
875
  }
1153
- /**
1154
- * Helper to extract credential string from various formats
1155
- */
1156
876
  extractCredentialString(credentialResponse) {
1157
- // If already a string, return it
1158
877
  if (typeof credentialResponse === 'string') {
1159
878
  return credentialResponse;
1160
879
  }
1161
- // Extract from vp_token object - try configured aggregatorId first, then 'glide', then 'default'
1162
880
  let credential = credentialResponse[this.config.aggregatorId || 'glide'];
1163
- // Fallback to 'glide' if not found
1164
881
  if (!credential && credentialResponse['glide']) {
1165
882
  credential = credentialResponse['glide'];
1166
883
  }
1167
- // Fallback to 'default' if still not found
1168
884
  if (!credential && credentialResponse['default']) {
1169
885
  credential = credentialResponse['default'];
1170
886
  }
1171
- // If still not found, try to get the first available key
1172
887
  if (!credential) {
1173
888
  const keys = Object.keys(credentialResponse);
1174
889
  if (keys.length > 0) {
1175
890
  credential = credentialResponse[keys[0]];
1176
891
  }
1177
892
  }
1178
- // Convert array to string if needed
1179
893
  return Array.isArray(credential) ? credential[0] : credential;
1180
894
  }
1181
- /**
1182
- * Helper to extract error details from response
1183
- */
1184
895
  extractErrorDetails(response) {
1185
896
  return __awaiter(this, void 0, void 0, function* () {
1186
897
  try {
1187
898
  const errorData = yield response.json();
1188
- // Always include the HTTP status from the response
1189
- return Object.assign(Object.assign({}, errorData), { status: response.status // Ensure HTTP status is included
1190
- });
899
+ return Object.assign(Object.assign({}, errorData), { status: response.status });
1191
900
  }
1192
901
  catch (_a) {
1193
902
  return { status: response.status, statusText: response.statusText };
1194
903
  }
1195
904
  });
1196
905
  }
1197
- /**
1198
- * Fetch with timeout
1199
- */
1200
906
  fetchWithTimeout(url, options) {
1201
907
  return __awaiter(this, void 0, void 0, function* () {
1202
908
  const controller = new AbortController();
@@ -1209,25 +915,18 @@ export class PhoneAuthClient {
1209
915
  }
1210
916
  });
1211
917
  }
1212
- /**
1213
- * Create an AuthError
1214
- */
1215
918
  createError(code, message, details) {
1216
919
  const error = {
1217
920
  code,
1218
921
  message,
1219
- details: (details === null || details === void 0 ? void 0 : details.originalError) ? Object.assign(Object.assign({}, details), { originalError: undefined // Remove the original error object
1220
- }) : details
922
+ details: (details === null || details === void 0 ? void 0 : details.originalError) ? Object.assign(Object.assign({}, details), { originalError: undefined }) : details
1221
923
  };
1222
- // Add browser error details if present
1223
924
  if (details === null || details === void 0 ? void 0 : details.browserError) {
1224
925
  error.browserError = details.browserError;
1225
926
  }
1226
- // Add context if present
1227
927
  if (details === null || details === void 0 ? void 0 : details.context) {
1228
928
  error.context = details.context;
1229
929
  }
1230
- // Add other specific fields
1231
930
  if (details === null || details === void 0 ? void 0 : details.status) {
1232
931
  error.status = details.status;
1233
932
  }
@@ -1242,51 +941,37 @@ export class PhoneAuthClient {
1242
941
  }
1243
942
  return error;
1244
943
  }
1245
- /**
1246
- * Type guard for AuthError
1247
- */
1248
944
  isAuthError(error) {
1249
945
  return error && typeof error.code === 'string' && typeof error.message === 'string';
1250
946
  }
1251
- /**
1252
- * Debug logging
1253
- */
1254
947
  log(...args) {
1255
948
  if (this.debug) {
1256
949
  console.log('[PhoneAuth]', ...args);
1257
950
  }
1258
951
  }
1259
- /**
1260
- * Determine if an error should trigger a retry
1261
- */
1262
952
  shouldRetry(error) {
1263
953
  var _a, _b, _c;
1264
- // Don't retry on 4xx client errors (these are not transient)
1265
954
  if (error.status && error.status >= 400 && error.status < 500) {
1266
955
  return false;
1267
956
  }
1268
- // Don't retry on explicit user denial or unsupported browser
1269
- // USER_DENIED cannot be retried automatically because the Digital Credentials API
1270
- // requires user interaction (transient activation)
1271
957
  const nonRetryableCodes = [
1272
958
  'USER_DENIED',
1273
959
  'BROWSER_NOT_SUPPORTED',
1274
960
  'INVALID_PHONE_NUMBER',
1275
961
  'INVALID_PARAMETERS',
1276
962
  'MISSING_PARAMETERS',
1277
- 'UNPROCESSABLE_ENTITY', // 422 errors
1278
- 'USE_CASE_MISMATCH', // Use case validation errors
1279
- 'PHONE_NUMBER_MISMATCH', // Phone verification failures
1280
- 'VERIFICATION_FAILED', // Verification failures
1281
- 'INVALID_CREDENTIAL', // Bad credentials
1282
- 'CARRIER_NOT_ELIGIBLE', // Carrier not supported
1283
- 'SESSION_EXPIRED', // Session expired
1284
- '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'
1285
971
  ];
1286
972
  if (nonRetryableCodes.includes(error.code)) {
1287
973
  return false;
1288
974
  }
1289
- // Only retry on network and server errors (5xx)
1290
975
  const retryableCodes = [
1291
976
  'NETWORK_ERROR',
1292
977
  'REQUEST_TIMEOUT',
@@ -1295,7 +980,6 @@ export class PhoneAuthClient {
1295
980
  'BAD_GATEWAY',
1296
981
  'INTERNAL_SERVER_ERROR'
1297
982
  ];
1298
- // Also check for cross-device error types in details
1299
983
  const isCrossDeviceError = ((_a = error.details) === null || _a === void 0 ? void 0 : _a.errorType) && [
1300
984
  'CROSS_DEVICE_TIMEOUT',
1301
985
  'CROSS_DEVICE_CONNECTION_LOST',
@@ -1305,17 +989,13 @@ export class PhoneAuthClient {
1305
989
  isCrossDeviceError ||
1306
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);
1307
991
  }
1308
- /**
1309
- * Analyze and enhance errors specific to cross-device flows
1310
- */
1311
992
  analyzeCrossDeviceError(error, prepareResponse) {
1312
993
  var _a;
1313
994
  const errorObj = error;
1314
- // Check for specific cross-device error patterns
1315
995
  const isCrossDeviceTimeout = (errorObj.name === 'AbortError' && this.crossDeviceActive) ||
1316
996
  (((_a = errorObj.message) === null || _a === void 0 ? void 0 : _a.includes('timeout')) && this.crossDeviceActive);
1317
997
  const isCrossDeviceNetworkIssue = errorObj.name === 'NetworkError' &&
1318
- errorObj.code !== 19 && // Not user cancellation
998
+ errorObj.code !== 19 &&
1319
999
  this.crossDeviceActive;
1320
1000
  if (isCrossDeviceTimeout) {
1321
1001
  return {
@@ -1343,12 +1023,8 @@ export class PhoneAuthClient {
1343
1023
  browserError: error.browserError
1344
1024
  };
1345
1025
  }
1346
- // Return the original error if not cross-device specific
1347
1026
  return error;
1348
1027
  }
1349
- /**
1350
- * Cache successful session for retry scenarios
1351
- */
1352
1028
  cacheSession(options, result) {
1353
1029
  const cacheKey = this.getCacheKey(options);
1354
1030
  this.sessionCache.set(cacheKey, {
@@ -1356,15 +1032,11 @@ export class PhoneAuthClient {
1356
1032
  data: result
1357
1033
  });
1358
1034
  }
1359
- /**
1360
- * Retrieve cached session if available and recent
1361
- */
1362
1035
  getCachedSession(options) {
1363
1036
  const cacheKey = this.getCacheKey(options);
1364
1037
  const cached = this.sessionCache.get(cacheKey);
1365
1038
  if (!cached)
1366
1039
  return null;
1367
- // Cache valid for 5 minutes
1368
1040
  const cacheValidMs = 5 * 60 * 1000;
1369
1041
  if (Date.now() - cached.timestamp > cacheValidMs) {
1370
1042
  this.sessionCache.delete(cacheKey);
@@ -1372,22 +1044,14 @@ export class PhoneAuthClient {
1372
1044
  }
1373
1045
  return cached.data;
1374
1046
  }
1375
- /**
1376
- * Generate cache key for session storage
1377
- */
1378
1047
  getCacheKey(options) {
1379
1048
  var _a, _b;
1380
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) || ''}`;
1381
1050
  }
1382
- /**
1383
- * Set up periodic cache cleanup
1384
- */
1385
1051
  setupCacheCleanup() {
1386
- // Only run cleanup in browser environment (not during SSR)
1387
1052
  if (typeof window === 'undefined') {
1388
1053
  return;
1389
1054
  }
1390
- // Clean up expired cache entries every minute
1391
1055
  setInterval(() => {
1392
1056
  const now = Date.now();
1393
1057
  const cacheValidMs = 5 * 60 * 1000;
@@ -1398,9 +1062,6 @@ export class PhoneAuthClient {
1398
1062
  }
1399
1063
  }, 60000);
1400
1064
  }
1401
- /**
1402
- * Utility delay function
1403
- */
1404
1065
  delay(ms) {
1405
1066
  return new Promise(resolve => setTimeout(resolve, ms));
1406
1067
  }