@glideidentity/web-client-sdk 4.4.10 → 5.0.1-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +167 -19
- package/dist/adapters/angular/index.js +0 -1
- package/dist/adapters/angular/phone-auth.service.d.ts +0 -18
- package/dist/adapters/angular/phone-auth.service.js +0 -26
- package/dist/adapters/react/index.d.ts +1 -1
- package/dist/adapters/react/index.js +0 -3
- package/dist/adapters/react/useClient.js +0 -1
- package/dist/adapters/react/usePhoneAuth.js +1 -16
- package/dist/adapters/vanilla/client.js +0 -1
- package/dist/adapters/vanilla/index.js +0 -1
- package/dist/adapters/vanilla/phone-auth.js +0 -31
- package/dist/adapters/vue/index.d.ts +1 -1
- package/dist/adapters/vue/index.js +0 -4
- package/dist/adapters/vue/useClient.js +0 -5
- package/dist/adapters/vue/usePhoneAuth.js +1 -20
- package/dist/browser/web-client-sdk.min.js +1 -1
- package/dist/browser/web-client-sdk.min.js.LICENSE.txt +1 -1
- package/dist/browser.d.ts +1 -1
- package/dist/browser.js +0 -6
- package/dist/core/client.js +0 -12
- package/dist/core/logger.js +1 -81
- package/dist/core/phone-auth/api-types.d.ts +0 -13
- package/dist/core/phone-auth/api-types.js +0 -83
- package/dist/core/phone-auth/client.js +27 -374
- package/dist/core/phone-auth/error-utils.js +1 -83
- package/dist/core/phone-auth/index.d.ts +1 -1
- package/dist/core/phone-auth/index.js +1 -3
- package/dist/core/phone-auth/status-types.d.ts +0 -78
- package/dist/core/phone-auth/status-types.js +0 -17
- package/dist/core/phone-auth/strategies/desktop.js +8 -126
- package/dist/core/phone-auth/strategies/index.d.ts +0 -4
- package/dist/core/phone-auth/strategies/index.js +0 -4
- package/dist/core/phone-auth/strategies/link.js +10 -88
- package/dist/core/phone-auth/strategies/ts43.d.ts +0 -19
- package/dist/core/phone-auth/strategies/ts43.js +2 -33
- package/dist/core/phone-auth/strategies/types.js +0 -4
- package/dist/core/phone-auth/type-guards.js +0 -131
- package/dist/core/phone-auth/types.js +0 -32
- package/dist/core/phone-auth/ui/mobile-debug-console.js +2 -28
- package/dist/core/phone-auth/ui/modal.d.ts +33 -55
- package/dist/core/phone-auth/ui/modal.js +889 -422
- package/dist/core/phone-auth/validation-utils.d.ts +0 -13
- package/dist/core/phone-auth/validation-utils.js +2 -81
- package/dist/core/version.js +1 -2
- package/dist/esm/adapters/angular/index.js +0 -1
- package/dist/esm/adapters/angular/phone-auth.service.d.ts +0 -18
- package/dist/esm/adapters/angular/phone-auth.service.js +0 -26
- package/dist/esm/adapters/react/index.d.ts +1 -1
- package/dist/esm/adapters/react/index.js +0 -3
- package/dist/esm/adapters/react/useClient.js +0 -1
- package/dist/esm/adapters/react/usePhoneAuth.js +1 -16
- package/dist/esm/adapters/vanilla/client.js +0 -1
- package/dist/esm/adapters/vanilla/index.js +0 -1
- package/dist/esm/adapters/vanilla/phone-auth.d.ts +0 -24
- package/dist/esm/adapters/vanilla/phone-auth.js +0 -31
- package/dist/esm/adapters/vue/index.d.ts +1 -1
- package/dist/esm/adapters/vue/index.js +0 -4
- package/dist/esm/adapters/vue/useClient.js +0 -5
- package/dist/esm/adapters/vue/usePhoneAuth.js +1 -20
- package/dist/esm/browser.d.ts +1 -1
- package/dist/esm/browser.js +0 -6
- package/dist/esm/core/client.d.ts +0 -10
- package/dist/esm/core/client.js +0 -12
- package/dist/esm/core/logger.d.ts +0 -53
- package/dist/esm/core/logger.js +1 -81
- package/dist/esm/core/phone-auth/api-types.d.ts +0 -328
- package/dist/esm/core/phone-auth/api-types.js +0 -83
- package/dist/esm/core/phone-auth/client.d.ts +0 -144
- package/dist/esm/core/phone-auth/client.js +28 -375
- package/dist/esm/core/phone-auth/error-utils.d.ts +0 -29
- package/dist/esm/core/phone-auth/error-utils.js +1 -83
- package/dist/esm/core/phone-auth/index.d.ts +1 -1
- package/dist/esm/core/phone-auth/index.js +2 -4
- package/dist/esm/core/phone-auth/status-types.d.ts +0 -78
- package/dist/esm/core/phone-auth/status-types.js +0 -17
- package/dist/esm/core/phone-auth/strategies/desktop.d.ts +0 -63
- package/dist/esm/core/phone-auth/strategies/desktop.js +8 -126
- package/dist/esm/core/phone-auth/strategies/index.d.ts +0 -4
- package/dist/esm/core/phone-auth/strategies/index.js +0 -4
- package/dist/esm/core/phone-auth/strategies/link.d.ts +0 -48
- package/dist/esm/core/phone-auth/strategies/link.js +10 -88
- package/dist/esm/core/phone-auth/strategies/ts43.d.ts +0 -19
- package/dist/esm/core/phone-auth/strategies/ts43.js +2 -33
- package/dist/esm/core/phone-auth/strategies/types.d.ts +0 -13
- package/dist/esm/core/phone-auth/strategies/types.js +0 -4
- package/dist/esm/core/phone-auth/type-guards.d.ts +0 -128
- package/dist/esm/core/phone-auth/type-guards.js +0 -131
- package/dist/esm/core/phone-auth/types.d.ts +0 -108
- package/dist/esm/core/phone-auth/types.js +0 -32
- package/dist/esm/core/phone-auth/ui/mobile-debug-console.d.ts +0 -4
- package/dist/esm/core/phone-auth/ui/mobile-debug-console.js +2 -28
- package/dist/esm/core/phone-auth/ui/modal.d.ts +27 -68
- package/dist/esm/core/phone-auth/ui/modal.js +889 -422
- package/dist/esm/core/phone-auth/validation-utils.d.ts +0 -44
- package/dist/esm/core/phone-auth/validation-utils.js +2 -80
- package/dist/esm/core/types.d.ts +0 -35
- package/dist/esm/core/version.js +1 -2
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js +1 -9
- package/dist/index.d.ts +1 -1
- package/dist/index.js +0 -7
- package/package.json +2 -2
|
@@ -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
|
|
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,
|
|
72
|
-
maxPollingAttempts: config.maxPollingAttempts || 30,
|
|
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;
|
|
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
|
-
|
|
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,67 +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 consent data if provided
|
|
337
|
-
if (options.consent_data) {
|
|
338
|
-
const consentValidation = (0, validation_utils_1.validateConsentData)(options.consent_data);
|
|
339
|
-
if (!consentValidation.valid) {
|
|
340
|
-
throw this.createError(error_utils_1.PhoneAuthErrorCode.BAD_REQUEST, consentValidation.error, { field: 'consent_data' });
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
// Validate use_case is provided (unless only parent_session_id is given)
|
|
344
261
|
if (!options.use_case && !(((_a = options.options) === null || _a === void 0 ? void 0 : _a.parent_session_id) && !options.phone_number && !options.plmn)) {
|
|
345
262
|
throw this.createError(error_utils_1.PhoneAuthErrorCode.MISSING_PARAMETERS, 'use_case is required', { field: 'use_case' });
|
|
346
263
|
}
|
|
347
|
-
// Validate required parameters based on use case
|
|
348
264
|
if (!options.phone_number && !options.plmn && !((_b = options.options) === null || _b === void 0 ? void 0 : _b.parent_session_id)) {
|
|
349
|
-
// Provide specific error message based on use case
|
|
350
265
|
if (options.use_case === API.USE_CASE.GET_PHONE_NUMBER) {
|
|
351
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' });
|
|
352
267
|
}
|
|
@@ -354,49 +269,36 @@ class PhoneAuthClient {
|
|
|
354
269
|
throw this.createError(error_utils_1.PhoneAuthErrorCode.MISSING_PARAMETERS, 'Phone number is required for VerifyPhoneNumber', { field: 'phoneNumber', useCase: 'VerifyPhoneNumber' });
|
|
355
270
|
}
|
|
356
271
|
else {
|
|
357
|
-
// Fallback for other use cases
|
|
358
272
|
throw this.createError(error_utils_1.PhoneAuthErrorCode.MISSING_PARAMETERS, 'Either phone number or PLMN (MCC/MNC) must be provided', { field: 'phoneNumber,plmn' });
|
|
359
273
|
}
|
|
360
274
|
}
|
|
361
|
-
// Log parent session usage
|
|
362
275
|
if (((_c = options.options) === null || _c === void 0 ? void 0 : _c.parent_session_id) && !options.phone_number && !options.plmn) {
|
|
363
276
|
if (this.debug) {
|
|
364
277
|
console.log('[PhoneAuth] Using parent_session_id: %s, use_case: %s', options.options.parent_session_id, options.use_case || 'not provided');
|
|
365
278
|
}
|
|
366
279
|
}
|
|
367
280
|
const requestId = `web-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
368
|
-
// Generate cryptographically secure nonce
|
|
369
281
|
const nonce = btoa(String.fromCharCode(...crypto.getRandomValues(new Uint8Array(32))))
|
|
370
282
|
.replace(/\+/g, '-')
|
|
371
283
|
.replace(/\//g, '_')
|
|
372
284
|
.replace(/=/g, '');
|
|
373
|
-
// Validate generated nonce
|
|
374
285
|
const nonceValidation = (0, validation_utils_1.validateNonce)(nonce);
|
|
375
286
|
if (!nonceValidation.valid) {
|
|
376
287
|
throw this.createError(error_utils_1.PhoneAuthErrorCode.INTERNAL_SERVER_ERROR, 'Failed to generate valid nonce', { field: 'nonce' });
|
|
377
288
|
}
|
|
378
|
-
// Build properly typed request body according to API specification
|
|
379
|
-
// Be permissive - backend will ignore extra fields if not needed
|
|
380
289
|
const requestBody = {
|
|
381
|
-
// Include use_case if provided (optional when parent_session_id is given)
|
|
382
290
|
use_case: options.use_case,
|
|
383
|
-
// Include phone_number if provided (backend ignores if not needed)
|
|
384
291
|
phone_number: options.phone_number,
|
|
385
|
-
// Include PLMN if provided
|
|
386
292
|
plmn: options.plmn ? {
|
|
387
293
|
mcc: options.plmn.mcc,
|
|
388
294
|
mnc: options.plmn.mnc
|
|
389
295
|
} : undefined,
|
|
390
|
-
// Auto-generated fields (SDK always provides these)
|
|
391
296
|
nonce: nonce,
|
|
392
297
|
id: requestId,
|
|
393
|
-
// Optional fields
|
|
394
|
-
consent_data: options.consent_data,
|
|
395
298
|
client_info: {
|
|
396
299
|
user_agent: navigator.userAgent,
|
|
397
300
|
platform: navigator.platform
|
|
398
301
|
},
|
|
399
|
-
// Advanced options (for desktop-mobile binding and future features)
|
|
400
302
|
options: options.options
|
|
401
303
|
};
|
|
402
304
|
this.log('Preparing phone verification request', requestBody);
|
|
@@ -407,20 +309,15 @@ class PhoneAuthClient {
|
|
|
407
309
|
body: JSON.stringify(requestBody)
|
|
408
310
|
});
|
|
409
311
|
if (!response.ok) {
|
|
410
|
-
// Try to get error details from response body
|
|
411
312
|
let errorDetails = null;
|
|
412
313
|
try {
|
|
413
314
|
errorDetails = yield response.json();
|
|
414
|
-
// Always include the HTTP status from the response
|
|
415
315
|
errorDetails = Object.assign(Object.assign({}, errorDetails), { status: response.status });
|
|
416
316
|
}
|
|
417
317
|
catch (_d) {
|
|
418
|
-
// If JSON parsing fails, use status text
|
|
419
318
|
errorDetails = { status: response.status, statusText: response.statusText };
|
|
420
319
|
}
|
|
421
|
-
// Parse the backend error response (handles both structured and unstructured errors)
|
|
422
320
|
const parsedError = (0, error_utils_1.parseBackendError)(errorDetails);
|
|
423
|
-
// Enhance with additional context
|
|
424
321
|
parsedError.context = {
|
|
425
322
|
step: 'prepare',
|
|
426
323
|
useCase: options.use_case,
|
|
@@ -428,7 +325,6 @@ class PhoneAuthClient {
|
|
|
428
325
|
userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : undefined,
|
|
429
326
|
url: typeof window !== 'undefined' ? window.location.href : undefined
|
|
430
327
|
};
|
|
431
|
-
// Add endpoint info
|
|
432
328
|
parsedError.details = Object.assign(Object.assign({}, parsedError.details), { endpoint: 'prepare', status: response.status });
|
|
433
329
|
throw parsedError;
|
|
434
330
|
}
|
|
@@ -437,16 +333,12 @@ class PhoneAuthClient {
|
|
|
437
333
|
if (!data.authentication_strategy || !data.data || !data.session) {
|
|
438
334
|
throw this.createError(error_utils_1.PhoneAuthErrorCode.INVALID_RESPONSE, 'Invalid response format from backend');
|
|
439
335
|
}
|
|
440
|
-
// Return the full response as-is
|
|
441
|
-
// The invoke method will handle it based on authentication_strategy
|
|
442
336
|
return data;
|
|
443
337
|
}
|
|
444
338
|
catch (error) {
|
|
445
|
-
// If it's already an AuthError, re-throw it
|
|
446
339
|
if (this.isAuthError(error)) {
|
|
447
340
|
throw error;
|
|
448
341
|
}
|
|
449
|
-
// Otherwise, wrap it as a network error
|
|
450
342
|
throw this.createError(error_utils_1.PhoneAuthErrorCode.NETWORK_ERROR, 'Failed to prepare verification request', {
|
|
451
343
|
originalError: error,
|
|
452
344
|
context: {
|
|
@@ -460,68 +352,16 @@ class PhoneAuthClient {
|
|
|
460
352
|
}
|
|
461
353
|
});
|
|
462
354
|
}
|
|
463
|
-
/**
|
|
464
|
-
* Step 2: Invoke secure prompt for user consent
|
|
465
|
-
*
|
|
466
|
-
* This method can work in two modes:
|
|
467
|
-
* 1. **UI Mode (default)**: Shows built-in UI components (modals/buttons)
|
|
468
|
-
* 2. **Headless Mode**: Returns raw data for custom UI implementation
|
|
469
|
-
*
|
|
470
|
-
* **Important**: This method automatically handles reactive objects from frameworks
|
|
471
|
-
* like Vue.js and React by deep cloning the input. This ensures compatibility with
|
|
472
|
-
* browser APIs that expect plain objects.
|
|
473
|
-
*
|
|
474
|
-
* @example UI Mode (shows modal/button)
|
|
475
|
-
* ```typescript
|
|
476
|
-
* // Shows SDK's built-in UI
|
|
477
|
-
* const credential = await phoneAuth.invokeSecurePrompt(prepareResult);
|
|
478
|
-
*
|
|
479
|
-
* // Customize the UI
|
|
480
|
-
* const credential = await phoneAuth.invokeSecurePrompt(prepareResult, {
|
|
481
|
-
* modalOptions: {
|
|
482
|
-
* title: 'Verify Your Identity',
|
|
483
|
-
* buttonText: 'Continue with Verizon'
|
|
484
|
-
* }
|
|
485
|
-
* });
|
|
486
|
-
* ```
|
|
487
|
-
*
|
|
488
|
-
* @example Extended Mode (returns control methods)
|
|
489
|
-
* ```typescript
|
|
490
|
-
* // Get control methods for custom implementation
|
|
491
|
-
* const result = await phoneAuth.invokeSecurePrompt(prepareResult, {
|
|
492
|
-
* executionMode: 'extended',
|
|
493
|
-
* preventDefaultUI: true // Desktop: no modal
|
|
494
|
-
* });
|
|
495
|
-
*
|
|
496
|
-
* if (result.strategy === 'desktop') {
|
|
497
|
-
* // Show custom QR UI
|
|
498
|
-
* showCustomQR(result.qr_code_data);
|
|
499
|
-
* // Start polling
|
|
500
|
-
* await result.start_polling();
|
|
501
|
-
* }
|
|
502
|
-
* ```
|
|
503
|
-
*
|
|
504
|
-
* @param prepareResponse - Response from prepare() with strategy and data
|
|
505
|
-
* @param options - Control UI behavior and response type
|
|
506
|
-
* @returns Credential or ExtendedResponse based on executionMode
|
|
507
|
-
*/
|
|
508
355
|
invokeSecurePrompt(prepareResponse, options) {
|
|
509
356
|
return __awaiter(this, void 0, void 0, function* () {
|
|
510
|
-
// Deep clone to avoid issues with reactive objects (Vue/React)
|
|
511
|
-
// This ensures we work with plain objects for browser APIs
|
|
512
|
-
// Vue's reactivity system wraps objects in Proxies which can interfere
|
|
513
|
-
// with browser APIs like Digital Credentials API that expect plain objects
|
|
514
357
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
|
|
515
|
-
// Try structuredClone first (modern browsers), but catch errors and fallback to JSON method
|
|
516
358
|
let plainResponse;
|
|
517
359
|
try {
|
|
518
|
-
// structuredClone might throw if object contains non-cloneable properties
|
|
519
360
|
plainResponse = typeof structuredClone !== 'undefined'
|
|
520
361
|
? structuredClone(prepareResponse)
|
|
521
362
|
: JSON.parse(JSON.stringify(prepareResponse));
|
|
522
363
|
}
|
|
523
364
|
catch (cloneError) {
|
|
524
|
-
// Fallback to JSON method if structuredClone fails
|
|
525
365
|
if (this.debug) {
|
|
526
366
|
console.log('[PhoneAuth] structuredClone failed, using JSON fallback:', cloneError);
|
|
527
367
|
}
|
|
@@ -531,16 +371,11 @@ class PhoneAuthClient {
|
|
|
531
371
|
console.log('[PhoneAuth] Session cache size:', this.sessionCache.size);
|
|
532
372
|
console.log('[PhoneAuth] Retry count:', this.retryCount);
|
|
533
373
|
console.log('[PhoneAuth] PrepareResponse received:', JSON.stringify(plainResponse, null, 2));
|
|
534
|
-
// Treat options as InvokeOptions (the modern format)
|
|
535
|
-
// Legacy DesktopAuthOptions properties will still work through property access
|
|
536
374
|
const opts = options;
|
|
537
|
-
// Get configuration from options - access properties directly
|
|
538
375
|
const strategy = plainResponse.authentication_strategy;
|
|
539
376
|
const preventDefaultUI = (_a = opts === null || opts === void 0 ? void 0 : opts.preventDefaultUI) !== null && _a !== void 0 ? _a : false;
|
|
540
377
|
const executionMode = (_b = opts === null || opts === void 0 ? void 0 : opts.executionMode) !== null && _b !== void 0 ? _b : 'standard';
|
|
541
|
-
// Handle based on authentication strategy
|
|
542
378
|
if (plainResponse.authentication_strategy === API.AUTHENTICATION_STRATEGY.TS43) {
|
|
543
|
-
// Check browser support for TS43 strategy which requires Digital Credentials API
|
|
544
379
|
if (!this.isSupported()) {
|
|
545
380
|
throw this.createError(error_utils_1.PhoneAuthErrorCode.BROWSER_NOT_SUPPORTED, 'Your browser does not support the Digital Credentials API required for TS43 authentication');
|
|
546
381
|
}
|
|
@@ -554,18 +389,13 @@ class PhoneAuthClient {
|
|
|
554
389
|
}
|
|
555
390
|
};
|
|
556
391
|
this.log('Invoking TS43 secure authentication prompt', secureCredentialRequest);
|
|
557
|
-
// Function to trigger TS43 authentication
|
|
558
392
|
const triggerTS43 = () => __awaiter(this, void 0, void 0, function* () {
|
|
559
393
|
var _a, _b;
|
|
560
394
|
try {
|
|
561
|
-
// This is the browser API call for TS43
|
|
562
|
-
// Cast to CredentialRequestOptions with digital field (TS43 specific)
|
|
563
395
|
const credentialOptions = secureCredentialRequest;
|
|
564
396
|
const credentialResponse = yield navigator.credentials.get(credentialOptions);
|
|
565
|
-
// Type guard for Digital Credential response
|
|
566
397
|
const digitalResponse = credentialResponse;
|
|
567
398
|
if (!digitalResponse || !('data' in digitalResponse) || !digitalResponse.data) {
|
|
568
|
-
// Check if this is likely due to the browser flag being disabled
|
|
569
399
|
const supportInfo = this.getBrowserSupportInfo();
|
|
570
400
|
if (supportInfo.browser === types_1.BrowserName.CHROME || supportInfo.browser === types_1.BrowserName.EDGE) {
|
|
571
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.`);
|
|
@@ -577,7 +407,6 @@ class PhoneAuthClient {
|
|
|
577
407
|
return credentialData.vp_token;
|
|
578
408
|
}
|
|
579
409
|
catch (error) {
|
|
580
|
-
// Capture detailed browser error information
|
|
581
410
|
const errorObj = error;
|
|
582
411
|
const browserErrorDetails = {
|
|
583
412
|
name: errorObj.name || 'UnknownError',
|
|
@@ -590,11 +419,9 @@ class PhoneAuthClient {
|
|
|
590
419
|
timestamp: new Date().toISOString(),
|
|
591
420
|
userAgent: navigator.userAgent,
|
|
592
421
|
url: window.location.href,
|
|
593
|
-
// Include request details for debugging
|
|
594
422
|
authentication_strategy: plainResponse.authentication_strategy,
|
|
595
423
|
hasSession: !!plainResponse.session
|
|
596
424
|
};
|
|
597
|
-
// Handle specific browser errors
|
|
598
425
|
if (errorObj.name === types_1.BrowserError.NOT_ALLOWED) {
|
|
599
426
|
throw this.createError(error_utils_1.PhoneAuthErrorCode.USER_DENIED, 'User denied the credential request or the request timed out', {
|
|
600
427
|
originalError: error,
|
|
@@ -602,7 +429,6 @@ class PhoneAuthClient {
|
|
|
602
429
|
context: errorContext
|
|
603
430
|
});
|
|
604
431
|
}
|
|
605
|
-
// NetworkError with code 19 specifically indicates user cancellation in Digital Credentials API
|
|
606
432
|
if (errorObj.name === types_1.BrowserError.NETWORK && errorObj.code === types_1.BrowserErrorCode.USER_CANCELLED_DC_API) {
|
|
607
433
|
throw this.createError(error_utils_1.PhoneAuthErrorCode.USER_DENIED, 'Authentication cancelled by user', {
|
|
608
434
|
originalError: error,
|
|
@@ -610,7 +436,6 @@ class PhoneAuthClient {
|
|
|
610
436
|
context: errorContext
|
|
611
437
|
});
|
|
612
438
|
}
|
|
613
|
-
// NetworkError without code 19 is a real network error
|
|
614
439
|
if (errorObj.name === types_1.BrowserError.NETWORK) {
|
|
615
440
|
throw this.createError(error_utils_1.PhoneAuthErrorCode.NETWORK_ERROR, 'Network error occurred while retrieving credentials', {
|
|
616
441
|
originalError: error,
|
|
@@ -632,7 +457,6 @@ class PhoneAuthClient {
|
|
|
632
457
|
context: errorContext
|
|
633
458
|
});
|
|
634
459
|
}
|
|
635
|
-
// Check for other cancellation patterns
|
|
636
460
|
if (errorObj.name === types_1.BrowserError.ABORT ||
|
|
637
461
|
((_a = browserErrorDetails.message) === null || _a === void 0 ? void 0 : _a.includes('The operation was aborted')) ||
|
|
638
462
|
((_b = browserErrorDetails.message) === null || _b === void 0 ? void 0 : _b.includes('User cancelled'))) {
|
|
@@ -642,7 +466,6 @@ class PhoneAuthClient {
|
|
|
642
466
|
context: errorContext
|
|
643
467
|
});
|
|
644
468
|
}
|
|
645
|
-
// For any other errors, capture all details
|
|
646
469
|
throw this.createError(error_utils_1.PhoneAuthErrorCode.INTERNAL_SERVER_ERROR, `Digital Credentials API error: ${errorObj.message || 'Unknown error'}`, {
|
|
647
470
|
originalError: error,
|
|
648
471
|
browserError: browserErrorDetails,
|
|
@@ -650,11 +473,6 @@ class PhoneAuthClient {
|
|
|
650
473
|
});
|
|
651
474
|
}
|
|
652
475
|
});
|
|
653
|
-
// IMPORTANT: For TS43, we ALWAYS call the API directly without any modal
|
|
654
|
-
// The Digital Credentials API provides its own OS-level UI (drawer/bottom sheet)
|
|
655
|
-
// Adding our own modal would be redundant and confusing for users
|
|
656
|
-
// Unlike Link (which needs a button for iOS App Clips), TS43 just needs direct invocation
|
|
657
|
-
// Enhanced trigger function with callback support
|
|
658
476
|
const enhancedTriggerTS43 = () => __awaiter(this, void 0, void 0, function* () {
|
|
659
477
|
var _a, _b;
|
|
660
478
|
try {
|
|
@@ -674,16 +492,11 @@ class PhoneAuthClient {
|
|
|
674
492
|
throw error;
|
|
675
493
|
}
|
|
676
494
|
});
|
|
677
|
-
// TS43 always auto-triggers (no SDK UI ever)
|
|
678
|
-
// The Digital Credentials API provides its own OS-level UI
|
|
679
|
-
// Use a wrapper object to allow updating the promise reference
|
|
680
495
|
const credentialWrapper = {
|
|
681
496
|
promise: null
|
|
682
497
|
};
|
|
683
498
|
try {
|
|
684
|
-
// Always try to trigger immediately
|
|
685
499
|
const vpToken = yield enhancedTriggerTS43();
|
|
686
|
-
// Convert to AuthCredential format
|
|
687
500
|
credentialWrapper.promise = Promise.resolve({
|
|
688
501
|
credential: typeof vpToken === 'string' ? vpToken : Object.values(vpToken)[0],
|
|
689
502
|
session: plainResponse.session,
|
|
@@ -691,34 +504,25 @@ class PhoneAuthClient {
|
|
|
691
504
|
});
|
|
692
505
|
}
|
|
693
506
|
catch (error) {
|
|
694
|
-
// If auto-trigger fails, create a rejected promise
|
|
695
507
|
credentialWrapper.promise = Promise.reject(error);
|
|
696
508
|
}
|
|
697
|
-
// Handle based on execution mode
|
|
698
509
|
if (executionMode === 'extended') {
|
|
699
|
-
// Extended mode - return control methods
|
|
700
510
|
const response = {
|
|
701
511
|
strategy: 'ts43',
|
|
702
512
|
session: plainResponse.session,
|
|
703
|
-
credential: credentialWrapper.promise,
|
|
704
|
-
// Re-trigger credential request
|
|
513
|
+
credential: credentialWrapper.promise,
|
|
705
514
|
trigger: () => __awaiter(this, void 0, void 0, function* () {
|
|
706
515
|
const vpToken = yield enhancedTriggerTS43();
|
|
707
|
-
// Update the credential promise in the wrapper
|
|
708
516
|
credentialWrapper.promise = Promise.resolve({
|
|
709
517
|
credential: typeof vpToken === 'string' ? vpToken : Object.values(vpToken)[0],
|
|
710
518
|
session: plainResponse.session,
|
|
711
519
|
authenticated: true
|
|
712
520
|
});
|
|
713
|
-
// Return void as per interface
|
|
714
521
|
}),
|
|
715
522
|
cancel: () => {
|
|
716
|
-
// TS43 doesn't have a way to cancel once triggered
|
|
717
|
-
// but we can reject the promise
|
|
718
523
|
credentialWrapper.promise = Promise.reject(this.createError(error_utils_1.PhoneAuthErrorCode.USER_DENIED, 'Authentication cancelled'));
|
|
719
524
|
}
|
|
720
525
|
};
|
|
721
|
-
// Define credential as a getter that always returns the current promise
|
|
722
526
|
Object.defineProperty(response, 'credential', {
|
|
723
527
|
get() {
|
|
724
528
|
return credentialWrapper.promise;
|
|
@@ -729,20 +533,15 @@ class PhoneAuthClient {
|
|
|
729
533
|
return response;
|
|
730
534
|
}
|
|
731
535
|
else {
|
|
732
|
-
// Standard mode - just return credential
|
|
733
|
-
// Wait for and return the credential
|
|
734
536
|
const credential = yield credentialWrapper.promise;
|
|
735
|
-
// Return in standard format
|
|
736
537
|
return {
|
|
737
538
|
[plainResponse.session.session_key]: credential.credential
|
|
738
539
|
};
|
|
739
540
|
}
|
|
740
541
|
}
|
|
741
542
|
else if (plainResponse.authentication_strategy === API.AUTHENTICATION_STRATEGY.DESKTOP) {
|
|
742
|
-
// Desktop strategy - QR code based authentication
|
|
743
543
|
const desktopData = plainResponse.data;
|
|
744
544
|
const handler = new desktop_1.DesktopHandler();
|
|
745
|
-
// Extract QR code data - convert to QRCodeData format for modal
|
|
746
545
|
const qrCodeData = {
|
|
747
546
|
iosQRCode: ((_c = desktopData.data) === null || _c === void 0 ? void 0 : _c.ios_qr_image) || desktopData.ios_qr_image ||
|
|
748
547
|
((_d = desktopData.data) === null || _d === void 0 ? void 0 : _d.qr_code_image) || desktopData.qr_code_image || desktopData.qr_code || '',
|
|
@@ -750,18 +549,15 @@ class PhoneAuthClient {
|
|
|
750
549
|
iosUrl: ((_f = desktopData.data) === null || _f === void 0 ? void 0 : _f.ios_url) || desktopData.ios_url,
|
|
751
550
|
androidUrl: ((_g = desktopData.data) === null || _g === void 0 ? void 0 : _g.android_url) || desktopData.android_url
|
|
752
551
|
};
|
|
753
|
-
// Also keep snake_case format for extended response
|
|
754
552
|
const qrCodeDataSnakeCase = {
|
|
755
553
|
ios_qr_image: ((_h = desktopData.data) === null || _h === void 0 ? void 0 : _h.ios_qr_image) || desktopData.ios_qr_image,
|
|
756
554
|
android_qr_image: ((_j = desktopData.data) === null || _j === void 0 ? void 0 : _j.android_qr_image) || desktopData.android_qr_image,
|
|
757
555
|
qr_code: ((_k = desktopData.data) === null || _k === void 0 ? void 0 : _k.qr_code_image) || desktopData.qr_code_image || desktopData.qr_code,
|
|
758
556
|
challenge: (_l = desktopData.data) === null || _l === void 0 ? void 0 : _l.challenge
|
|
759
557
|
};
|
|
760
|
-
// Polling options - gather from any options format
|
|
761
558
|
const pollingEndpointToUse = (opts === null || opts === void 0 ? void 0 : opts.pollingEndpoint) ||
|
|
762
559
|
((_m = this.config.endpoints) === null || _m === void 0 ? void 0 : _m.polling);
|
|
763
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 });
|
|
764
|
-
// Decide whether to show modal based on preventDefaultUI
|
|
765
561
|
const showModal = !preventDefaultUI;
|
|
766
562
|
let modal;
|
|
767
563
|
let modalRef = undefined;
|
|
@@ -773,40 +569,27 @@ class PhoneAuthClient {
|
|
|
773
569
|
});
|
|
774
570
|
if (showModal) {
|
|
775
571
|
console.log('[Desktop] Creating modal with QR data:', qrCodeData);
|
|
776
|
-
// Create and setup modal
|
|
777
572
|
modal = new modal_1.AuthModal(opts === null || opts === void 0 ? void 0 : opts.modalOptions, opts === null || opts === void 0 ? void 0 : opts.callbacks);
|
|
778
573
|
modal.setCloseCallback(() => {
|
|
779
574
|
this.log('Desktop QR modal closed by user, cancelling polling');
|
|
780
575
|
handler.cancel();
|
|
781
576
|
});
|
|
782
|
-
// Add UI callbacks to polling options
|
|
783
577
|
pollingOptions.onQRCodeReady = (qrData) => {
|
|
784
578
|
console.log('[Desktop] onQRCodeReady callback triggered:', qrData);
|
|
785
579
|
modal.showQRCode(qrData, 'Scan with your mobile device');
|
|
786
580
|
};
|
|
787
581
|
pollingOptions.onStatusUpdate = (status) => {
|
|
788
|
-
if (status.status === '
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
modal.updateStatus('Authentication successful!');
|
|
793
|
-
setTimeout(() => modal.close(), 1500);
|
|
794
|
-
}
|
|
795
|
-
else if (status.status === 'expired') {
|
|
796
|
-
modal.updateStatus('QR code expired', true);
|
|
797
|
-
}
|
|
798
|
-
else if (status.status === 'error') {
|
|
799
|
-
modal.updateStatus('Authentication failed', true);
|
|
582
|
+
if (status.status === 'authenticated' ||
|
|
583
|
+
status.status === 'error' ||
|
|
584
|
+
status.status === 'expired') {
|
|
585
|
+
modal.close();
|
|
800
586
|
}
|
|
801
587
|
};
|
|
802
|
-
// Note: We don't show the QR code here. It will be shown by the onQRCodeReady callback
|
|
803
|
-
// that gets triggered immediately when handler.invoke() is called
|
|
804
588
|
modalRef = modal;
|
|
805
589
|
}
|
|
806
590
|
else {
|
|
807
591
|
console.log('[Desktop] Modal not shown - preventDefaultUI is true');
|
|
808
592
|
}
|
|
809
|
-
// Create credential promise
|
|
810
593
|
const startPolling = () => handler.invoke(plainResponse, pollingOptions).then(result => {
|
|
811
594
|
if (result.authenticated && result.credential) {
|
|
812
595
|
return {
|
|
@@ -819,22 +602,13 @@ class PhoneAuthClient {
|
|
|
819
602
|
throw this.createError(error_utils_1.PhoneAuthErrorCode.USER_DENIED, result.error || 'Desktop authentication failed');
|
|
820
603
|
}
|
|
821
604
|
});
|
|
822
|
-
// Handle based on execution mode
|
|
823
605
|
if (executionMode === 'extended') {
|
|
824
|
-
// Extended mode - return control methods
|
|
825
|
-
// Create a promise and always start polling immediately
|
|
826
606
|
const credentialPromise = new Promise((resolve, reject) => {
|
|
827
|
-
// Always start polling immediately in extended mode (consistent with Link)
|
|
828
|
-
// This prevents the "unresolved promise trap" where developers might await
|
|
829
|
-
// the credential before calling start_polling()
|
|
830
607
|
startPolling()
|
|
831
608
|
.then(resolve)
|
|
832
609
|
.catch(reject);
|
|
833
610
|
});
|
|
834
|
-
// Create wrapped functions
|
|
835
611
|
const wrappedStartPolling = () => __awaiter(this, void 0, void 0, function* () {
|
|
836
|
-
// Polling has already started automatically in extended mode
|
|
837
|
-
// This method just returns the existing credential promise for consistency
|
|
838
612
|
return credentialPromise;
|
|
839
613
|
});
|
|
840
614
|
const wrappedStopPolling = () => {
|
|
@@ -847,9 +621,7 @@ class PhoneAuthClient {
|
|
|
847
621
|
handler.cleanup();
|
|
848
622
|
if (modal)
|
|
849
623
|
modal.close();
|
|
850
|
-
// The credential promise will be rejected by the handler.cancel() call
|
|
851
624
|
};
|
|
852
|
-
// Create the response object with a getter for is_polling
|
|
853
625
|
const response = {
|
|
854
626
|
strategy: 'desktop',
|
|
855
627
|
session: plainResponse.session,
|
|
@@ -859,10 +631,8 @@ class PhoneAuthClient {
|
|
|
859
631
|
start_polling: wrappedStartPolling,
|
|
860
632
|
stop_polling: wrappedStopPolling,
|
|
861
633
|
cancel: wrappedCancel,
|
|
862
|
-
|
|
863
|
-
is_polling: false // Initial value, will be overridden by getter
|
|
634
|
+
is_polling: false
|
|
864
635
|
};
|
|
865
|
-
// Define is_polling as a getter that returns current state
|
|
866
636
|
Object.defineProperty(response, 'is_polling', {
|
|
867
637
|
get() {
|
|
868
638
|
return handler.isPolling();
|
|
@@ -873,11 +643,8 @@ class PhoneAuthClient {
|
|
|
873
643
|
return response;
|
|
874
644
|
}
|
|
875
645
|
else {
|
|
876
|
-
// Standard mode - return credential when complete
|
|
877
|
-
// Start polling and wait for result
|
|
878
646
|
try {
|
|
879
647
|
const credential = yield startPolling();
|
|
880
|
-
// Extract session ID for compatibility
|
|
881
648
|
let sessionId = 'default';
|
|
882
649
|
if (desktopData && typeof desktopData === 'object') {
|
|
883
650
|
if (desktopData.data && typeof desktopData.data === 'object') {
|
|
@@ -897,10 +664,8 @@ class PhoneAuthClient {
|
|
|
897
664
|
}
|
|
898
665
|
}
|
|
899
666
|
else if (plainResponse.authentication_strategy === API.AUTHENTICATION_STRATEGY.LINK) {
|
|
900
|
-
// Link strategy - app-based authentication (iOS/Android)
|
|
901
667
|
const linkData = plainResponse.data;
|
|
902
668
|
const handler = new link_1.LinkHandler();
|
|
903
|
-
// Create reusable trigger function that ONLY opens the App Clip
|
|
904
669
|
const triggerLink = () => {
|
|
905
670
|
var _a, _b;
|
|
906
671
|
try {
|
|
@@ -920,12 +685,9 @@ class PhoneAuthClient {
|
|
|
920
685
|
});
|
|
921
686
|
}
|
|
922
687
|
};
|
|
923
|
-
// Link always auto-opens the app (no SDK UI by default)
|
|
924
|
-
// Open immediately to preserve user gesture context
|
|
925
688
|
if ((opts === null || opts === void 0 ? void 0 : opts.autoTrigger) !== false) {
|
|
926
689
|
triggerLink();
|
|
927
690
|
}
|
|
928
|
-
// Start polling in the background
|
|
929
691
|
console.log('[PhoneAuth Client] Link polling config:', {
|
|
930
692
|
fromOptions: opts === null || opts === void 0 ? void 0 : opts.pollingEndpoint,
|
|
931
693
|
fromClientConfig: (_o = this.config.endpoints) === null || _o === void 0 ? void 0 : _o.polling,
|
|
@@ -939,15 +701,11 @@ class PhoneAuthClient {
|
|
|
939
701
|
onStatusUpdate: undefined
|
|
940
702
|
};
|
|
941
703
|
console.log('[PhoneAuth Client] Final Link polling options:', pollingOptions);
|
|
942
|
-
// Handle based on execution mode
|
|
943
704
|
if (executionMode === 'extended') {
|
|
944
|
-
// Extended mode - return control methods and start polling immediately
|
|
945
705
|
let pollingStarted = false;
|
|
946
706
|
let pollingPromise = null;
|
|
947
|
-
// Start polling immediately (Link always polls automatically unlike Desktop)
|
|
948
707
|
pollingPromise = handler.invoke(plainResponse, pollingOptions);
|
|
949
708
|
pollingStarted = true;
|
|
950
|
-
// Create credential promise from the polling result
|
|
951
709
|
const credentialPromise = pollingPromise.then(result => {
|
|
952
710
|
if (result.authenticated && result.credential) {
|
|
953
711
|
return {
|
|
@@ -960,10 +718,8 @@ class PhoneAuthClient {
|
|
|
960
718
|
throw this.createError(error_utils_1.PhoneAuthErrorCode.USER_DENIED, result.error || 'Link authentication failed');
|
|
961
719
|
}
|
|
962
720
|
});
|
|
963
|
-
// Function to restart polling (for retry scenarios)
|
|
964
721
|
const startPolling = () => __awaiter(this, void 0, void 0, function* () {
|
|
965
722
|
if (!pollingStarted) {
|
|
966
|
-
// This is here for API consistency, but for Link it's already polling
|
|
967
723
|
pollingStarted = true;
|
|
968
724
|
if (!pollingPromise) {
|
|
969
725
|
pollingPromise = handler.invoke(plainResponse, pollingOptions);
|
|
@@ -980,7 +736,6 @@ class PhoneAuthClient {
|
|
|
980
736
|
throw this.createError(error_utils_1.PhoneAuthErrorCode.USER_DENIED, result.error || 'Link authentication failed');
|
|
981
737
|
}
|
|
982
738
|
}
|
|
983
|
-
// If already polling, just return the existing promise result
|
|
984
739
|
const result = yield pollingPromise;
|
|
985
740
|
if (result.authenticated && result.credential) {
|
|
986
741
|
return {
|
|
@@ -1000,21 +755,15 @@ class PhoneAuthClient {
|
|
|
1000
755
|
data: {
|
|
1001
756
|
app_url: linkData.url
|
|
1002
757
|
},
|
|
1003
|
-
// Re-open the app link
|
|
1004
758
|
trigger: triggerLink,
|
|
1005
|
-
// Polling control - now prevents double polling
|
|
1006
759
|
start_polling: startPolling,
|
|
1007
760
|
stop_polling: () => handler.cleanup(),
|
|
1008
761
|
cancel: () => {
|
|
1009
762
|
handler.cancel();
|
|
1010
763
|
handler.cleanup();
|
|
1011
|
-
// Note: For Link, polling is already started, so the promise
|
|
1012
|
-
// will be rejected by the handler.cancel() call
|
|
1013
764
|
},
|
|
1014
|
-
|
|
1015
|
-
is_polling: false // Initial value, will be overridden by getter
|
|
765
|
+
is_polling: false
|
|
1016
766
|
};
|
|
1017
|
-
// Define is_polling as a getter that returns current state
|
|
1018
767
|
Object.defineProperty(response, 'is_polling', {
|
|
1019
768
|
get() {
|
|
1020
769
|
return handler.isPolling();
|
|
@@ -1025,7 +774,6 @@ class PhoneAuthClient {
|
|
|
1025
774
|
return response;
|
|
1026
775
|
}
|
|
1027
776
|
else {
|
|
1028
|
-
// Standard mode - start polling immediately and wait for completion
|
|
1029
777
|
const credentialPromise = handler.invoke(plainResponse, pollingOptions).then(result => {
|
|
1030
778
|
if (result.authenticated && result.credential) {
|
|
1031
779
|
return {
|
|
@@ -1038,44 +786,28 @@ class PhoneAuthClient {
|
|
|
1038
786
|
throw this.createError(error_utils_1.PhoneAuthErrorCode.USER_DENIED, result.error || 'Link authentication failed');
|
|
1039
787
|
}
|
|
1040
788
|
});
|
|
1041
|
-
// Wait for credential and return in standard format
|
|
1042
789
|
const credential = yield credentialPromise;
|
|
1043
790
|
const aggregatorId = this.config.aggregatorId || 'default';
|
|
1044
791
|
return { [aggregatorId]: credential.credential };
|
|
1045
792
|
}
|
|
1046
793
|
}
|
|
1047
794
|
else {
|
|
1048
|
-
// Unknown strategy
|
|
1049
795
|
throw this.createError(error_utils_1.PhoneAuthErrorCode.UNSUPPORTED_STRATEGY, `Unknown authentication strategy: ${plainResponse.authentication_strategy}`);
|
|
1050
796
|
}
|
|
1051
797
|
});
|
|
1052
798
|
}
|
|
1053
|
-
/**
|
|
1054
|
-
* Step 3A: Get phone number from credential
|
|
1055
|
-
*
|
|
1056
|
-
* @example
|
|
1057
|
-
* ```typescript
|
|
1058
|
-
* const prepareResp = await phoneAuthClient.preparePhoneRequest({ useCase: 'GetPhoneNumber', plmn: {...} });
|
|
1059
|
-
* const credential = await phoneAuthClient.invokeSecurePrompt(prepareResp);
|
|
1060
|
-
* const result = await phoneAuthClient.getPhoneNumber(credential, prepareResp.session);
|
|
1061
|
-
* console.log(result.phone_number); // +1234567890
|
|
1062
|
-
* ```
|
|
1063
|
-
*/
|
|
1064
799
|
getPhoneNumber(credentialResponse, session) {
|
|
1065
800
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1066
|
-
// Extract credential string
|
|
1067
801
|
const credentialString = this.extractCredentialString(credentialResponse);
|
|
1068
|
-
// Build request body for GetPhoneNumber
|
|
1069
802
|
const requestBody = {
|
|
1070
803
|
session: session,
|
|
1071
804
|
credential: credentialString,
|
|
1072
|
-
use_case: API.USE_CASE.GET_PHONE_NUMBER
|
|
805
|
+
use_case: API.USE_CASE.GET_PHONE_NUMBER
|
|
1073
806
|
};
|
|
1074
|
-
// Only show full details in debug mode, mask sensitive data otherwise
|
|
1075
807
|
if (this.config.debug) {
|
|
1076
808
|
this.log('Getting phone number from credential', {
|
|
1077
809
|
session: session,
|
|
1078
|
-
credential: credentialString ? credentialString.substring(0, 50) + '...' : 'undefined',
|
|
810
|
+
credential: credentialString ? credentialString.substring(0, 50) + '...' : 'undefined',
|
|
1079
811
|
endpoint: this.config.endpoints.process || '/api/phone-auth/process'
|
|
1080
812
|
});
|
|
1081
813
|
}
|
|
@@ -1119,35 +851,18 @@ class PhoneAuthClient {
|
|
|
1119
851
|
}
|
|
1120
852
|
});
|
|
1121
853
|
}
|
|
1122
|
-
/**
|
|
1123
|
-
* Step 3B: Verify phone number with credential
|
|
1124
|
-
*
|
|
1125
|
-
* @example
|
|
1126
|
-
* ```typescript
|
|
1127
|
-
* const prepareResp = await phoneAuthClient.preparePhoneRequest({
|
|
1128
|
-
* useCase: 'VerifyPhoneNumber',
|
|
1129
|
-
* phoneNumber: '+1234567890'
|
|
1130
|
-
* });
|
|
1131
|
-
* const credential = await phoneAuthClient.invokeSecurePrompt(prepareResp);
|
|
1132
|
-
* const result = await phoneAuthClient.verifyPhoneNumber(credential, prepareResp.session);
|
|
1133
|
-
* console.log(result.verified); // true
|
|
1134
|
-
* ```
|
|
1135
|
-
*/
|
|
1136
854
|
verifyPhoneNumber(credentialResponse, session) {
|
|
1137
855
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1138
|
-
// Extract credential string
|
|
1139
856
|
const credentialString = this.extractCredentialString(credentialResponse);
|
|
1140
|
-
// Build request body for VerifyPhoneNumber
|
|
1141
857
|
const requestBody = {
|
|
1142
858
|
session: session,
|
|
1143
859
|
credential: credentialString,
|
|
1144
|
-
use_case: API.USE_CASE.VERIFY_PHONE_NUMBER
|
|
860
|
+
use_case: API.USE_CASE.VERIFY_PHONE_NUMBER
|
|
1145
861
|
};
|
|
1146
|
-
// Only show full details in debug mode, mask sensitive data otherwise
|
|
1147
862
|
if (this.config.debug) {
|
|
1148
863
|
this.log('Verifying phone number with credential', {
|
|
1149
864
|
session: session,
|
|
1150
|
-
credential: credentialString ? credentialString.substring(0, 50) + '...' : 'undefined',
|
|
865
|
+
credential: credentialString ? credentialString.substring(0, 50) + '...' : 'undefined',
|
|
1151
866
|
endpoint: this.config.endpoints.process || '/api/phone-auth/process'
|
|
1152
867
|
});
|
|
1153
868
|
}
|
|
@@ -1194,53 +909,36 @@ class PhoneAuthClient {
|
|
|
1194
909
|
}
|
|
1195
910
|
});
|
|
1196
911
|
}
|
|
1197
|
-
/**
|
|
1198
|
-
* Helper to extract credential string from various formats
|
|
1199
|
-
*/
|
|
1200
912
|
extractCredentialString(credentialResponse) {
|
|
1201
|
-
// If already a string, return it
|
|
1202
913
|
if (typeof credentialResponse === 'string') {
|
|
1203
914
|
return credentialResponse;
|
|
1204
915
|
}
|
|
1205
|
-
// Extract from vp_token object - try configured aggregatorId first, then 'glide', then 'default'
|
|
1206
916
|
let credential = credentialResponse[this.config.aggregatorId || 'glide'];
|
|
1207
|
-
// Fallback to 'glide' if not found
|
|
1208
917
|
if (!credential && credentialResponse['glide']) {
|
|
1209
918
|
credential = credentialResponse['glide'];
|
|
1210
919
|
}
|
|
1211
|
-
// Fallback to 'default' if still not found
|
|
1212
920
|
if (!credential && credentialResponse['default']) {
|
|
1213
921
|
credential = credentialResponse['default'];
|
|
1214
922
|
}
|
|
1215
|
-
// If still not found, try to get the first available key
|
|
1216
923
|
if (!credential) {
|
|
1217
924
|
const keys = Object.keys(credentialResponse);
|
|
1218
925
|
if (keys.length > 0) {
|
|
1219
926
|
credential = credentialResponse[keys[0]];
|
|
1220
927
|
}
|
|
1221
928
|
}
|
|
1222
|
-
// Convert array to string if needed
|
|
1223
929
|
return Array.isArray(credential) ? credential[0] : credential;
|
|
1224
930
|
}
|
|
1225
|
-
/**
|
|
1226
|
-
* Helper to extract error details from response
|
|
1227
|
-
*/
|
|
1228
931
|
extractErrorDetails(response) {
|
|
1229
932
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1230
933
|
try {
|
|
1231
934
|
const errorData = yield response.json();
|
|
1232
|
-
|
|
1233
|
-
return Object.assign(Object.assign({}, errorData), { status: response.status // Ensure HTTP status is included
|
|
1234
|
-
});
|
|
935
|
+
return Object.assign(Object.assign({}, errorData), { status: response.status });
|
|
1235
936
|
}
|
|
1236
937
|
catch (_a) {
|
|
1237
938
|
return { status: response.status, statusText: response.statusText };
|
|
1238
939
|
}
|
|
1239
940
|
});
|
|
1240
941
|
}
|
|
1241
|
-
/**
|
|
1242
|
-
* Fetch with timeout
|
|
1243
|
-
*/
|
|
1244
942
|
fetchWithTimeout(url, options) {
|
|
1245
943
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1246
944
|
const controller = new AbortController();
|
|
@@ -1253,25 +951,18 @@ class PhoneAuthClient {
|
|
|
1253
951
|
}
|
|
1254
952
|
});
|
|
1255
953
|
}
|
|
1256
|
-
/**
|
|
1257
|
-
* Create an AuthError
|
|
1258
|
-
*/
|
|
1259
954
|
createError(code, message, details) {
|
|
1260
955
|
const error = {
|
|
1261
956
|
code,
|
|
1262
957
|
message,
|
|
1263
|
-
details: (details === null || details === void 0 ? void 0 : details.originalError) ? Object.assign(Object.assign({}, details), { originalError: undefined
|
|
1264
|
-
}) : details
|
|
958
|
+
details: (details === null || details === void 0 ? void 0 : details.originalError) ? Object.assign(Object.assign({}, details), { originalError: undefined }) : details
|
|
1265
959
|
};
|
|
1266
|
-
// Add browser error details if present
|
|
1267
960
|
if (details === null || details === void 0 ? void 0 : details.browserError) {
|
|
1268
961
|
error.browserError = details.browserError;
|
|
1269
962
|
}
|
|
1270
|
-
// Add context if present
|
|
1271
963
|
if (details === null || details === void 0 ? void 0 : details.context) {
|
|
1272
964
|
error.context = details.context;
|
|
1273
965
|
}
|
|
1274
|
-
// Add other specific fields
|
|
1275
966
|
if (details === null || details === void 0 ? void 0 : details.status) {
|
|
1276
967
|
error.status = details.status;
|
|
1277
968
|
}
|
|
@@ -1286,51 +977,37 @@ class PhoneAuthClient {
|
|
|
1286
977
|
}
|
|
1287
978
|
return error;
|
|
1288
979
|
}
|
|
1289
|
-
/**
|
|
1290
|
-
* Type guard for AuthError
|
|
1291
|
-
*/
|
|
1292
980
|
isAuthError(error) {
|
|
1293
981
|
return error && typeof error.code === 'string' && typeof error.message === 'string';
|
|
1294
982
|
}
|
|
1295
|
-
/**
|
|
1296
|
-
* Debug logging
|
|
1297
|
-
*/
|
|
1298
983
|
log(...args) {
|
|
1299
984
|
if (this.debug) {
|
|
1300
985
|
console.log('[PhoneAuth]', ...args);
|
|
1301
986
|
}
|
|
1302
987
|
}
|
|
1303
|
-
/**
|
|
1304
|
-
* Determine if an error should trigger a retry
|
|
1305
|
-
*/
|
|
1306
988
|
shouldRetry(error) {
|
|
1307
989
|
var _a, _b, _c;
|
|
1308
|
-
// Don't retry on 4xx client errors (these are not transient)
|
|
1309
990
|
if (error.status && error.status >= 400 && error.status < 500) {
|
|
1310
991
|
return false;
|
|
1311
992
|
}
|
|
1312
|
-
// Don't retry on explicit user denial or unsupported browser
|
|
1313
|
-
// USER_DENIED cannot be retried automatically because the Digital Credentials API
|
|
1314
|
-
// requires user interaction (transient activation)
|
|
1315
993
|
const nonRetryableCodes = [
|
|
1316
994
|
'USER_DENIED',
|
|
1317
995
|
'BROWSER_NOT_SUPPORTED',
|
|
1318
996
|
'INVALID_PHONE_NUMBER',
|
|
1319
997
|
'INVALID_PARAMETERS',
|
|
1320
998
|
'MISSING_PARAMETERS',
|
|
1321
|
-
'UNPROCESSABLE_ENTITY',
|
|
1322
|
-
'USE_CASE_MISMATCH',
|
|
1323
|
-
'PHONE_NUMBER_MISMATCH',
|
|
1324
|
-
'VERIFICATION_FAILED',
|
|
1325
|
-
'INVALID_CREDENTIAL',
|
|
1326
|
-
'CARRIER_NOT_ELIGIBLE',
|
|
1327
|
-
'SESSION_EXPIRED',
|
|
1328
|
-
'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'
|
|
1329
1007
|
];
|
|
1330
1008
|
if (nonRetryableCodes.includes(error.code)) {
|
|
1331
1009
|
return false;
|
|
1332
1010
|
}
|
|
1333
|
-
// Only retry on network and server errors (5xx)
|
|
1334
1011
|
const retryableCodes = [
|
|
1335
1012
|
'NETWORK_ERROR',
|
|
1336
1013
|
'REQUEST_TIMEOUT',
|
|
@@ -1339,7 +1016,6 @@ class PhoneAuthClient {
|
|
|
1339
1016
|
'BAD_GATEWAY',
|
|
1340
1017
|
'INTERNAL_SERVER_ERROR'
|
|
1341
1018
|
];
|
|
1342
|
-
// Also check for cross-device error types in details
|
|
1343
1019
|
const isCrossDeviceError = ((_a = error.details) === null || _a === void 0 ? void 0 : _a.errorType) && [
|
|
1344
1020
|
'CROSS_DEVICE_TIMEOUT',
|
|
1345
1021
|
'CROSS_DEVICE_CONNECTION_LOST',
|
|
@@ -1349,17 +1025,13 @@ class PhoneAuthClient {
|
|
|
1349
1025
|
isCrossDeviceError ||
|
|
1350
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);
|
|
1351
1027
|
}
|
|
1352
|
-
/**
|
|
1353
|
-
* Analyze and enhance errors specific to cross-device flows
|
|
1354
|
-
*/
|
|
1355
1028
|
analyzeCrossDeviceError(error, prepareResponse) {
|
|
1356
1029
|
var _a;
|
|
1357
1030
|
const errorObj = error;
|
|
1358
|
-
// Check for specific cross-device error patterns
|
|
1359
1031
|
const isCrossDeviceTimeout = (errorObj.name === 'AbortError' && this.crossDeviceActive) ||
|
|
1360
1032
|
(((_a = errorObj.message) === null || _a === void 0 ? void 0 : _a.includes('timeout')) && this.crossDeviceActive);
|
|
1361
1033
|
const isCrossDeviceNetworkIssue = errorObj.name === 'NetworkError' &&
|
|
1362
|
-
errorObj.code !== 19 &&
|
|
1034
|
+
errorObj.code !== 19 &&
|
|
1363
1035
|
this.crossDeviceActive;
|
|
1364
1036
|
if (isCrossDeviceTimeout) {
|
|
1365
1037
|
return {
|
|
@@ -1387,12 +1059,8 @@ class PhoneAuthClient {
|
|
|
1387
1059
|
browserError: error.browserError
|
|
1388
1060
|
};
|
|
1389
1061
|
}
|
|
1390
|
-
// Return the original error if not cross-device specific
|
|
1391
1062
|
return error;
|
|
1392
1063
|
}
|
|
1393
|
-
/**
|
|
1394
|
-
* Cache successful session for retry scenarios
|
|
1395
|
-
*/
|
|
1396
1064
|
cacheSession(options, result) {
|
|
1397
1065
|
const cacheKey = this.getCacheKey(options);
|
|
1398
1066
|
this.sessionCache.set(cacheKey, {
|
|
@@ -1400,15 +1068,11 @@ class PhoneAuthClient {
|
|
|
1400
1068
|
data: result
|
|
1401
1069
|
});
|
|
1402
1070
|
}
|
|
1403
|
-
/**
|
|
1404
|
-
* Retrieve cached session if available and recent
|
|
1405
|
-
*/
|
|
1406
1071
|
getCachedSession(options) {
|
|
1407
1072
|
const cacheKey = this.getCacheKey(options);
|
|
1408
1073
|
const cached = this.sessionCache.get(cacheKey);
|
|
1409
1074
|
if (!cached)
|
|
1410
1075
|
return null;
|
|
1411
|
-
// Cache valid for 5 minutes
|
|
1412
1076
|
const cacheValidMs = 5 * 60 * 1000;
|
|
1413
1077
|
if (Date.now() - cached.timestamp > cacheValidMs) {
|
|
1414
1078
|
this.sessionCache.delete(cacheKey);
|
|
@@ -1416,22 +1080,14 @@ class PhoneAuthClient {
|
|
|
1416
1080
|
}
|
|
1417
1081
|
return cached.data;
|
|
1418
1082
|
}
|
|
1419
|
-
/**
|
|
1420
|
-
* Generate cache key for session storage
|
|
1421
|
-
*/
|
|
1422
1083
|
getCacheKey(options) {
|
|
1423
1084
|
var _a, _b;
|
|
1424
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) || ''}`;
|
|
1425
1086
|
}
|
|
1426
|
-
/**
|
|
1427
|
-
* Set up periodic cache cleanup
|
|
1428
|
-
*/
|
|
1429
1087
|
setupCacheCleanup() {
|
|
1430
|
-
// Only run cleanup in browser environment (not during SSR)
|
|
1431
1088
|
if (typeof window === 'undefined') {
|
|
1432
1089
|
return;
|
|
1433
1090
|
}
|
|
1434
|
-
// Clean up expired cache entries every minute
|
|
1435
1091
|
setInterval(() => {
|
|
1436
1092
|
const now = Date.now();
|
|
1437
1093
|
const cacheValidMs = 5 * 60 * 1000;
|
|
@@ -1442,9 +1098,6 @@ class PhoneAuthClient {
|
|
|
1442
1098
|
}
|
|
1443
1099
|
}, 60000);
|
|
1444
1100
|
}
|
|
1445
|
-
/**
|
|
1446
|
-
* Utility delay function
|
|
1447
|
-
*/
|
|
1448
1101
|
delay(ms) {
|
|
1449
1102
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
1450
1103
|
}
|