@glideidentity/web-client-sdk 4.4.8-beta.3 → 4.4.9
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 +395 -714
- package/dist/browser/web-client-sdk.min.js +1 -1
- package/dist/core/phone-auth/client.js +189 -73
- package/dist/core/phone-auth/strategies/desktop.d.ts +9 -3
- package/dist/core/phone-auth/strategies/desktop.js +47 -4
- package/dist/core/phone-auth/strategies/link.d.ts +8 -2
- package/dist/core/phone-auth/strategies/link.js +46 -10
- package/dist/core/phone-auth/types.d.ts +1 -1
- package/dist/core/phone-auth/ui/modal.d.ts +4 -0
- package/dist/core/phone-auth/ui/modal.js +17 -6
- package/dist/core/version.js +1 -1
- package/dist/esm/core/phone-auth/client.js +189 -73
- package/dist/esm/core/phone-auth/strategies/desktop.d.ts +9 -3
- package/dist/esm/core/phone-auth/strategies/desktop.js +47 -4
- package/dist/esm/core/phone-auth/strategies/link.d.ts +8 -2
- package/dist/esm/core/phone-auth/strategies/link.js +46 -10
- package/dist/esm/core/phone-auth/types.d.ts +1 -1
- package/dist/esm/core/phone-auth/ui/modal.d.ts +4 -0
- package/dist/esm/core/phone-auth/ui/modal.js +17 -6
- package/dist/esm/core/version.js +1 -1
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -3
- package/package.json +1 -1
|
@@ -17,8 +17,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
17
17
|
exports.LinkHandler = void 0;
|
|
18
18
|
class LinkHandler {
|
|
19
19
|
constructor() {
|
|
20
|
-
this.
|
|
20
|
+
this.isPollingActive = false;
|
|
21
21
|
this.isCancelled = false;
|
|
22
|
+
this.isPollingInProgress = false;
|
|
22
23
|
}
|
|
23
24
|
/**
|
|
24
25
|
* Invoke link-based authentication
|
|
@@ -26,7 +27,6 @@ class LinkHandler {
|
|
|
26
27
|
*/
|
|
27
28
|
invoke(data, options) {
|
|
28
29
|
return __awaiter(this, void 0, void 0, function* () {
|
|
29
|
-
console.log('[Link Auth] 🔗 SDK Version: 4.4.8-beta.2 (with enhanced error logging)');
|
|
30
30
|
console.log('[Link Auth] 🔗 invoke() called with data:', JSON.stringify(data, null, 2));
|
|
31
31
|
console.log('[Link Auth] Options:', options ? JSON.stringify({
|
|
32
32
|
pollingInterval: options.pollingInterval,
|
|
@@ -70,7 +70,7 @@ class LinkHandler {
|
|
|
70
70
|
startPolling(sessionKey, linkData, options) {
|
|
71
71
|
return __awaiter(this, void 0, void 0, function* () {
|
|
72
72
|
const interval = (options === null || options === void 0 ? void 0 : options.pollingInterval) || 2000; // Fixed 2 second interval
|
|
73
|
-
const maxAttempts = (options === null || options === void 0 ? void 0 : options.maxPollingAttempts) ||
|
|
73
|
+
const maxAttempts = (options === null || options === void 0 ? void 0 : options.maxPollingAttempts) || 30; // 1 minute with 2s interval
|
|
74
74
|
let attempts = 0;
|
|
75
75
|
console.log('[Link Auth] 🚀 Starting polling:', {
|
|
76
76
|
sessionKey,
|
|
@@ -79,27 +79,37 @@ class LinkHandler {
|
|
|
79
79
|
linkDataAvailable: !!linkData
|
|
80
80
|
});
|
|
81
81
|
return new Promise((resolve, reject) => {
|
|
82
|
-
this.
|
|
82
|
+
this.isPollingActive = true;
|
|
83
|
+
this.pollingReject = reject; // Store reject function for cancel()
|
|
83
84
|
const poll = () => __awaiter(this, void 0, void 0, function* () {
|
|
84
|
-
if (!this.
|
|
85
|
+
if (!this.isPollingActive) {
|
|
85
86
|
return; // Polling was stopped
|
|
86
87
|
}
|
|
88
|
+
// Skip if another poll is already in progress
|
|
89
|
+
if (this.isPollingInProgress) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
87
92
|
let statusUrl = ''; // Declare at function scope for catch block access
|
|
88
93
|
try {
|
|
89
|
-
|
|
90
|
-
// Check max attempts
|
|
94
|
+
this.isPollingInProgress = true;
|
|
95
|
+
// Check max attempts before making the request
|
|
91
96
|
if (attempts >= maxAttempts) {
|
|
92
97
|
this.stopPolling();
|
|
93
98
|
if (options === null || options === void 0 ? void 0 : options.onTimeout) {
|
|
94
99
|
options.onTimeout();
|
|
95
100
|
}
|
|
101
|
+
// Calculate actual timeout duration
|
|
102
|
+
const timeoutSeconds = Math.round((maxAttempts * interval) / 1000);
|
|
103
|
+
const timeoutMessage = timeoutSeconds >= 60
|
|
104
|
+
? `${Math.floor(timeoutSeconds / 60)} minute${Math.floor(timeoutSeconds / 60) > 1 ? 's' : ''}`
|
|
105
|
+
: `${timeoutSeconds} seconds`;
|
|
96
106
|
if (options === null || options === void 0 ? void 0 : options.onStatusUpdate) {
|
|
97
107
|
options.onStatusUpdate({
|
|
98
108
|
status: 'expired',
|
|
99
|
-
message:
|
|
109
|
+
message: `Authentication timeout after ${timeoutMessage}`
|
|
100
110
|
});
|
|
101
111
|
}
|
|
102
|
-
reject(new Error(
|
|
112
|
+
reject(new Error(`Authentication timeout after ${timeoutMessage}`));
|
|
103
113
|
return;
|
|
104
114
|
}
|
|
105
115
|
// Build public status endpoint URL
|
|
@@ -178,6 +188,7 @@ class LinkHandler {
|
|
|
178
188
|
});
|
|
179
189
|
}
|
|
180
190
|
// Return the authentication result
|
|
191
|
+
this.pollingReject = undefined; // Clear reject function on success
|
|
181
192
|
resolve({
|
|
182
193
|
authenticated: true,
|
|
183
194
|
credential: result.credential || sessionKey,
|
|
@@ -193,6 +204,7 @@ class LinkHandler {
|
|
|
193
204
|
else if (result.status === 'pending') {
|
|
194
205
|
// Continue polling
|
|
195
206
|
console.log('[Link Auth] Status still pending, continuing to poll...');
|
|
207
|
+
attempts++; // Increment attempts after successful poll
|
|
196
208
|
if (options === null || options === void 0 ? void 0 : options.onStatusUpdate) {
|
|
197
209
|
options.onStatusUpdate({
|
|
198
210
|
status: 'pending',
|
|
@@ -203,6 +215,7 @@ class LinkHandler {
|
|
|
203
215
|
else {
|
|
204
216
|
// Unexpected status value
|
|
205
217
|
console.log('[Link Auth] ⚠️ Unexpected status value:', result.status, 'Full result:', JSON.stringify(result, null, 2));
|
|
218
|
+
attempts++; // Increment for unexpected status too
|
|
206
219
|
}
|
|
207
220
|
}
|
|
208
221
|
else if (response.status === 410) {
|
|
@@ -263,6 +276,7 @@ class LinkHandler {
|
|
|
263
276
|
else {
|
|
264
277
|
// Unexpected status - continue polling
|
|
265
278
|
console.log('[Link Auth] ⚠️ Unexpected HTTP status:', response.status, 'continuing to poll...');
|
|
279
|
+
attempts++; // Increment for unexpected HTTP status
|
|
266
280
|
try {
|
|
267
281
|
const body = yield response.text();
|
|
268
282
|
console.log('[Link Auth] Response body:', body);
|
|
@@ -275,6 +289,7 @@ class LinkHandler {
|
|
|
275
289
|
catch (error) {
|
|
276
290
|
// Network or other error - continue polling
|
|
277
291
|
console.error('[Link Auth] 🔴 Polling error:', error.message || error);
|
|
292
|
+
attempts++; // Increment for error case
|
|
278
293
|
console.error('[Link Auth] Error details:', {
|
|
279
294
|
name: error.name,
|
|
280
295
|
message: error.message,
|
|
@@ -295,6 +310,10 @@ class LinkHandler {
|
|
|
295
310
|
});
|
|
296
311
|
}
|
|
297
312
|
}
|
|
313
|
+
finally {
|
|
314
|
+
// Always clear the polling flag when done
|
|
315
|
+
this.isPollingInProgress = false;
|
|
316
|
+
}
|
|
298
317
|
});
|
|
299
318
|
// Start initial poll immediately
|
|
300
319
|
poll();
|
|
@@ -308,7 +327,8 @@ class LinkHandler {
|
|
|
308
327
|
*/
|
|
309
328
|
stopPolling() {
|
|
310
329
|
console.log('[Link Auth] 🏁 Stopping polling');
|
|
311
|
-
this.
|
|
330
|
+
this.isPollingActive = false;
|
|
331
|
+
this.isPollingInProgress = false;
|
|
312
332
|
if (this.pollingInterval) {
|
|
313
333
|
clearInterval(this.pollingInterval);
|
|
314
334
|
this.pollingInterval = undefined;
|
|
@@ -343,15 +363,31 @@ class LinkHandler {
|
|
|
343
363
|
this.stopPolling();
|
|
344
364
|
this.isCancelled = false;
|
|
345
365
|
this.onCancel = undefined;
|
|
366
|
+
this.pollingReject = undefined;
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Check if polling is currently active
|
|
370
|
+
*/
|
|
371
|
+
isPolling() {
|
|
372
|
+
return this.pollingInterval !== undefined;
|
|
346
373
|
}
|
|
347
374
|
/**
|
|
348
375
|
* Cancel the ongoing authentication
|
|
349
376
|
*/
|
|
350
377
|
cancel() {
|
|
351
378
|
var _a;
|
|
379
|
+
console.log('[Link Auth] Cancelling authentication');
|
|
352
380
|
this.isCancelled = true;
|
|
353
381
|
this.stopPolling();
|
|
354
382
|
(_a = this.onCancel) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
383
|
+
// Immediately reject the polling promise
|
|
384
|
+
if (this.pollingReject) {
|
|
385
|
+
this.pollingReject({
|
|
386
|
+
code: 'USER_DENIED',
|
|
387
|
+
message: 'Authentication cancelled by user'
|
|
388
|
+
});
|
|
389
|
+
this.pollingReject = undefined;
|
|
390
|
+
}
|
|
355
391
|
}
|
|
356
392
|
}
|
|
357
393
|
exports.LinkHandler = LinkHandler;
|
|
@@ -72,7 +72,7 @@ export interface AuthConfig extends PhoneAuthCallbacks {
|
|
|
72
72
|
pollingInterval?: number;
|
|
73
73
|
/**
|
|
74
74
|
* Maximum polling attempts before timeout
|
|
75
|
-
* @default
|
|
75
|
+
* @default 30 (1 minute with 2s interval)
|
|
76
76
|
*/
|
|
77
77
|
maxPollingAttempts?: number;
|
|
78
78
|
/**
|
|
@@ -26,6 +26,10 @@ export declare class AuthModal {
|
|
|
26
26
|
private closeCallback?;
|
|
27
27
|
constructor(options?: InvokeOptions['modalOptions'], callbacks?: InvokeOptions['callbacks']);
|
|
28
28
|
private handleEscapeKey;
|
|
29
|
+
/**
|
|
30
|
+
* Escape HTML to prevent XSS attacks
|
|
31
|
+
*/
|
|
32
|
+
private escapeHtml;
|
|
29
33
|
/**
|
|
30
34
|
* Shows the modal with a QR code for desktop authentication
|
|
31
35
|
* Supports both single QR code (legacy) and dual-platform QR codes (iOS + Android)
|
|
@@ -47,6 +47,17 @@ class AuthModal {
|
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* Escape HTML to prevent XSS attacks
|
|
52
|
+
*/
|
|
53
|
+
escapeHtml(unsafe) {
|
|
54
|
+
return unsafe
|
|
55
|
+
.replace(/&/g, "&")
|
|
56
|
+
.replace(/</g, "<")
|
|
57
|
+
.replace(/>/g, ">")
|
|
58
|
+
.replace(/"/g, """)
|
|
59
|
+
.replace(/'/g, "'");
|
|
60
|
+
}
|
|
50
61
|
/**
|
|
51
62
|
* Shows the modal with a QR code for desktop authentication
|
|
52
63
|
* Supports both single QR code (legacy) and dual-platform QR codes (iOS + Android)
|
|
@@ -75,7 +86,7 @@ class AuthModal {
|
|
|
75
86
|
// Only iOS QR code available - show single QR
|
|
76
87
|
this.createModal(`
|
|
77
88
|
<div class="glide-auth-qr-container">
|
|
78
|
-
<img src="${qrCodeData.iosQRCode}" alt="QR Code" class="glide-auth-qr-code" />
|
|
89
|
+
<img src="${this.escapeHtml(qrCodeData.iosQRCode)}" alt="QR Code" class="glide-auth-qr-code" />
|
|
79
90
|
<p class="glide-auth-status">Scan with your iPhone to authenticate</p>
|
|
80
91
|
</div>
|
|
81
92
|
`);
|
|
@@ -86,8 +97,8 @@ class AuthModal {
|
|
|
86
97
|
// Legacy single QR code
|
|
87
98
|
this.createModal(`
|
|
88
99
|
<div class="glide-auth-qr-container">
|
|
89
|
-
<img src="${qrCodeData}" alt="QR Code" class="glide-auth-qr-code" />
|
|
90
|
-
<p class="glide-auth-status">${statusMessage}</p>
|
|
100
|
+
<img src="${this.escapeHtml(qrCodeData)}" alt="QR Code" class="glide-auth-qr-code" />
|
|
101
|
+
<p class="glide-auth-status">${this.escapeHtml(statusMessage)}</p>
|
|
91
102
|
</div>
|
|
92
103
|
`);
|
|
93
104
|
}
|
|
@@ -118,11 +129,11 @@ class AuthModal {
|
|
|
118
129
|
<!-- QR Code Image -->
|
|
119
130
|
<img
|
|
120
131
|
id="glide-qr-code-img"
|
|
121
|
-
src="${qrCodeData.iosQRCode}"
|
|
132
|
+
src="${this.escapeHtml(qrCodeData.iosQRCode)}"
|
|
122
133
|
alt="QR Code"
|
|
123
134
|
class="glide-auth-qr-code"
|
|
124
|
-
data-ios="${qrCodeData.iosQRCode}"
|
|
125
|
-
data-android="${qrCodeData.androidQRCode}"
|
|
135
|
+
data-ios="${this.escapeHtml(qrCodeData.iosQRCode)}"
|
|
136
|
+
data-android="${this.escapeHtml(qrCodeData.androidQRCode || '')}"
|
|
126
137
|
/>
|
|
127
138
|
|
|
128
139
|
<!-- Status Message -->
|
package/dist/core/version.js
CHANGED
|
@@ -170,6 +170,10 @@ export class PhoneAuthClient {
|
|
|
170
170
|
}
|
|
171
171
|
// Step 3: Process the response through appropriate endpoint
|
|
172
172
|
const credential = credentialResponse;
|
|
173
|
+
// Validate use_case is provided for endpoint selection
|
|
174
|
+
if (!options.use_case) {
|
|
175
|
+
throw this.createError(PhoneAuthErrorCode.MISSING_PARAMETERS, 'use_case is required', { field: 'use_case' });
|
|
176
|
+
}
|
|
173
177
|
const result = options.use_case === API.USE_CASE.GET_PHONE_NUMBER
|
|
174
178
|
? yield this.getPhoneNumber(credential, preparedRequest.session)
|
|
175
179
|
: yield this.verifyPhoneNumber(credential, preparedRequest.session);
|
|
@@ -278,16 +282,7 @@ export class PhoneAuthClient {
|
|
|
278
282
|
*/
|
|
279
283
|
preparePhoneRequest(options) {
|
|
280
284
|
return __awaiter(this, void 0, void 0, function* () {
|
|
281
|
-
|
|
282
|
-
// const useCaseValidation = validateUseCaseRequirements(options.use_case, options.phone_number);
|
|
283
|
-
// if (!useCaseValidation.valid) {
|
|
284
|
-
// throw this.createError(
|
|
285
|
-
// PhoneAuthErrorCode.BAD_REQUEST,
|
|
286
|
-
// useCaseValidation.error!,
|
|
287
|
-
// { field: 'use_case' }
|
|
288
|
-
// );
|
|
289
|
-
// }
|
|
290
|
-
var _a, _b;
|
|
285
|
+
var _a, _b, _c;
|
|
291
286
|
// Validate phone number if provided
|
|
292
287
|
if (options.phone_number) {
|
|
293
288
|
const phoneValidation = validatePhoneNumber(options.phone_number);
|
|
@@ -309,8 +304,12 @@ export class PhoneAuthClient {
|
|
|
309
304
|
throw this.createError(PhoneAuthErrorCode.BAD_REQUEST, consentValidation.error, { field: 'consent_data' });
|
|
310
305
|
}
|
|
311
306
|
}
|
|
307
|
+
// Validate use_case is provided (unless only parent_session_id is given)
|
|
308
|
+
if (!options.use_case && !(((_a = options.options) === null || _a === void 0 ? void 0 : _a.parent_session_id) && !options.phone_number && !options.plmn)) {
|
|
309
|
+
throw this.createError(PhoneAuthErrorCode.MISSING_PARAMETERS, 'use_case is required', { field: 'use_case' });
|
|
310
|
+
}
|
|
312
311
|
// Validate required parameters based on use case
|
|
313
|
-
if (!options.phone_number && !options.plmn && !((
|
|
312
|
+
if (!options.phone_number && !options.plmn && !((_b = options.options) === null || _b === void 0 ? void 0 : _b.parent_session_id)) {
|
|
314
313
|
// Provide specific error message based on use case
|
|
315
314
|
if (options.use_case === API.USE_CASE.GET_PHONE_NUMBER) {
|
|
316
315
|
throw this.createError(PhoneAuthErrorCode.MISSING_PARAMETERS, 'PLMN (MCC/MNC) is required for GetPhoneNumber. Please provide carrier network information.', { field: 'plmn', useCase: 'GetPhoneNumber' });
|
|
@@ -318,19 +317,15 @@ export class PhoneAuthClient {
|
|
|
318
317
|
else if (options.use_case === API.USE_CASE.VERIFY_PHONE_NUMBER) {
|
|
319
318
|
throw this.createError(PhoneAuthErrorCode.MISSING_PARAMETERS, 'Phone number is required for VerifyPhoneNumber', { field: 'phoneNumber', useCase: 'VerifyPhoneNumber' });
|
|
320
319
|
}
|
|
321
|
-
else if (!options.use_case) {
|
|
322
|
-
// If no use case, that's an error
|
|
323
|
-
throw this.createError(PhoneAuthErrorCode.MISSING_PARAMETERS, 'use_case is required', { field: 'use_case' });
|
|
324
|
-
}
|
|
325
320
|
else {
|
|
326
321
|
// Fallback for other use cases
|
|
327
322
|
throw this.createError(PhoneAuthErrorCode.MISSING_PARAMETERS, 'Either phone number or PLMN (MCC/MNC) must be provided', { field: 'phoneNumber,plmn' });
|
|
328
323
|
}
|
|
329
324
|
}
|
|
330
|
-
//
|
|
331
|
-
if (((
|
|
325
|
+
// Log parent session usage
|
|
326
|
+
if (((_c = options.options) === null || _c === void 0 ? void 0 : _c.parent_session_id) && !options.phone_number && !options.plmn) {
|
|
332
327
|
if (this.debug) {
|
|
333
|
-
console.log('[PhoneAuth] Using parent_session_id
|
|
328
|
+
console.log('[PhoneAuth] Using parent_session_id: %s, use_case: %s', options.options.parent_session_id, options.use_case || 'not provided');
|
|
334
329
|
}
|
|
335
330
|
}
|
|
336
331
|
const requestId = `web-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
@@ -345,11 +340,13 @@ export class PhoneAuthClient {
|
|
|
345
340
|
throw this.createError(PhoneAuthErrorCode.INTERNAL_SERVER_ERROR, 'Failed to generate valid nonce', { field: 'nonce' });
|
|
346
341
|
}
|
|
347
342
|
// Build properly typed request body according to API specification
|
|
343
|
+
// Be permissive - backend will ignore extra fields if not needed
|
|
348
344
|
const requestBody = {
|
|
349
|
-
//
|
|
345
|
+
// Include use_case if provided (optional when parent_session_id is given)
|
|
350
346
|
use_case: options.use_case,
|
|
351
|
-
//
|
|
347
|
+
// Include phone_number if provided (backend ignores if not needed)
|
|
352
348
|
phone_number: options.phone_number,
|
|
349
|
+
// Include PLMN if provided
|
|
353
350
|
plmn: options.plmn ? {
|
|
354
351
|
mcc: options.plmn.mcc,
|
|
355
352
|
mnc: options.plmn.mnc
|
|
@@ -381,7 +378,7 @@ export class PhoneAuthClient {
|
|
|
381
378
|
// Always include the HTTP status from the response
|
|
382
379
|
errorDetails = Object.assign(Object.assign({}, errorDetails), { status: response.status });
|
|
383
380
|
}
|
|
384
|
-
catch (
|
|
381
|
+
catch (_d) {
|
|
385
382
|
// If JSON parsing fails, use status text
|
|
386
383
|
errorDetails = { status: response.status, statusText: response.statusText };
|
|
387
384
|
}
|
|
@@ -649,12 +646,15 @@ export class PhoneAuthClient {
|
|
|
649
646
|
});
|
|
650
647
|
// TS43 always auto-triggers (no SDK UI ever)
|
|
651
648
|
// The Digital Credentials API provides its own OS-level UI
|
|
652
|
-
|
|
649
|
+
// Use a wrapper object to allow updating the promise reference
|
|
650
|
+
const credentialWrapper = {
|
|
651
|
+
promise: null
|
|
652
|
+
};
|
|
653
653
|
try {
|
|
654
654
|
// Always try to trigger immediately
|
|
655
655
|
const vpToken = yield enhancedTriggerTS43();
|
|
656
656
|
// Convert to AuthCredential format
|
|
657
|
-
|
|
657
|
+
credentialWrapper.promise = Promise.resolve({
|
|
658
658
|
credential: typeof vpToken === 'string' ? vpToken : Object.values(vpToken)[0],
|
|
659
659
|
session: plainResponse.session,
|
|
660
660
|
authenticated: true
|
|
@@ -662,36 +662,46 @@ export class PhoneAuthClient {
|
|
|
662
662
|
}
|
|
663
663
|
catch (error) {
|
|
664
664
|
// If auto-trigger fails, create a rejected promise
|
|
665
|
-
|
|
665
|
+
credentialWrapper.promise = Promise.reject(error);
|
|
666
666
|
}
|
|
667
667
|
// Handle based on execution mode
|
|
668
668
|
if (executionMode === 'extended') {
|
|
669
669
|
// Extended mode - return control methods
|
|
670
|
-
|
|
670
|
+
const response = {
|
|
671
671
|
strategy: 'ts43',
|
|
672
672
|
session: plainResponse.session,
|
|
673
|
-
credential:
|
|
673
|
+
credential: credentialWrapper.promise, // Initial value
|
|
674
674
|
// Re-trigger credential request
|
|
675
675
|
trigger: () => __awaiter(this, void 0, void 0, function* () {
|
|
676
676
|
const vpToken = yield enhancedTriggerTS43();
|
|
677
|
-
// Update the credential promise
|
|
678
|
-
|
|
677
|
+
// Update the credential promise in the wrapper
|
|
678
|
+
credentialWrapper.promise = Promise.resolve({
|
|
679
679
|
credential: typeof vpToken === 'string' ? vpToken : Object.values(vpToken)[0],
|
|
680
680
|
session: plainResponse.session,
|
|
681
681
|
authenticated: true
|
|
682
682
|
});
|
|
683
|
+
// Return void as per interface
|
|
683
684
|
}),
|
|
684
685
|
cancel: () => {
|
|
685
686
|
// TS43 doesn't have a way to cancel once triggered
|
|
686
687
|
// but we can reject the promise
|
|
687
|
-
|
|
688
|
+
credentialWrapper.promise = Promise.reject(this.createError(PhoneAuthErrorCode.USER_DENIED, 'Authentication cancelled'));
|
|
688
689
|
}
|
|
689
690
|
};
|
|
691
|
+
// Define credential as a getter that always returns the current promise
|
|
692
|
+
Object.defineProperty(response, 'credential', {
|
|
693
|
+
get() {
|
|
694
|
+
return credentialWrapper.promise;
|
|
695
|
+
},
|
|
696
|
+
enumerable: true,
|
|
697
|
+
configurable: true
|
|
698
|
+
});
|
|
699
|
+
return response;
|
|
690
700
|
}
|
|
691
701
|
else {
|
|
692
702
|
// Standard mode - just return credential
|
|
693
703
|
// Wait for and return the credential
|
|
694
|
-
const credential = yield
|
|
704
|
+
const credential = yield credentialWrapper.promise;
|
|
695
705
|
// Return in standard format
|
|
696
706
|
return {
|
|
697
707
|
[plainResponse.session.session_key]: credential.credential
|
|
@@ -721,7 +731,7 @@ export class PhoneAuthClient {
|
|
|
721
731
|
const pollingEndpointToUse = (invokeOptions === null || invokeOptions === void 0 ? void 0 : invokeOptions.pollingEndpoint) ||
|
|
722
732
|
(desktopOptions === null || desktopOptions === void 0 ? void 0 : desktopOptions.pollingEndpoint) ||
|
|
723
733
|
((_m = this.config.endpoints) === null || _m === void 0 ? void 0 : _m.polling);
|
|
724
|
-
const pollingOptions = Object.assign(Object.assign({}, desktopOptions), { pollingEndpoint: pollingEndpointToUse, pollingInterval: (invokeOptions === null || invokeOptions === void 0 ? void 0 : invokeOptions.pollingInterval) || (desktopOptions === null || desktopOptions === void 0 ? void 0 : desktopOptions.pollingInterval) || this.config.pollingInterval || 2000, maxPollingAttempts: (invokeOptions === null || invokeOptions === void 0 ? void 0 : invokeOptions.maxPollingAttempts) || (desktopOptions === null || desktopOptions === void 0 ? void 0 : desktopOptions.maxPollingAttempts) || this.config.maxPollingAttempts ||
|
|
734
|
+
const pollingOptions = Object.assign(Object.assign({}, desktopOptions), { pollingEndpoint: pollingEndpointToUse, pollingInterval: (invokeOptions === null || invokeOptions === void 0 ? void 0 : invokeOptions.pollingInterval) || (desktopOptions === null || desktopOptions === void 0 ? void 0 : desktopOptions.pollingInterval) || this.config.pollingInterval || 2000, maxPollingAttempts: (invokeOptions === null || invokeOptions === void 0 ? void 0 : invokeOptions.maxPollingAttempts) || (desktopOptions === null || desktopOptions === void 0 ? void 0 : desktopOptions.maxPollingAttempts) || this.config.maxPollingAttempts || 30, onQRCodeReady: undefined, onStatusUpdate: undefined });
|
|
725
735
|
// Decide whether to show modal based on preventDefaultUI
|
|
726
736
|
const showModal = !preventDefaultUI;
|
|
727
737
|
let modal;
|
|
@@ -784,31 +794,79 @@ export class PhoneAuthClient {
|
|
|
784
794
|
// Handle based on execution mode
|
|
785
795
|
if (executionMode === 'extended') {
|
|
786
796
|
// Extended mode - return control methods
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
797
|
+
let credentialResolve = null;
|
|
798
|
+
let credentialReject = null;
|
|
799
|
+
let pollingStarted = false;
|
|
800
|
+
// Create a promise that will remain pending until polling starts
|
|
801
|
+
const credentialPromise = new Promise((resolve, reject) => {
|
|
802
|
+
credentialResolve = resolve;
|
|
803
|
+
credentialReject = reject;
|
|
804
|
+
// If modal is shown, start polling immediately
|
|
805
|
+
if (showModal) {
|
|
806
|
+
pollingStarted = true;
|
|
807
|
+
startPolling()
|
|
808
|
+
.then(resolve)
|
|
809
|
+
.catch(reject);
|
|
810
|
+
}
|
|
811
|
+
// Otherwise, promise remains pending until start_polling() is called
|
|
791
812
|
});
|
|
792
|
-
|
|
813
|
+
// Create wrapped functions
|
|
814
|
+
const wrappedStartPolling = () => __awaiter(this, void 0, void 0, function* () {
|
|
815
|
+
if (pollingStarted) {
|
|
816
|
+
throw this.createError(PhoneAuthErrorCode.INVALID_SESSION_STATE, 'Polling has already been started');
|
|
817
|
+
}
|
|
818
|
+
pollingStarted = true;
|
|
819
|
+
try {
|
|
820
|
+
const result = yield startPolling();
|
|
821
|
+
if (credentialResolve) {
|
|
822
|
+
credentialResolve(result);
|
|
823
|
+
}
|
|
824
|
+
return result;
|
|
825
|
+
}
|
|
826
|
+
catch (err) {
|
|
827
|
+
if (credentialReject) {
|
|
828
|
+
credentialReject(err);
|
|
829
|
+
}
|
|
830
|
+
throw err;
|
|
831
|
+
}
|
|
832
|
+
});
|
|
833
|
+
const wrappedStopPolling = () => {
|
|
834
|
+
handler.cleanup();
|
|
835
|
+
if (modal)
|
|
836
|
+
modal.close();
|
|
837
|
+
};
|
|
838
|
+
const wrappedCancel = () => {
|
|
839
|
+
handler.cancel();
|
|
840
|
+
handler.cleanup();
|
|
841
|
+
if (modal)
|
|
842
|
+
modal.close();
|
|
843
|
+
// Reject the credential promise if it's still pending
|
|
844
|
+
if (!pollingStarted && credentialReject) {
|
|
845
|
+
credentialReject(this.createError(PhoneAuthErrorCode.USER_DENIED, 'Desktop authentication cancelled by user'));
|
|
846
|
+
}
|
|
847
|
+
};
|
|
848
|
+
// Create the response object with a getter for is_polling
|
|
849
|
+
const response = {
|
|
793
850
|
strategy: 'desktop',
|
|
794
851
|
session: plainResponse.session,
|
|
795
852
|
credential: credentialPromise,
|
|
796
853
|
qr_code_data: qrCodeDataSnakeCase,
|
|
797
854
|
modal_ref: modalRef,
|
|
798
|
-
start_polling:
|
|
799
|
-
stop_polling:
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
},
|
|
804
|
-
cancel: () => {
|
|
805
|
-
handler.cancel();
|
|
806
|
-
handler.cleanup();
|
|
807
|
-
if (modal)
|
|
808
|
-
modal.close();
|
|
809
|
-
},
|
|
810
|
-
is_polling: showModal // If modal shown, polling already started
|
|
855
|
+
start_polling: wrappedStartPolling,
|
|
856
|
+
stop_polling: wrappedStopPolling,
|
|
857
|
+
cancel: wrappedCancel,
|
|
858
|
+
// This will be replaced with a getter
|
|
859
|
+
is_polling: false // Initial value, will be overridden by getter
|
|
811
860
|
};
|
|
861
|
+
// Define is_polling as a getter that returns current state
|
|
862
|
+
Object.defineProperty(response, 'is_polling', {
|
|
863
|
+
get() {
|
|
864
|
+
return handler.isPolling();
|
|
865
|
+
},
|
|
866
|
+
enumerable: true,
|
|
867
|
+
configurable: true
|
|
868
|
+
});
|
|
869
|
+
return response;
|
|
812
870
|
}
|
|
813
871
|
else {
|
|
814
872
|
// Standard mode - return credential when complete
|
|
@@ -872,28 +930,66 @@ export class PhoneAuthClient {
|
|
|
872
930
|
const pollingOptions = {
|
|
873
931
|
pollingEndpoint: (invokeOptions === null || invokeOptions === void 0 ? void 0 : invokeOptions.pollingEndpoint) || ((_q = this.config.endpoints) === null || _q === void 0 ? void 0 : _q.polling),
|
|
874
932
|
pollingInterval: (invokeOptions === null || invokeOptions === void 0 ? void 0 : invokeOptions.pollingInterval) || this.config.pollingInterval || 2000,
|
|
875
|
-
maxPollingAttempts: (invokeOptions === null || invokeOptions === void 0 ? void 0 : invokeOptions.maxPollingAttempts) || this.config.maxPollingAttempts ||
|
|
933
|
+
maxPollingAttempts: (invokeOptions === null || invokeOptions === void 0 ? void 0 : invokeOptions.maxPollingAttempts) || this.config.maxPollingAttempts || 30,
|
|
876
934
|
onLinkOpened: undefined,
|
|
877
935
|
onStatusUpdate: undefined
|
|
878
936
|
};
|
|
879
937
|
console.log('[PhoneAuth Client] Final Link polling options:', pollingOptions);
|
|
880
|
-
// Create credential promise
|
|
881
|
-
const credentialPromise = handler.invoke(plainResponse, pollingOptions).then(result => {
|
|
882
|
-
if (result.authenticated && result.credential) {
|
|
883
|
-
return {
|
|
884
|
-
credential: result.credential,
|
|
885
|
-
session: plainResponse.session,
|
|
886
|
-
authenticated: true
|
|
887
|
-
};
|
|
888
|
-
}
|
|
889
|
-
else {
|
|
890
|
-
throw this.createError(PhoneAuthErrorCode.USER_DENIED, result.error || 'Link authentication failed');
|
|
891
|
-
}
|
|
892
|
-
});
|
|
893
938
|
// Handle based on execution mode
|
|
894
939
|
if (executionMode === 'extended') {
|
|
895
|
-
// Extended mode - return control methods
|
|
896
|
-
|
|
940
|
+
// Extended mode - return control methods and start polling immediately
|
|
941
|
+
let pollingStarted = false;
|
|
942
|
+
let pollingPromise = null;
|
|
943
|
+
// Start polling immediately (Link always polls automatically unlike Desktop)
|
|
944
|
+
pollingPromise = handler.invoke(plainResponse, pollingOptions);
|
|
945
|
+
pollingStarted = true;
|
|
946
|
+
// Create credential promise from the polling result
|
|
947
|
+
const credentialPromise = pollingPromise.then(result => {
|
|
948
|
+
if (result.authenticated && result.credential) {
|
|
949
|
+
return {
|
|
950
|
+
credential: result.credential,
|
|
951
|
+
session: plainResponse.session,
|
|
952
|
+
authenticated: true
|
|
953
|
+
};
|
|
954
|
+
}
|
|
955
|
+
else {
|
|
956
|
+
throw this.createError(PhoneAuthErrorCode.USER_DENIED, result.error || 'Link authentication failed');
|
|
957
|
+
}
|
|
958
|
+
});
|
|
959
|
+
// Function to restart polling (for retry scenarios)
|
|
960
|
+
const startPolling = () => __awaiter(this, void 0, void 0, function* () {
|
|
961
|
+
if (!pollingStarted) {
|
|
962
|
+
// This is here for API consistency, but for Link it's already polling
|
|
963
|
+
pollingStarted = true;
|
|
964
|
+
if (!pollingPromise) {
|
|
965
|
+
pollingPromise = handler.invoke(plainResponse, pollingOptions);
|
|
966
|
+
}
|
|
967
|
+
const result = yield pollingPromise;
|
|
968
|
+
if (result.authenticated && result.credential) {
|
|
969
|
+
return {
|
|
970
|
+
credential: result.credential,
|
|
971
|
+
session: plainResponse.session,
|
|
972
|
+
authenticated: true
|
|
973
|
+
};
|
|
974
|
+
}
|
|
975
|
+
else {
|
|
976
|
+
throw this.createError(PhoneAuthErrorCode.USER_DENIED, result.error || 'Link authentication failed');
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
// If already polling, just return the existing promise result
|
|
980
|
+
const result = yield pollingPromise;
|
|
981
|
+
if (result.authenticated && result.credential) {
|
|
982
|
+
return {
|
|
983
|
+
credential: result.credential,
|
|
984
|
+
session: plainResponse.session,
|
|
985
|
+
authenticated: true
|
|
986
|
+
};
|
|
987
|
+
}
|
|
988
|
+
else {
|
|
989
|
+
throw this.createError(PhoneAuthErrorCode.USER_DENIED, result.error || 'Link authentication failed');
|
|
990
|
+
}
|
|
991
|
+
});
|
|
992
|
+
const response = {
|
|
897
993
|
strategy: 'link',
|
|
898
994
|
session: plainResponse.session,
|
|
899
995
|
credential: credentialPromise,
|
|
@@ -902,22 +998,42 @@ export class PhoneAuthClient {
|
|
|
902
998
|
},
|
|
903
999
|
// Re-open the app link
|
|
904
1000
|
trigger: triggerLink,
|
|
905
|
-
// Polling control
|
|
906
|
-
start_polling:
|
|
907
|
-
credential: result.credential || '',
|
|
908
|
-
session: plainResponse.session,
|
|
909
|
-
authenticated: result.authenticated
|
|
910
|
-
})),
|
|
1001
|
+
// Polling control - now prevents double polling
|
|
1002
|
+
start_polling: startPolling,
|
|
911
1003
|
stop_polling: () => handler.cleanup(),
|
|
912
1004
|
cancel: () => {
|
|
913
1005
|
handler.cancel();
|
|
914
1006
|
handler.cleanup();
|
|
1007
|
+
// Note: For Link, polling is already started, so the promise
|
|
1008
|
+
// will be rejected by the handler.cancel() call
|
|
915
1009
|
},
|
|
916
|
-
|
|
1010
|
+
// This will be replaced with a getter
|
|
1011
|
+
is_polling: false // Initial value, will be overridden by getter
|
|
917
1012
|
};
|
|
1013
|
+
// Define is_polling as a getter that returns current state
|
|
1014
|
+
Object.defineProperty(response, 'is_polling', {
|
|
1015
|
+
get() {
|
|
1016
|
+
return handler.isPolling();
|
|
1017
|
+
},
|
|
1018
|
+
enumerable: true,
|
|
1019
|
+
configurable: true
|
|
1020
|
+
});
|
|
1021
|
+
return response;
|
|
918
1022
|
}
|
|
919
1023
|
else {
|
|
920
|
-
// Standard mode -
|
|
1024
|
+
// Standard mode - start polling immediately and wait for completion
|
|
1025
|
+
const credentialPromise = handler.invoke(plainResponse, pollingOptions).then(result => {
|
|
1026
|
+
if (result.authenticated && result.credential) {
|
|
1027
|
+
return {
|
|
1028
|
+
credential: result.credential,
|
|
1029
|
+
session: plainResponse.session,
|
|
1030
|
+
authenticated: true
|
|
1031
|
+
};
|
|
1032
|
+
}
|
|
1033
|
+
else {
|
|
1034
|
+
throw this.createError(PhoneAuthErrorCode.USER_DENIED, result.error || 'Link authentication failed');
|
|
1035
|
+
}
|
|
1036
|
+
});
|
|
921
1037
|
// Wait for credential and return in standard format
|
|
922
1038
|
const credential = yield credentialPromise;
|
|
923
1039
|
const aggregatorId = this.config.aggregatorId || 'default';
|