@glideidentity/web-client-sdk 5.0.1-beta.0 → 5.1.1-beta.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 +8 -108
- package/dist/adapters/angular/index.js +1 -0
- package/dist/adapters/angular/phone-auth.service.d.ts +18 -0
- package/dist/adapters/angular/phone-auth.service.js +26 -0
- package/dist/adapters/react/index.js +3 -0
- package/dist/adapters/react/useClient.js +1 -0
- package/dist/adapters/react/usePhoneAuth.js +16 -1
- package/dist/adapters/vanilla/client.js +1 -0
- package/dist/adapters/vanilla/index.js +1 -0
- package/dist/adapters/vanilla/phone-auth.js +31 -0
- package/dist/adapters/vue/index.js +4 -0
- package/dist/adapters/vue/useClient.js +5 -0
- package/dist/adapters/vue/usePhoneAuth.js +20 -1
- package/dist/browser/web-client-sdk.min.js +1 -2
- package/dist/browser.js +6 -0
- package/dist/core/client.js +12 -0
- package/dist/core/logger.js +81 -1
- package/dist/core/phone-auth/api-types.d.ts +1 -4
- package/dist/core/phone-auth/api-types.js +83 -0
- package/dist/core/phone-auth/client.js +374 -38
- package/dist/core/phone-auth/error-utils.js +83 -1
- package/dist/core/phone-auth/index.d.ts +1 -1
- package/dist/core/phone-auth/index.js +2 -2
- package/dist/core/phone-auth/status-types.d.ts +78 -0
- package/dist/core/phone-auth/status-types.js +17 -0
- package/dist/core/phone-auth/strategies/desktop.d.ts +2 -0
- package/dist/core/phone-auth/strategies/desktop.js +136 -13
- package/dist/core/phone-auth/strategies/index.d.ts +4 -0
- package/dist/core/phone-auth/strategies/index.js +4 -0
- package/dist/core/phone-auth/strategies/link.d.ts +2 -0
- package/dist/core/phone-auth/strategies/link.js +97 -13
- package/dist/core/phone-auth/strategies/ts43.d.ts +19 -0
- package/dist/core/phone-auth/strategies/ts43.js +33 -2
- package/dist/core/phone-auth/strategies/types.js +4 -0
- package/dist/core/phone-auth/type-guards.js +131 -0
- package/dist/core/phone-auth/types.d.ts +5 -0
- package/dist/core/phone-auth/types.js +32 -0
- package/dist/core/phone-auth/ui/mobile-debug-console.js +28 -2
- package/dist/core/phone-auth/ui/modal.d.ts +55 -33
- package/dist/core/phone-auth/ui/modal.js +422 -889
- package/dist/core/phone-auth/validation-utils.d.ts +0 -9
- package/dist/core/phone-auth/validation-utils.js +34 -25
- package/dist/core/version.js +2 -1
- package/dist/esm/adapters/angular/index.js +1 -0
- package/dist/esm/adapters/angular/phone-auth.service.d.ts +18 -0
- package/dist/esm/adapters/angular/phone-auth.service.js +26 -0
- package/dist/esm/adapters/react/index.js +3 -0
- package/dist/esm/adapters/react/useClient.js +1 -0
- package/dist/esm/adapters/react/usePhoneAuth.js +16 -1
- package/dist/esm/adapters/vanilla/client.js +1 -0
- package/dist/esm/adapters/vanilla/index.js +1 -0
- package/dist/esm/adapters/vanilla/phone-auth.d.ts +24 -0
- package/dist/esm/adapters/vanilla/phone-auth.js +31 -0
- package/dist/esm/adapters/vue/index.js +4 -0
- package/dist/esm/adapters/vue/useClient.js +5 -0
- package/dist/esm/adapters/vue/usePhoneAuth.js +20 -1
- package/dist/esm/browser.js +6 -0
- package/dist/esm/core/client.d.ts +10 -0
- package/dist/esm/core/client.js +12 -0
- package/dist/esm/core/logger.d.ts +53 -0
- package/dist/esm/core/logger.js +81 -1
- package/dist/esm/core/phone-auth/api-types.d.ts +313 -1
- package/dist/esm/core/phone-auth/api-types.js +83 -0
- package/dist/esm/core/phone-auth/client.d.ts +144 -0
- package/dist/esm/core/phone-auth/client.js +375 -39
- package/dist/esm/core/phone-auth/error-utils.d.ts +29 -0
- package/dist/esm/core/phone-auth/error-utils.js +83 -1
- package/dist/esm/core/phone-auth/index.d.ts +1 -1
- package/dist/esm/core/phone-auth/index.js +4 -2
- package/dist/esm/core/phone-auth/status-types.d.ts +78 -0
- package/dist/esm/core/phone-auth/status-types.js +17 -0
- package/dist/esm/core/phone-auth/strategies/desktop.d.ts +65 -0
- package/dist/esm/core/phone-auth/strategies/desktop.js +136 -13
- package/dist/esm/core/phone-auth/strategies/index.d.ts +4 -0
- package/dist/esm/core/phone-auth/strategies/index.js +4 -0
- package/dist/esm/core/phone-auth/strategies/link.d.ts +50 -0
- package/dist/esm/core/phone-auth/strategies/link.js +97 -13
- package/dist/esm/core/phone-auth/strategies/ts43.d.ts +19 -0
- package/dist/esm/core/phone-auth/strategies/ts43.js +33 -2
- package/dist/esm/core/phone-auth/strategies/types.d.ts +13 -0
- package/dist/esm/core/phone-auth/strategies/types.js +4 -0
- package/dist/esm/core/phone-auth/type-guards.d.ts +128 -0
- package/dist/esm/core/phone-auth/type-guards.js +131 -0
- package/dist/esm/core/phone-auth/types.d.ts +113 -0
- package/dist/esm/core/phone-auth/types.js +32 -0
- package/dist/esm/core/phone-auth/ui/mobile-debug-console.d.ts +4 -0
- package/dist/esm/core/phone-auth/ui/mobile-debug-console.js +28 -2
- package/dist/esm/core/phone-auth/ui/modal.d.ts +68 -27
- package/dist/esm/core/phone-auth/ui/modal.js +422 -889
- package/dist/esm/core/phone-auth/validation-utils.d.ts +26 -4
- package/dist/esm/core/phone-auth/validation-utils.js +34 -24
- package/dist/esm/core/types.d.ts +35 -0
- package/dist/esm/core/version.js +2 -1
- package/dist/esm/index.js +9 -1
- package/dist/index.js +7 -0
- package/package.json +1 -1
- package/dist/browser/web-client-sdk.min.js.LICENSE.txt +0 -1
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Desktop Strategy Handler
|
|
4
|
+
* Handles QR code-based authentication for desktop browsers
|
|
5
|
+
* Manages QR code display and polling for authentication status
|
|
6
|
+
*/
|
|
2
7
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
8
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
9
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -18,33 +23,49 @@ class DesktopHandler {
|
|
|
18
23
|
this.isCancelled = false;
|
|
19
24
|
this.isPollingInProgress = false;
|
|
20
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* Maps backend HTTP status codes to client status
|
|
28
|
+
* @param httpStatus HTTP status code from backend
|
|
29
|
+
* @param bodyStatus Optional status from response body (for 200 OK responses)
|
|
30
|
+
* @returns Mapped status string for client use
|
|
31
|
+
*/
|
|
21
32
|
mapBackendStatus(httpStatus, bodyStatus) {
|
|
22
33
|
switch (httpStatus) {
|
|
23
34
|
case 200:
|
|
35
|
+
// For 200 OK, check the body status
|
|
24
36
|
return bodyStatus === 'completed' ? 'authenticated' : 'pending';
|
|
25
37
|
case 410:
|
|
26
38
|
return 'expired';
|
|
27
39
|
case 422:
|
|
28
|
-
return 'error';
|
|
40
|
+
return 'error'; // Failed or cancelled
|
|
29
41
|
case 404:
|
|
30
|
-
return 'error';
|
|
42
|
+
return 'error'; // Session not found
|
|
31
43
|
case 400:
|
|
32
|
-
return 'error';
|
|
44
|
+
return 'error'; // Invalid session key
|
|
33
45
|
default:
|
|
34
46
|
return 'error';
|
|
35
47
|
}
|
|
36
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Invoke desktop authentication with QR code
|
|
51
|
+
* Returns QR code data for display and starts polling if endpoint is provided
|
|
52
|
+
*/
|
|
37
53
|
invoke(data, options) {
|
|
38
54
|
return __awaiter(this, void 0, void 0, function* () {
|
|
39
55
|
const desktopData = data.data;
|
|
56
|
+
// Extract QR code from nested or flat structure, checking multiple field names
|
|
40
57
|
let qrCode;
|
|
41
58
|
let sessionId;
|
|
42
59
|
let pollingEndpoint;
|
|
43
60
|
let pollingInterval;
|
|
44
61
|
let expiresIn;
|
|
62
|
+
// Check nested structure first (data.data.qr_code_image or data.data.qr_code)
|
|
45
63
|
if (desktopData && desktopData.data && typeof desktopData.data === 'object') {
|
|
46
64
|
const innerData = desktopData.data;
|
|
65
|
+
// Try to extract from inner data
|
|
66
|
+
// Support both single QR (qr_code_image) and dual-platform QR (ios/android)
|
|
47
67
|
if (innerData.ios_qr_image && innerData.android_qr_image) {
|
|
68
|
+
// Dual-platform QR format - both must be present
|
|
48
69
|
qrCode = {
|
|
49
70
|
iosQRCode: innerData.ios_qr_image,
|
|
50
71
|
androidQRCode: innerData.android_qr_image,
|
|
@@ -53,9 +74,11 @@ class DesktopHandler {
|
|
|
53
74
|
};
|
|
54
75
|
}
|
|
55
76
|
else if (innerData.ios_qr_image || innerData.android_qr_image) {
|
|
77
|
+
// Only one platform QR - use as single QR format
|
|
56
78
|
qrCode = innerData.ios_qr_image || innerData.android_qr_image;
|
|
57
79
|
}
|
|
58
80
|
else {
|
|
81
|
+
// Single QR format (legacy)
|
|
59
82
|
qrCode = innerData.qr_code_image || innerData.qr_code;
|
|
60
83
|
}
|
|
61
84
|
sessionId = innerData.session_id;
|
|
@@ -63,6 +86,7 @@ class DesktopHandler {
|
|
|
63
86
|
pollingInterval = innerData.polling_interval;
|
|
64
87
|
expiresIn = innerData.expires_in;
|
|
65
88
|
}
|
|
89
|
+
// Fall back to flat structure if no QR code found
|
|
66
90
|
if (!qrCode && desktopData) {
|
|
67
91
|
qrCode = desktopData.qr_code_image || desktopData.qr_code;
|
|
68
92
|
sessionId = sessionId || desktopData.session_id;
|
|
@@ -70,47 +94,65 @@ class DesktopHandler {
|
|
|
70
94
|
pollingInterval = pollingInterval || desktopData.polling_interval;
|
|
71
95
|
expiresIn = expiresIn || desktopData.expires_in;
|
|
72
96
|
}
|
|
97
|
+
// Validate QR code exists
|
|
73
98
|
if (!qrCode) {
|
|
74
99
|
throw new Error('Invalid desktop authentication data: missing QR code');
|
|
75
100
|
}
|
|
101
|
+
// Validate session ID exists
|
|
76
102
|
if (!sessionId) {
|
|
77
103
|
throw new Error('Invalid desktop authentication data: missing session ID');
|
|
78
104
|
}
|
|
105
|
+
// Notify that QR code is ready
|
|
79
106
|
if (options === null || options === void 0 ? void 0 : options.onQRCodeReady) {
|
|
80
107
|
options.onQRCodeReady(qrCode);
|
|
81
108
|
}
|
|
109
|
+
// Use polling endpoint with this priority:
|
|
110
|
+
// 1. Developer-provided endpoint from options (highest priority)
|
|
111
|
+
// 2. Backend-provided endpoint
|
|
112
|
+
// 3. Hardcoded fallback
|
|
82
113
|
console.log('[Desktop QR] Polling endpoint selection:');
|
|
83
114
|
console.log(' - options?.pollingEndpoint:', options === null || options === void 0 ? void 0 : options.pollingEndpoint);
|
|
84
115
|
console.log(' - backend pollingEndpoint:', pollingEndpoint);
|
|
85
116
|
let finalPollingEndpoint = options === null || options === void 0 ? void 0 : options.pollingEndpoint;
|
|
86
117
|
let endpointSource = 'options';
|
|
87
118
|
if (!finalPollingEndpoint) {
|
|
88
|
-
finalPollingEndpoint = pollingEndpoint;
|
|
119
|
+
finalPollingEndpoint = pollingEndpoint; // Backend-provided status_url
|
|
89
120
|
endpointSource = 'backend';
|
|
90
121
|
}
|
|
91
122
|
if (!finalPollingEndpoint) {
|
|
123
|
+
// Use hardcoded fallback - this will be constructed in the polling function
|
|
92
124
|
console.log('[Desktop QR] No polling endpoint provided, will use hardcoded fallback');
|
|
93
125
|
endpointSource = 'fallback';
|
|
94
126
|
}
|
|
95
127
|
console.log('[Desktop QR] Selected endpoint:', finalPollingEndpoint, 'from source:', endpointSource);
|
|
128
|
+
// Start polling for authentication status
|
|
96
129
|
const finalPollingInterval = (options === null || options === void 0 ? void 0 : options.pollingInterval) || pollingInterval || 2000;
|
|
97
|
-
const maxAttempts = (options === null || options === void 0 ? void 0 : options.maxPollingAttempts) || 30;
|
|
130
|
+
const maxAttempts = (options === null || options === void 0 ? void 0 : options.maxPollingAttempts) || 30; // Default to 1 minute
|
|
98
131
|
console.log(`[Desktop QR] Starting polling - endpoint source: ${endpointSource}, interval: ${finalPollingInterval}ms, max attempts: ${maxAttempts}`);
|
|
99
|
-
return this.startPolling(finalPollingEndpoint || '',
|
|
132
|
+
return this.startPolling(finalPollingEndpoint || '', // Pass empty string if undefined, will use fallback
|
|
133
|
+
sessionId, finalPollingInterval, maxAttempts, expiresIn || 300, // Default 5 minutes expiry
|
|
134
|
+
options, endpointSource // Pass the endpoint source for logging
|
|
135
|
+
);
|
|
100
136
|
});
|
|
101
137
|
}
|
|
138
|
+
/**
|
|
139
|
+
* Start polling for authentication status
|
|
140
|
+
*/
|
|
102
141
|
startPolling(endpoint_1, sessionId_1, interval_1, maxAttempts_1, expiresIn_1, options_1) {
|
|
103
142
|
return __awaiter(this, arguments, void 0, function* (endpoint, sessionId, interval, maxAttempts, expiresIn, options, endpointSource = 'unknown') {
|
|
104
143
|
const startTime = Date.now();
|
|
105
144
|
const expiryTime = startTime + (expiresIn * 1000);
|
|
106
145
|
return new Promise((resolve, reject) => {
|
|
146
|
+
// Store the reject function so we can call it from cancel()
|
|
107
147
|
this.pollingReject = reject;
|
|
108
148
|
const poll = () => __awaiter(this, void 0, void 0, function* () {
|
|
149
|
+
// Skip if another poll is already in progress
|
|
109
150
|
if (this.isPollingInProgress) {
|
|
110
151
|
return;
|
|
111
152
|
}
|
|
112
153
|
try {
|
|
113
154
|
this.isPollingInProgress = true;
|
|
155
|
+
// Check if cancelled
|
|
114
156
|
if (this.isCancelled) {
|
|
115
157
|
this.stopPolling();
|
|
116
158
|
if (options === null || options === void 0 ? void 0 : options.onCancel) {
|
|
@@ -122,12 +164,14 @@ class DesktopHandler {
|
|
|
122
164
|
message: 'Authentication cancelled'
|
|
123
165
|
});
|
|
124
166
|
}
|
|
167
|
+
// Reject the promise with a cancellation error
|
|
125
168
|
reject({
|
|
126
169
|
code: 'USER_DENIED',
|
|
127
170
|
message: 'Authentication cancelled by user'
|
|
128
171
|
});
|
|
129
172
|
return;
|
|
130
173
|
}
|
|
174
|
+
// Check if expired
|
|
131
175
|
if (Date.now() > expiryTime) {
|
|
132
176
|
this.stopPolling();
|
|
133
177
|
if (options === null || options === void 0 ? void 0 : options.onExpired) {
|
|
@@ -145,6 +189,7 @@ class DesktopHandler {
|
|
|
145
189
|
});
|
|
146
190
|
return;
|
|
147
191
|
}
|
|
192
|
+
// Check max attempts
|
|
148
193
|
if (this.pollingAttempts >= maxAttempts) {
|
|
149
194
|
this.stopPolling();
|
|
150
195
|
if (options === null || options === void 0 ? void 0 : options.onTimeout) {
|
|
@@ -162,35 +207,58 @@ class DesktopHandler {
|
|
|
162
207
|
});
|
|
163
208
|
return;
|
|
164
209
|
}
|
|
210
|
+
// Build public status endpoint URL - use relative path for proxied requests
|
|
165
211
|
let statusUrl;
|
|
212
|
+
// Extract base URL and construct public endpoint
|
|
166
213
|
if (endpoint && (endpoint.startsWith('http://') || endpoint.startsWith('https://'))) {
|
|
167
|
-
|
|
168
|
-
statusUrl =
|
|
214
|
+
// Full URL provided - use as is
|
|
215
|
+
statusUrl = endpoint;
|
|
169
216
|
}
|
|
170
217
|
else if (endpoint && endpoint !== '') {
|
|
218
|
+
// Relative path provided (e.g. '/api/phone-auth/status')
|
|
219
|
+
// Append session ID to the provided endpoint
|
|
171
220
|
statusUrl = `${endpoint}/${sessionId}`;
|
|
172
221
|
}
|
|
173
222
|
else {
|
|
223
|
+
// No endpoint provided - use hardcoded fallback
|
|
224
|
+
// This ensures it goes through the same domain and uses the app's API configuration
|
|
174
225
|
statusUrl = `/api/phone-auth/status/${sessionId}`;
|
|
175
226
|
}
|
|
227
|
+
// Poll the public endpoint - no authentication required
|
|
176
228
|
console.log(`[Desktop QR] Polling status (attempt ${this.pollingAttempts}/${maxAttempts})`);
|
|
177
229
|
console.log(`[Desktop QR] Using ${endpointSource} endpoint: ${statusUrl}`);
|
|
230
|
+
// Build headers
|
|
231
|
+
const headers = {
|
|
232
|
+
'Accept': 'application/json'
|
|
233
|
+
};
|
|
234
|
+
// Add developer header if devEnv is set
|
|
235
|
+
if (options === null || options === void 0 ? void 0 : options.devEnv) {
|
|
236
|
+
headers['developer'] = options.devEnv;
|
|
237
|
+
console.log(`[Desktop QR] Adding developer header: ${options.devEnv}`);
|
|
238
|
+
}
|
|
178
239
|
const response = yield fetch(statusUrl, {
|
|
179
240
|
method: 'GET',
|
|
180
|
-
headers
|
|
181
|
-
'Accept': 'application/json'
|
|
182
|
-
}
|
|
241
|
+
headers
|
|
183
242
|
});
|
|
184
243
|
console.log(`[Desktop QR] Status response: ${response.status} ${response.statusText}`);
|
|
244
|
+
// Backend Status Code Mapping:
|
|
245
|
+
// - 200 OK: Session exists (body has 'pending' or 'completed' status)
|
|
246
|
+
// - 410 Gone: Session expired after timeout
|
|
247
|
+
// - 422 Unprocessable Entity: Authentication failed or cancelled
|
|
248
|
+
// - 404 Not Found: Session doesn't exist
|
|
249
|
+
// - 400 Bad Request: Invalid session key format
|
|
250
|
+
// Handle response based on HTTP status code
|
|
185
251
|
if (response.status === 200) {
|
|
186
252
|
const result = yield response.json();
|
|
187
253
|
if (result.status === 'completed') {
|
|
254
|
+
// Authentication completed successfully
|
|
188
255
|
this.stopPolling();
|
|
256
|
+
// Close the modal if it exists
|
|
189
257
|
const modal = document.querySelector('[style*="position: fixed"]');
|
|
190
258
|
if (modal && modal.querySelector('#desktop-auth-status')) {
|
|
191
259
|
setTimeout(() => {
|
|
192
260
|
modal.remove();
|
|
193
|
-
}, 500);
|
|
261
|
+
}, 500); // Brief delay to show success message
|
|
194
262
|
}
|
|
195
263
|
if (options === null || options === void 0 ? void 0 : options.onStatusUpdate) {
|
|
196
264
|
options.onStatusUpdate({
|
|
@@ -199,7 +267,8 @@ class DesktopHandler {
|
|
|
199
267
|
data: result
|
|
200
268
|
});
|
|
201
269
|
}
|
|
202
|
-
|
|
270
|
+
// Return the session data for next steps
|
|
271
|
+
this.pollingReject = undefined; // Clear the reject function on success
|
|
203
272
|
resolve({
|
|
204
273
|
authenticated: true,
|
|
205
274
|
credential: result.credential || result.session_key || sessionId,
|
|
@@ -213,6 +282,7 @@ class DesktopHandler {
|
|
|
213
282
|
});
|
|
214
283
|
}
|
|
215
284
|
else if (result.status === 'pending') {
|
|
285
|
+
// Continue polling
|
|
216
286
|
this.pollingAttempts++;
|
|
217
287
|
if (options === null || options === void 0 ? void 0 : options.onStatusUpdate) {
|
|
218
288
|
options.onStatusUpdate({
|
|
@@ -223,8 +293,10 @@ class DesktopHandler {
|
|
|
223
293
|
}
|
|
224
294
|
}
|
|
225
295
|
else if (response.status === 410) {
|
|
296
|
+
// Session expired
|
|
226
297
|
this.stopPolling();
|
|
227
298
|
const errorData = yield response.json().catch(() => ({ message: 'Session expired' }));
|
|
299
|
+
// Close the modal on error
|
|
228
300
|
const modal = document.querySelector('[style*="position: fixed"]');
|
|
229
301
|
if (modal && modal.querySelector('#desktop-auth-status')) {
|
|
230
302
|
setTimeout(() => {
|
|
@@ -246,8 +318,10 @@ class DesktopHandler {
|
|
|
246
318
|
});
|
|
247
319
|
}
|
|
248
320
|
else if (response.status === 422) {
|
|
321
|
+
// Authentication failed
|
|
249
322
|
this.stopPolling();
|
|
250
323
|
const errorData = yield response.json().catch(() => ({ message: 'Authentication failed' }));
|
|
324
|
+
// Close the modal on error
|
|
251
325
|
const modal = document.querySelector('[style*="position: fixed"]');
|
|
252
326
|
if (modal && modal.querySelector('#desktop-auth-status')) {
|
|
253
327
|
setTimeout(() => {
|
|
@@ -269,6 +343,7 @@ class DesktopHandler {
|
|
|
269
343
|
});
|
|
270
344
|
}
|
|
271
345
|
else if (response.status === 404) {
|
|
346
|
+
// Session not found
|
|
272
347
|
this.stopPolling();
|
|
273
348
|
if (options === null || options === void 0 ? void 0 : options.onStatusUpdate) {
|
|
274
349
|
options.onStatusUpdate({
|
|
@@ -282,6 +357,7 @@ class DesktopHandler {
|
|
|
282
357
|
});
|
|
283
358
|
}
|
|
284
359
|
else if (response.status === 400) {
|
|
360
|
+
// Invalid session key
|
|
285
361
|
this.stopPolling();
|
|
286
362
|
const errorData = yield response.json().catch(() => ({ message: 'Invalid session key' }));
|
|
287
363
|
resolve({
|
|
@@ -290,10 +366,12 @@ class DesktopHandler {
|
|
|
290
366
|
});
|
|
291
367
|
}
|
|
292
368
|
else {
|
|
369
|
+
// Unexpected status - continue polling
|
|
293
370
|
this.pollingAttempts++;
|
|
294
371
|
}
|
|
295
372
|
}
|
|
296
373
|
catch (error) {
|
|
374
|
+
// Network or other error - continue polling
|
|
297
375
|
this.pollingAttempts++;
|
|
298
376
|
if (options === null || options === void 0 ? void 0 : options.onStatusUpdate) {
|
|
299
377
|
options.onStatusUpdate({
|
|
@@ -303,14 +381,20 @@ class DesktopHandler {
|
|
|
303
381
|
}
|
|
304
382
|
}
|
|
305
383
|
finally {
|
|
384
|
+
// Always clear the polling flag when done
|
|
306
385
|
this.isPollingInProgress = false;
|
|
307
386
|
}
|
|
308
387
|
});
|
|
388
|
+
// Start initial poll
|
|
309
389
|
poll();
|
|
390
|
+
// Set up interval for subsequent polls
|
|
310
391
|
this.pollingIntervalId = setInterval(poll, interval);
|
|
311
392
|
});
|
|
312
393
|
});
|
|
313
394
|
}
|
|
395
|
+
/**
|
|
396
|
+
* Stop polling
|
|
397
|
+
*/
|
|
314
398
|
stopPolling() {
|
|
315
399
|
if (this.pollingIntervalId) {
|
|
316
400
|
console.log('[Desktop QR] Stopping polling');
|
|
@@ -319,10 +403,18 @@ class DesktopHandler {
|
|
|
319
403
|
}
|
|
320
404
|
this.pollingAttempts = 0;
|
|
321
405
|
this.isPollingInProgress = false;
|
|
406
|
+
// Don't clear pollingReject here - it's needed by cancel()
|
|
322
407
|
}
|
|
408
|
+
/**
|
|
409
|
+
* Check if polling is currently active
|
|
410
|
+
*/
|
|
323
411
|
isPolling() {
|
|
324
412
|
return this.pollingIntervalId !== undefined;
|
|
325
413
|
}
|
|
414
|
+
/**
|
|
415
|
+
* Format response for backend processing
|
|
416
|
+
* Desktop strategy typically returns the credential from mobile authentication
|
|
417
|
+
*/
|
|
326
418
|
formatResponse(response) {
|
|
327
419
|
if (!response.authenticated || !response.credential) {
|
|
328
420
|
throw new Error('Authentication not completed');
|
|
@@ -332,23 +424,35 @@ class DesktopHandler {
|
|
|
332
424
|
session: response.session
|
|
333
425
|
};
|
|
334
426
|
}
|
|
427
|
+
/**
|
|
428
|
+
* Check if desktop authentication is supported
|
|
429
|
+
* Desktop auth with QR codes works in any modern browser
|
|
430
|
+
*/
|
|
335
431
|
isSupported() {
|
|
432
|
+
// Check for required browser APIs
|
|
336
433
|
return typeof window !== 'undefined' &&
|
|
337
434
|
typeof fetch !== 'undefined' &&
|
|
338
435
|
typeof Promise !== 'undefined';
|
|
339
436
|
}
|
|
437
|
+
/**
|
|
438
|
+
* Clean up resources (stop polling if active)
|
|
439
|
+
*/
|
|
340
440
|
cleanup() {
|
|
341
441
|
this.stopPolling();
|
|
342
442
|
this.isCancelled = false;
|
|
343
443
|
this.onCancel = undefined;
|
|
344
444
|
this.pollingReject = undefined;
|
|
345
445
|
}
|
|
446
|
+
/**
|
|
447
|
+
* Cancel the ongoing authentication
|
|
448
|
+
*/
|
|
346
449
|
cancel() {
|
|
347
450
|
var _a;
|
|
348
451
|
console.log('[Desktop QR] Cancelling authentication');
|
|
349
452
|
this.isCancelled = true;
|
|
350
453
|
this.stopPolling();
|
|
351
454
|
(_a = this.onCancel) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
455
|
+
// Immediately reject the polling promise
|
|
352
456
|
if (this.pollingReject) {
|
|
353
457
|
this.pollingReject({
|
|
354
458
|
code: 'USER_DENIED',
|
|
@@ -359,14 +463,20 @@ class DesktopHandler {
|
|
|
359
463
|
}
|
|
360
464
|
}
|
|
361
465
|
exports.DesktopHandler = DesktopHandler;
|
|
466
|
+
/**
|
|
467
|
+
* Helper function to display QR code in a modal or inline
|
|
468
|
+
*/
|
|
362
469
|
function createQRCodeDisplay(qrCodeData, options) {
|
|
363
470
|
const container = (options === null || options === void 0 ? void 0 : options.container) || document.createElement('div');
|
|
364
471
|
container.className = 'phone-auth-qr-container';
|
|
472
|
+
// Determine QR code to display
|
|
365
473
|
let qrCodeSrc;
|
|
366
474
|
if (typeof qrCodeData === 'string') {
|
|
475
|
+
// Simple string QR code
|
|
367
476
|
qrCodeSrc = qrCodeData;
|
|
368
477
|
}
|
|
369
478
|
else if (qrCodeData && typeof qrCodeData === 'object') {
|
|
479
|
+
// QRCodeData object - prefer iOS QR if available
|
|
370
480
|
if (qrCodeData.iosQRCode) {
|
|
371
481
|
qrCodeSrc = qrCodeData.iosQRCode;
|
|
372
482
|
}
|
|
@@ -380,6 +490,7 @@ function createQRCodeDisplay(qrCodeData, options) {
|
|
|
380
490
|
else {
|
|
381
491
|
throw new Error('Invalid qrCodeData: must be string or QRCodeData object');
|
|
382
492
|
}
|
|
493
|
+
// Create QR code image
|
|
383
494
|
const img = document.createElement('img');
|
|
384
495
|
img.src = qrCodeSrc;
|
|
385
496
|
img.alt = 'QR Code for authentication';
|
|
@@ -387,6 +498,7 @@ function createQRCodeDisplay(qrCodeData, options) {
|
|
|
387
498
|
img.style.height = `${(options === null || options === void 0 ? void 0 : options.size) || 256}px`;
|
|
388
499
|
img.style.display = 'block';
|
|
389
500
|
img.style.margin = '0 auto';
|
|
501
|
+
// Add title if provided
|
|
390
502
|
if (options === null || options === void 0 ? void 0 : options.title) {
|
|
391
503
|
const title = document.createElement('h3');
|
|
392
504
|
title.textContent = options.title;
|
|
@@ -395,6 +507,7 @@ function createQRCodeDisplay(qrCodeData, options) {
|
|
|
395
507
|
container.appendChild(title);
|
|
396
508
|
}
|
|
397
509
|
container.appendChild(img);
|
|
510
|
+
// Add description if provided
|
|
398
511
|
if (options === null || options === void 0 ? void 0 : options.description) {
|
|
399
512
|
const desc = document.createElement('p');
|
|
400
513
|
desc.textContent = options.description;
|
|
@@ -405,7 +518,12 @@ function createQRCodeDisplay(qrCodeData, options) {
|
|
|
405
518
|
}
|
|
406
519
|
return container;
|
|
407
520
|
}
|
|
521
|
+
/**
|
|
522
|
+
* Helper function to create a modal for QR code display
|
|
523
|
+
* Supports both string QR codes and QRCodeData objects for dual-platform support
|
|
524
|
+
*/
|
|
408
525
|
function showQRCodeModal(qrCodeData, options) {
|
|
526
|
+
// Create modal overlay
|
|
409
527
|
const overlay = document.createElement('div');
|
|
410
528
|
overlay.style.cssText = `
|
|
411
529
|
position: fixed;
|
|
@@ -419,6 +537,7 @@ function showQRCodeModal(qrCodeData, options) {
|
|
|
419
537
|
justify-content: center;
|
|
420
538
|
z-index: 9999;
|
|
421
539
|
`;
|
|
540
|
+
// Create modal content
|
|
422
541
|
const modal = document.createElement('div');
|
|
423
542
|
modal.style.cssText = `
|
|
424
543
|
background: white;
|
|
@@ -427,6 +546,7 @@ function showQRCodeModal(qrCodeData, options) {
|
|
|
427
546
|
max-width: 400px;
|
|
428
547
|
position: relative;
|
|
429
548
|
`;
|
|
549
|
+
// Add close button
|
|
430
550
|
const closeBtn = document.createElement('button');
|
|
431
551
|
closeBtn.textContent = '×';
|
|
432
552
|
closeBtn.style.cssText = `
|
|
@@ -446,11 +566,13 @@ function showQRCodeModal(qrCodeData, options) {
|
|
|
446
566
|
}
|
|
447
567
|
};
|
|
448
568
|
modal.appendChild(closeBtn);
|
|
569
|
+
// Add QR code display
|
|
449
570
|
const qrDisplay = createQRCodeDisplay(qrCodeData, {
|
|
450
571
|
title: (options === null || options === void 0 ? void 0 : options.title) || 'Scan QR Code to Authenticate',
|
|
451
572
|
description: (options === null || options === void 0 ? void 0 : options.description) || 'Use your mobile device to scan this QR code'
|
|
452
573
|
});
|
|
453
574
|
modal.appendChild(qrDisplay);
|
|
575
|
+
// Add loading spinner placeholder
|
|
454
576
|
const statusDiv = document.createElement('div');
|
|
455
577
|
statusDiv.id = 'desktop-auth-status';
|
|
456
578
|
statusDiv.style.cssText = `
|
|
@@ -462,6 +584,7 @@ function showQRCodeModal(qrCodeData, options) {
|
|
|
462
584
|
modal.appendChild(statusDiv);
|
|
463
585
|
overlay.appendChild(modal);
|
|
464
586
|
document.body.appendChild(overlay);
|
|
587
|
+
// Close modal on overlay click
|
|
465
588
|
overlay.onclick = (e) => {
|
|
466
589
|
if (e.target === overlay) {
|
|
467
590
|
document.body.removeChild(overlay);
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Strategy Handlers Export
|
|
4
|
+
* Provides implementations for TS43, Link, and Desktop authentication strategies
|
|
5
|
+
*/
|
|
2
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
7
|
exports.showQRCodeModal = exports.createQRCodeDisplay = exports.DesktopHandler = exports.LinkHandler = exports.TS43Handler = void 0;
|
|
4
8
|
var ts43_1 = require("./ts43");
|
|
@@ -12,6 +12,8 @@ export interface LinkAuthOptions {
|
|
|
12
12
|
maxPollingAttempts?: number;
|
|
13
13
|
/** Custom polling endpoint (overrides backend-provided or configured endpoint) */
|
|
14
14
|
pollingEndpoint?: string;
|
|
15
|
+
/** Developer environment (adds 'developer' header to requests) */
|
|
16
|
+
devEnv?: string;
|
|
15
17
|
/** Callback when link is opened */
|
|
16
18
|
onLinkOpened?: () => void;
|
|
17
19
|
/** Callback for polling status updates */
|