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