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