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