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