@glideidentity/web-client-sdk 4.4.8-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 +938 -0
- package/dist/adapters/angular/client.service.d.ts +7 -0
- package/dist/adapters/angular/client.service.js +30 -0
- package/dist/adapters/angular/index.d.ts +3 -0
- package/dist/adapters/angular/index.js +18 -0
- package/dist/adapters/angular/phone-auth.service.d.ts +38 -0
- package/dist/adapters/angular/phone-auth.service.js +130 -0
- package/dist/adapters/react/index.d.ts +9 -0
- package/dist/adapters/react/index.js +28 -0
- package/dist/adapters/react/useClient.d.ts +26 -0
- package/dist/adapters/react/useClient.js +121 -0
- package/dist/adapters/react/usePhoneAuth.d.ts +23 -0
- package/dist/adapters/react/usePhoneAuth.js +95 -0
- package/dist/adapters/vanilla/client.d.ts +8 -0
- package/dist/adapters/vanilla/client.js +33 -0
- package/dist/adapters/vanilla/index.d.ts +3 -0
- package/dist/adapters/vanilla/index.js +18 -0
- package/dist/adapters/vanilla/phone-auth.d.ts +46 -0
- package/dist/adapters/vanilla/phone-auth.js +138 -0
- package/dist/adapters/vue/index.d.ts +10 -0
- package/dist/adapters/vue/index.js +36 -0
- package/dist/adapters/vue/useClient.d.ts +115 -0
- package/dist/adapters/vue/useClient.js +131 -0
- package/dist/adapters/vue/usePhoneAuth.d.ts +94 -0
- package/dist/adapters/vue/usePhoneAuth.js +103 -0
- package/dist/browser/web-client-sdk.min.js +2 -0
- package/dist/browser/web-client-sdk.min.js.LICENSE.txt +1 -0
- package/dist/browser.d.ts +7 -0
- package/dist/browser.js +31 -0
- package/dist/core/client.d.ts +22 -0
- package/dist/core/client.js +77 -0
- package/dist/core/logger.d.ts +130 -0
- package/dist/core/logger.js +370 -0
- package/dist/core/phone-auth/api-types.d.ts +525 -0
- package/dist/core/phone-auth/api-types.js +215 -0
- package/dist/core/phone-auth/client.d.ts +187 -0
- package/dist/core/phone-auth/client.js +1353 -0
- package/dist/core/phone-auth/error-utils.d.ts +110 -0
- package/dist/core/phone-auth/error-utils.js +350 -0
- package/dist/core/phone-auth/index.d.ts +7 -0
- package/dist/core/phone-auth/index.js +47 -0
- package/dist/core/phone-auth/status-types.d.ts +107 -0
- package/dist/core/phone-auth/status-types.js +31 -0
- package/dist/core/phone-auth/strategies/desktop.d.ts +113 -0
- package/dist/core/phone-auth/strategies/desktop.js +502 -0
- package/dist/core/phone-auth/strategies/index.d.ts +11 -0
- package/dist/core/phone-auth/strategies/index.js +15 -0
- package/dist/core/phone-auth/strategies/link.d.ts +81 -0
- package/dist/core/phone-auth/strategies/link.js +265 -0
- package/dist/core/phone-auth/strategies/ts43.d.ts +32 -0
- package/dist/core/phone-auth/strategies/ts43.js +146 -0
- package/dist/core/phone-auth/strategies/types.d.ts +18 -0
- package/dist/core/phone-auth/strategies/types.js +6 -0
- package/dist/core/phone-auth/type-guards.d.ts +125 -0
- package/dist/core/phone-auth/type-guards.js +160 -0
- package/dist/core/phone-auth/types.d.ts +232 -0
- package/dist/core/phone-auth/types.js +93 -0
- package/dist/core/phone-auth/ui/mobile-debug-console.d.ts +25 -0
- package/dist/core/phone-auth/ui/mobile-debug-console.js +288 -0
- package/dist/core/phone-auth/ui/modal.d.ts +84 -0
- package/dist/core/phone-auth/ui/modal.js +574 -0
- package/dist/core/phone-auth/validation-utils.d.ts +66 -0
- package/dist/core/phone-auth/validation-utils.js +182 -0
- package/dist/core/types.d.ts +62 -0
- package/dist/core/types.js +2 -0
- package/dist/core/version.d.ts +1 -0
- package/dist/core/version.js +5 -0
- package/dist/esm/adapters/angular/client.service.d.ts +7 -0
- package/dist/esm/adapters/angular/client.service.js +27 -0
- package/dist/esm/adapters/angular/index.d.ts +3 -0
- package/dist/esm/adapters/angular/index.js +4 -0
- package/dist/esm/adapters/angular/phone-auth.service.d.ts +38 -0
- package/dist/esm/adapters/angular/phone-auth.service.js +127 -0
- package/dist/esm/adapters/react/index.d.ts +9 -0
- package/dist/esm/adapters/react/index.js +8 -0
- package/dist/esm/adapters/react/useClient.d.ts +26 -0
- package/dist/esm/adapters/react/useClient.js +116 -0
- package/dist/esm/adapters/react/usePhoneAuth.d.ts +23 -0
- package/dist/esm/adapters/react/usePhoneAuth.js +92 -0
- package/dist/esm/adapters/vanilla/client.d.ts +8 -0
- package/dist/esm/adapters/vanilla/client.js +29 -0
- package/dist/esm/adapters/vanilla/index.d.ts +3 -0
- package/dist/esm/adapters/vanilla/index.js +4 -0
- package/dist/esm/adapters/vanilla/phone-auth.d.ts +46 -0
- package/dist/esm/adapters/vanilla/phone-auth.js +134 -0
- package/dist/esm/adapters/vue/index.d.ts +10 -0
- package/dist/esm/adapters/vue/index.js +11 -0
- package/dist/esm/adapters/vue/useClient.d.ts +115 -0
- package/dist/esm/adapters/vue/useClient.js +127 -0
- package/dist/esm/adapters/vue/usePhoneAuth.d.ts +94 -0
- package/dist/esm/adapters/vue/usePhoneAuth.js +100 -0
- package/dist/esm/browser.d.ts +7 -0
- package/dist/esm/browser.js +11 -0
- package/dist/esm/core/client.d.ts +22 -0
- package/dist/esm/core/client.js +70 -0
- package/dist/esm/core/logger.d.ts +130 -0
- package/dist/esm/core/logger.js +359 -0
- package/dist/esm/core/phone-auth/api-types.d.ts +525 -0
- package/dist/esm/core/phone-auth/api-types.js +203 -0
- package/dist/esm/core/phone-auth/client.d.ts +187 -0
- package/dist/esm/core/phone-auth/client.js +1316 -0
- package/dist/esm/core/phone-auth/error-utils.d.ts +110 -0
- package/dist/esm/core/phone-auth/error-utils.js +338 -0
- package/dist/esm/core/phone-auth/index.d.ts +7 -0
- package/dist/esm/core/phone-auth/index.js +6 -0
- package/dist/esm/core/phone-auth/status-types.d.ts +107 -0
- package/dist/esm/core/phone-auth/status-types.js +26 -0
- package/dist/esm/core/phone-auth/strategies/desktop.d.ts +113 -0
- package/dist/esm/core/phone-auth/strategies/desktop.js +496 -0
- package/dist/esm/core/phone-auth/strategies/index.d.ts +11 -0
- package/dist/esm/core/phone-auth/strategies/index.js +7 -0
- package/dist/esm/core/phone-auth/strategies/link.d.ts +81 -0
- package/dist/esm/core/phone-auth/strategies/link.js +261 -0
- package/dist/esm/core/phone-auth/strategies/ts43.d.ts +32 -0
- package/dist/esm/core/phone-auth/strategies/ts43.js +142 -0
- package/dist/esm/core/phone-auth/strategies/types.d.ts +18 -0
- package/dist/esm/core/phone-auth/strategies/types.js +5 -0
- package/dist/esm/core/phone-auth/type-guards.d.ts +125 -0
- package/dist/esm/core/phone-auth/type-guards.js +150 -0
- package/dist/esm/core/phone-auth/types.d.ts +232 -0
- package/dist/esm/core/phone-auth/types.js +76 -0
- package/dist/esm/core/phone-auth/ui/mobile-debug-console.d.ts +25 -0
- package/dist/esm/core/phone-auth/ui/mobile-debug-console.js +284 -0
- package/dist/esm/core/phone-auth/ui/modal.d.ts +84 -0
- package/dist/esm/core/phone-auth/ui/modal.js +570 -0
- package/dist/esm/core/phone-auth/validation-utils.d.ts +66 -0
- package/dist/esm/core/phone-auth/validation-utils.js +174 -0
- package/dist/esm/core/types.d.ts +62 -0
- package/dist/esm/core/types.js +1 -0
- package/dist/esm/core/version.d.ts +1 -0
- package/dist/esm/core/version.js +2 -0
- package/dist/esm/index.d.ts +12 -0
- package/dist/esm/index.js +15 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +52 -0
- package/package.json +92 -0
|
@@ -0,0 +1,113 @@
|
|
|
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
|
+
import type { StrategyHandler } from './types';
|
|
7
|
+
import type { PrepareResponse } from '../types';
|
|
8
|
+
/**
|
|
9
|
+
* QR code data structure for dual-platform support
|
|
10
|
+
*/
|
|
11
|
+
export interface QRCodeData {
|
|
12
|
+
iosQRCode: string;
|
|
13
|
+
androidQRCode?: string;
|
|
14
|
+
iosUrl?: string;
|
|
15
|
+
androidUrl?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface DesktopAuthOptions {
|
|
18
|
+
/** Custom polling interval in milliseconds (overrides server-provided value) */
|
|
19
|
+
pollingInterval?: number;
|
|
20
|
+
/** Maximum polling attempts before timeout (default: 150 = 5 minutes with 2s interval) */
|
|
21
|
+
maxPollingAttempts?: number;
|
|
22
|
+
/** Custom polling endpoint (overrides backend-provided or uses configured endpoint) */
|
|
23
|
+
pollingEndpoint?: string;
|
|
24
|
+
/** Callback when QR code is ready to display */
|
|
25
|
+
onQRCodeReady?: (qrCodeData: string | QRCodeData) => void;
|
|
26
|
+
/** Callback for polling status updates */
|
|
27
|
+
onStatusUpdate?: (status: PollingStatus) => void;
|
|
28
|
+
/** Callback when authentication expires */
|
|
29
|
+
onExpired?: () => void;
|
|
30
|
+
/** Callback when authentication is cancelled by user */
|
|
31
|
+
onCancel?: () => void;
|
|
32
|
+
/** Callback when polling times out (max attempts reached) */
|
|
33
|
+
onTimeout?: () => void;
|
|
34
|
+
}
|
|
35
|
+
export interface PollingStatus {
|
|
36
|
+
/** Current status of the authentication */
|
|
37
|
+
status: 'pending' | 'authenticated' | 'expired' | 'cancelled' | 'error';
|
|
38
|
+
/** Optional message */
|
|
39
|
+
message?: string;
|
|
40
|
+
/** Authentication result data if status is 'authenticated' */
|
|
41
|
+
data?: any;
|
|
42
|
+
}
|
|
43
|
+
export interface DesktopAuthResult {
|
|
44
|
+
/** Whether authentication was successful */
|
|
45
|
+
authenticated: boolean;
|
|
46
|
+
/** Authentication credential if successful */
|
|
47
|
+
credential?: string;
|
|
48
|
+
/** Session info for subsequent requests */
|
|
49
|
+
session?: any;
|
|
50
|
+
/** Error message if authentication failed */
|
|
51
|
+
error?: string;
|
|
52
|
+
}
|
|
53
|
+
export declare class DesktopHandler implements StrategyHandler {
|
|
54
|
+
private pollingIntervalId?;
|
|
55
|
+
private pollingAttempts;
|
|
56
|
+
private isCancelled;
|
|
57
|
+
private onCancel?;
|
|
58
|
+
/**
|
|
59
|
+
* Maps backend HTTP status codes to client status
|
|
60
|
+
* @param httpStatus HTTP status code from backend
|
|
61
|
+
* @param bodyStatus Optional status from response body (for 200 OK responses)
|
|
62
|
+
* @returns Mapped status string for client use
|
|
63
|
+
*/
|
|
64
|
+
private mapBackendStatus;
|
|
65
|
+
/**
|
|
66
|
+
* Invoke desktop authentication with QR code
|
|
67
|
+
* Returns QR code data for display and starts polling if endpoint is provided
|
|
68
|
+
*/
|
|
69
|
+
invoke(data: PrepareResponse, options?: DesktopAuthOptions): Promise<DesktopAuthResult>;
|
|
70
|
+
/**
|
|
71
|
+
* Start polling for authentication status
|
|
72
|
+
*/
|
|
73
|
+
private startPolling;
|
|
74
|
+
/**
|
|
75
|
+
* Stop polling
|
|
76
|
+
*/
|
|
77
|
+
private stopPolling;
|
|
78
|
+
/**
|
|
79
|
+
* Format response for backend processing
|
|
80
|
+
* Desktop strategy typically returns the credential from mobile authentication
|
|
81
|
+
*/
|
|
82
|
+
formatResponse(response: DesktopAuthResult): any;
|
|
83
|
+
/**
|
|
84
|
+
* Check if desktop authentication is supported
|
|
85
|
+
* Desktop auth with QR codes works in any modern browser
|
|
86
|
+
*/
|
|
87
|
+
isSupported(): boolean;
|
|
88
|
+
/**
|
|
89
|
+
* Clean up resources (stop polling if active)
|
|
90
|
+
*/
|
|
91
|
+
cleanup(): void;
|
|
92
|
+
/**
|
|
93
|
+
* Cancel the ongoing authentication
|
|
94
|
+
*/
|
|
95
|
+
cancel(): void;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Helper function to display QR code in a modal or inline
|
|
99
|
+
*/
|
|
100
|
+
export declare function createQRCodeDisplay(qrCodeData: string, options?: {
|
|
101
|
+
container?: HTMLElement;
|
|
102
|
+
size?: number;
|
|
103
|
+
title?: string;
|
|
104
|
+
description?: string;
|
|
105
|
+
}): HTMLElement;
|
|
106
|
+
/**
|
|
107
|
+
* Helper function to create a modal for QR code display
|
|
108
|
+
*/
|
|
109
|
+
export declare function showQRCodeModal(qrCodeData: string, options?: {
|
|
110
|
+
title?: string;
|
|
111
|
+
description?: string;
|
|
112
|
+
onClose?: () => void;
|
|
113
|
+
}): void;
|
|
@@ -0,0 +1,502 @@
|
|
|
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
|
+
*/
|
|
7
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
8
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
9
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
10
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
11
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
12
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
13
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.DesktopHandler = void 0;
|
|
18
|
+
exports.createQRCodeDisplay = createQRCodeDisplay;
|
|
19
|
+
exports.showQRCodeModal = showQRCodeModal;
|
|
20
|
+
class DesktopHandler {
|
|
21
|
+
constructor() {
|
|
22
|
+
this.pollingAttempts = 0;
|
|
23
|
+
this.isCancelled = false;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Maps backend HTTP status codes to client status
|
|
27
|
+
* @param httpStatus HTTP status code from backend
|
|
28
|
+
* @param bodyStatus Optional status from response body (for 200 OK responses)
|
|
29
|
+
* @returns Mapped status string for client use
|
|
30
|
+
*/
|
|
31
|
+
mapBackendStatus(httpStatus, bodyStatus) {
|
|
32
|
+
switch (httpStatus) {
|
|
33
|
+
case 200:
|
|
34
|
+
// For 200 OK, check the body status
|
|
35
|
+
return bodyStatus === 'completed' ? 'authenticated' : 'pending';
|
|
36
|
+
case 410:
|
|
37
|
+
return 'expired';
|
|
38
|
+
case 422:
|
|
39
|
+
return 'error'; // Failed or cancelled
|
|
40
|
+
case 404:
|
|
41
|
+
return 'error'; // Session not found
|
|
42
|
+
case 400:
|
|
43
|
+
return 'error'; // Invalid session key
|
|
44
|
+
default:
|
|
45
|
+
return 'error';
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Invoke desktop authentication with QR code
|
|
50
|
+
* Returns QR code data for display and starts polling if endpoint is provided
|
|
51
|
+
*/
|
|
52
|
+
invoke(data, options) {
|
|
53
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
54
|
+
const desktopData = data.data;
|
|
55
|
+
// Extract QR code from nested or flat structure, checking multiple field names
|
|
56
|
+
let qrCode;
|
|
57
|
+
let sessionId;
|
|
58
|
+
let pollingEndpoint;
|
|
59
|
+
let pollingInterval;
|
|
60
|
+
let expiresIn;
|
|
61
|
+
// Check nested structure first (data.data.qr_code_image or data.data.qr_code)
|
|
62
|
+
if (desktopData && desktopData.data && typeof desktopData.data === 'object') {
|
|
63
|
+
const innerData = desktopData.data;
|
|
64
|
+
// Try to extract from inner data
|
|
65
|
+
qrCode = innerData.qr_code_image || innerData.qr_code;
|
|
66
|
+
sessionId = innerData.session_id;
|
|
67
|
+
pollingEndpoint = innerData.status_url;
|
|
68
|
+
pollingInterval = innerData.polling_interval;
|
|
69
|
+
expiresIn = innerData.expires_in;
|
|
70
|
+
}
|
|
71
|
+
// Fall back to flat structure if no QR code found
|
|
72
|
+
if (!qrCode && desktopData) {
|
|
73
|
+
qrCode = desktopData.qr_code_image || desktopData.qr_code;
|
|
74
|
+
sessionId = sessionId || desktopData.session_id;
|
|
75
|
+
pollingEndpoint = pollingEndpoint || desktopData.status_url || desktopData.polling_endpoint;
|
|
76
|
+
pollingInterval = pollingInterval || desktopData.polling_interval;
|
|
77
|
+
expiresIn = expiresIn || desktopData.expires_in;
|
|
78
|
+
}
|
|
79
|
+
// Validate QR code exists
|
|
80
|
+
if (!qrCode) {
|
|
81
|
+
throw new Error('Invalid desktop authentication data: missing QR code');
|
|
82
|
+
}
|
|
83
|
+
// Validate session ID exists
|
|
84
|
+
if (!sessionId) {
|
|
85
|
+
throw new Error('Invalid desktop authentication data: missing session ID');
|
|
86
|
+
}
|
|
87
|
+
// Notify that QR code is ready
|
|
88
|
+
if (options === null || options === void 0 ? void 0 : options.onQRCodeReady) {
|
|
89
|
+
options.onQRCodeReady(qrCode);
|
|
90
|
+
}
|
|
91
|
+
// Use polling endpoint with this priority:
|
|
92
|
+
// 1. Developer-provided endpoint from options
|
|
93
|
+
// 2. Backend-provided endpoint
|
|
94
|
+
// 3. Error if none available
|
|
95
|
+
const finalPollingEndpoint = (options === null || options === void 0 ? void 0 : options.pollingEndpoint) || pollingEndpoint;
|
|
96
|
+
// If no polling endpoint available, return QR code data and let the app handle the rest
|
|
97
|
+
if (!finalPollingEndpoint) {
|
|
98
|
+
return {
|
|
99
|
+
authenticated: false,
|
|
100
|
+
session: data.session,
|
|
101
|
+
error: 'No polling endpoint provided - configure endpoints.polling or ensure backend provides status_url'
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
// Start polling for authentication status
|
|
105
|
+
const finalPollingInterval = (options === null || options === void 0 ? void 0 : options.pollingInterval) || pollingInterval || 2000;
|
|
106
|
+
const maxAttempts = (options === null || options === void 0 ? void 0 : options.maxPollingAttempts) || 150; // Default to 5 minutes
|
|
107
|
+
return this.startPolling(finalPollingEndpoint, sessionId, finalPollingInterval, maxAttempts, expiresIn || 300, // Default 5 minutes expiry
|
|
108
|
+
options);
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Start polling for authentication status
|
|
113
|
+
*/
|
|
114
|
+
startPolling(endpoint, sessionId, interval, maxAttempts, expiresIn, options) {
|
|
115
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
116
|
+
const startTime = Date.now();
|
|
117
|
+
const expiryTime = startTime + (expiresIn * 1000);
|
|
118
|
+
return new Promise((resolve, reject) => {
|
|
119
|
+
const poll = () => __awaiter(this, void 0, void 0, function* () {
|
|
120
|
+
try {
|
|
121
|
+
// Check if cancelled
|
|
122
|
+
if (this.isCancelled) {
|
|
123
|
+
this.stopPolling();
|
|
124
|
+
if (options === null || options === void 0 ? void 0 : options.onCancel) {
|
|
125
|
+
options.onCancel();
|
|
126
|
+
}
|
|
127
|
+
if (options === null || options === void 0 ? void 0 : options.onStatusUpdate) {
|
|
128
|
+
options.onStatusUpdate({
|
|
129
|
+
status: 'cancelled',
|
|
130
|
+
message: 'Authentication cancelled'
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
resolve({
|
|
134
|
+
authenticated: false,
|
|
135
|
+
error: 'Authentication cancelled'
|
|
136
|
+
});
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
// Check if expired
|
|
140
|
+
if (Date.now() > expiryTime) {
|
|
141
|
+
this.stopPolling();
|
|
142
|
+
if (options === null || options === void 0 ? void 0 : options.onExpired) {
|
|
143
|
+
options.onExpired();
|
|
144
|
+
}
|
|
145
|
+
if (options === null || options === void 0 ? void 0 : options.onStatusUpdate) {
|
|
146
|
+
options.onStatusUpdate({
|
|
147
|
+
status: 'expired',
|
|
148
|
+
message: 'QR code has expired'
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
resolve({
|
|
152
|
+
authenticated: false,
|
|
153
|
+
error: 'Authentication expired'
|
|
154
|
+
});
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
// Check max attempts
|
|
158
|
+
if (this.pollingAttempts >= maxAttempts) {
|
|
159
|
+
this.stopPolling();
|
|
160
|
+
if (options === null || options === void 0 ? void 0 : options.onTimeout) {
|
|
161
|
+
options.onTimeout();
|
|
162
|
+
}
|
|
163
|
+
if (options === null || options === void 0 ? void 0 : options.onStatusUpdate) {
|
|
164
|
+
options.onStatusUpdate({
|
|
165
|
+
status: 'error',
|
|
166
|
+
message: 'Polling timeout reached'
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
resolve({
|
|
170
|
+
authenticated: false,
|
|
171
|
+
error: 'Polling timeout'
|
|
172
|
+
});
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
// Build public status endpoint URL - use relative path for proxied requests
|
|
176
|
+
let statusUrl;
|
|
177
|
+
// Extract base URL and construct public endpoint
|
|
178
|
+
if (endpoint && (endpoint.startsWith('http://') || endpoint.startsWith('https://'))) {
|
|
179
|
+
// Full URL provided - extract base and use public endpoint
|
|
180
|
+
const url = new URL(endpoint);
|
|
181
|
+
statusUrl = `${url.protocol}//${url.host}/public/public/status/${sessionId}`;
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
// Use relative path that will be proxied through the app's server
|
|
185
|
+
// This ensures it goes through the same domain and uses the app's API configuration
|
|
186
|
+
statusUrl = `/api/phone-auth/status/${sessionId}`;
|
|
187
|
+
}
|
|
188
|
+
// Poll the public endpoint - no authentication required
|
|
189
|
+
const response = yield fetch(statusUrl, {
|
|
190
|
+
method: 'GET',
|
|
191
|
+
headers: {
|
|
192
|
+
'Accept': 'application/json'
|
|
193
|
+
// No Authorization header needed for public endpoint
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
// Backend Status Code Mapping:
|
|
197
|
+
// - 200 OK: Session exists (body has 'pending' or 'completed' status)
|
|
198
|
+
// - 410 Gone: Session expired after timeout
|
|
199
|
+
// - 422 Unprocessable Entity: Authentication failed or cancelled
|
|
200
|
+
// - 404 Not Found: Session doesn't exist
|
|
201
|
+
// - 400 Bad Request: Invalid session key format
|
|
202
|
+
// Handle response based on HTTP status code
|
|
203
|
+
if (response.status === 200) {
|
|
204
|
+
const result = yield response.json();
|
|
205
|
+
if (result.status === 'completed') {
|
|
206
|
+
// Authentication completed successfully
|
|
207
|
+
this.stopPolling();
|
|
208
|
+
// Close the modal if it exists
|
|
209
|
+
const modal = document.querySelector('[style*="position: fixed"]');
|
|
210
|
+
if (modal && modal.querySelector('#desktop-auth-status')) {
|
|
211
|
+
setTimeout(() => {
|
|
212
|
+
modal.remove();
|
|
213
|
+
}, 500); // Brief delay to show success message
|
|
214
|
+
}
|
|
215
|
+
if (options === null || options === void 0 ? void 0 : options.onStatusUpdate) {
|
|
216
|
+
options.onStatusUpdate({
|
|
217
|
+
status: 'authenticated',
|
|
218
|
+
message: 'Authentication successful',
|
|
219
|
+
data: result
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
// Return the session data for next steps
|
|
223
|
+
resolve({
|
|
224
|
+
authenticated: true,
|
|
225
|
+
credential: result.credential || result.session_key || sessionId,
|
|
226
|
+
session: result.session || {
|
|
227
|
+
session_key: result.session_key || sessionId,
|
|
228
|
+
status: result.status,
|
|
229
|
+
protocol: result.protocol,
|
|
230
|
+
created_at: result.created_at,
|
|
231
|
+
last_updated: result.last_updated
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
else if (result.status === 'pending') {
|
|
236
|
+
// Continue polling
|
|
237
|
+
this.pollingAttempts++;
|
|
238
|
+
if (options === null || options === void 0 ? void 0 : options.onStatusUpdate) {
|
|
239
|
+
options.onStatusUpdate({
|
|
240
|
+
status: 'pending',
|
|
241
|
+
message: `Waiting for authentication (attempt ${this.pollingAttempts}/${maxAttempts})`
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
else if (response.status === 410) {
|
|
247
|
+
// Session expired
|
|
248
|
+
this.stopPolling();
|
|
249
|
+
const errorData = yield response.json().catch(() => ({ message: 'Session expired' }));
|
|
250
|
+
// Close the modal on error
|
|
251
|
+
const modal = document.querySelector('[style*="position: fixed"]');
|
|
252
|
+
if (modal && modal.querySelector('#desktop-auth-status')) {
|
|
253
|
+
setTimeout(() => {
|
|
254
|
+
modal.remove();
|
|
255
|
+
}, 500);
|
|
256
|
+
}
|
|
257
|
+
if (options === null || options === void 0 ? void 0 : options.onExpired) {
|
|
258
|
+
options.onExpired();
|
|
259
|
+
}
|
|
260
|
+
if (options === null || options === void 0 ? void 0 : options.onStatusUpdate) {
|
|
261
|
+
options.onStatusUpdate({
|
|
262
|
+
status: 'expired',
|
|
263
|
+
message: errorData.message || 'Session has expired'
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
resolve({
|
|
267
|
+
authenticated: false,
|
|
268
|
+
error: errorData.message || 'Session expired'
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
else if (response.status === 422) {
|
|
272
|
+
// Authentication failed
|
|
273
|
+
this.stopPolling();
|
|
274
|
+
const errorData = yield response.json().catch(() => ({ message: 'Authentication failed' }));
|
|
275
|
+
// Close the modal on error
|
|
276
|
+
const modal = document.querySelector('[style*="position: fixed"]');
|
|
277
|
+
if (modal && modal.querySelector('#desktop-auth-status')) {
|
|
278
|
+
setTimeout(() => {
|
|
279
|
+
modal.remove();
|
|
280
|
+
}, 500);
|
|
281
|
+
}
|
|
282
|
+
const isUserCancelled = errorData.code === 'USER_CANCELLED';
|
|
283
|
+
if (options === null || options === void 0 ? void 0 : options.onStatusUpdate) {
|
|
284
|
+
options.onStatusUpdate({
|
|
285
|
+
status: 'error',
|
|
286
|
+
message: errorData.message || 'Authentication failed'
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
resolve({
|
|
290
|
+
authenticated: false,
|
|
291
|
+
error: isUserCancelled
|
|
292
|
+
? 'User cancelled authentication'
|
|
293
|
+
: (errorData.message || 'Authentication failed')
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
else if (response.status === 404) {
|
|
297
|
+
// Session not found
|
|
298
|
+
this.stopPolling();
|
|
299
|
+
if (options === null || options === void 0 ? void 0 : options.onStatusUpdate) {
|
|
300
|
+
options.onStatusUpdate({
|
|
301
|
+
status: 'error',
|
|
302
|
+
message: 'Session not found'
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
resolve({
|
|
306
|
+
authenticated: false,
|
|
307
|
+
error: 'Session not found'
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
else if (response.status === 400) {
|
|
311
|
+
// Invalid session key
|
|
312
|
+
this.stopPolling();
|
|
313
|
+
const errorData = yield response.json().catch(() => ({ message: 'Invalid session key' }));
|
|
314
|
+
resolve({
|
|
315
|
+
authenticated: false,
|
|
316
|
+
error: errorData.message || 'Invalid session key'
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
else {
|
|
320
|
+
// Unexpected status - continue polling
|
|
321
|
+
this.pollingAttempts++;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
catch (error) {
|
|
325
|
+
// Network or other error - continue polling
|
|
326
|
+
this.pollingAttempts++;
|
|
327
|
+
if (options === null || options === void 0 ? void 0 : options.onStatusUpdate) {
|
|
328
|
+
options.onStatusUpdate({
|
|
329
|
+
status: 'pending',
|
|
330
|
+
message: `Network error, retrying... (attempt ${this.pollingAttempts}/${maxAttempts})`
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
// Start initial poll
|
|
336
|
+
poll();
|
|
337
|
+
// Set up interval for subsequent polls
|
|
338
|
+
this.pollingIntervalId = setInterval(poll, interval);
|
|
339
|
+
});
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Stop polling
|
|
344
|
+
*/
|
|
345
|
+
stopPolling() {
|
|
346
|
+
if (this.pollingIntervalId) {
|
|
347
|
+
clearInterval(this.pollingIntervalId);
|
|
348
|
+
this.pollingIntervalId = undefined;
|
|
349
|
+
}
|
|
350
|
+
this.pollingAttempts = 0;
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Format response for backend processing
|
|
354
|
+
* Desktop strategy typically returns the credential from mobile authentication
|
|
355
|
+
*/
|
|
356
|
+
formatResponse(response) {
|
|
357
|
+
if (!response.authenticated || !response.credential) {
|
|
358
|
+
throw new Error('Authentication not completed');
|
|
359
|
+
}
|
|
360
|
+
return {
|
|
361
|
+
credential: response.credential,
|
|
362
|
+
session: response.session
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Check if desktop authentication is supported
|
|
367
|
+
* Desktop auth with QR codes works in any modern browser
|
|
368
|
+
*/
|
|
369
|
+
isSupported() {
|
|
370
|
+
// Check for required browser APIs
|
|
371
|
+
return typeof window !== 'undefined' &&
|
|
372
|
+
typeof fetch !== 'undefined' &&
|
|
373
|
+
typeof Promise !== 'undefined';
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Clean up resources (stop polling if active)
|
|
377
|
+
*/
|
|
378
|
+
cleanup() {
|
|
379
|
+
this.stopPolling();
|
|
380
|
+
this.isCancelled = false;
|
|
381
|
+
this.onCancel = undefined;
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Cancel the ongoing authentication
|
|
385
|
+
*/
|
|
386
|
+
cancel() {
|
|
387
|
+
var _a;
|
|
388
|
+
this.isCancelled = true;
|
|
389
|
+
this.stopPolling();
|
|
390
|
+
(_a = this.onCancel) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
exports.DesktopHandler = DesktopHandler;
|
|
394
|
+
/**
|
|
395
|
+
* Helper function to display QR code in a modal or inline
|
|
396
|
+
*/
|
|
397
|
+
function createQRCodeDisplay(qrCodeData, options) {
|
|
398
|
+
const container = (options === null || options === void 0 ? void 0 : options.container) || document.createElement('div');
|
|
399
|
+
container.className = 'phone-auth-qr-container';
|
|
400
|
+
// Create QR code image
|
|
401
|
+
const img = document.createElement('img');
|
|
402
|
+
img.src = qrCodeData;
|
|
403
|
+
img.alt = 'QR Code for authentication';
|
|
404
|
+
img.style.width = `${(options === null || options === void 0 ? void 0 : options.size) || 256}px`;
|
|
405
|
+
img.style.height = `${(options === null || options === void 0 ? void 0 : options.size) || 256}px`;
|
|
406
|
+
img.style.display = 'block';
|
|
407
|
+
img.style.margin = '0 auto';
|
|
408
|
+
// Add title if provided
|
|
409
|
+
if (options === null || options === void 0 ? void 0 : options.title) {
|
|
410
|
+
const title = document.createElement('h3');
|
|
411
|
+
title.textContent = options.title;
|
|
412
|
+
title.style.textAlign = 'center';
|
|
413
|
+
title.style.marginBottom = '1rem';
|
|
414
|
+
container.appendChild(title);
|
|
415
|
+
}
|
|
416
|
+
container.appendChild(img);
|
|
417
|
+
// Add description if provided
|
|
418
|
+
if (options === null || options === void 0 ? void 0 : options.description) {
|
|
419
|
+
const desc = document.createElement('p');
|
|
420
|
+
desc.textContent = options.description;
|
|
421
|
+
desc.style.textAlign = 'center';
|
|
422
|
+
desc.style.marginTop = '1rem';
|
|
423
|
+
desc.style.color = '#666';
|
|
424
|
+
container.appendChild(desc);
|
|
425
|
+
}
|
|
426
|
+
return container;
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Helper function to create a modal for QR code display
|
|
430
|
+
*/
|
|
431
|
+
function showQRCodeModal(qrCodeData, options) {
|
|
432
|
+
// Create modal overlay
|
|
433
|
+
const overlay = document.createElement('div');
|
|
434
|
+
overlay.style.cssText = `
|
|
435
|
+
position: fixed;
|
|
436
|
+
top: 0;
|
|
437
|
+
left: 0;
|
|
438
|
+
right: 0;
|
|
439
|
+
bottom: 0;
|
|
440
|
+
background-color: rgba(0, 0, 0, 0.5);
|
|
441
|
+
display: flex;
|
|
442
|
+
align-items: center;
|
|
443
|
+
justify-content: center;
|
|
444
|
+
z-index: 9999;
|
|
445
|
+
`;
|
|
446
|
+
// Create modal content
|
|
447
|
+
const modal = document.createElement('div');
|
|
448
|
+
modal.style.cssText = `
|
|
449
|
+
background: white;
|
|
450
|
+
padding: 2rem;
|
|
451
|
+
border-radius: 8px;
|
|
452
|
+
max-width: 400px;
|
|
453
|
+
position: relative;
|
|
454
|
+
`;
|
|
455
|
+
// Add close button
|
|
456
|
+
const closeBtn = document.createElement('button');
|
|
457
|
+
closeBtn.textContent = '×';
|
|
458
|
+
closeBtn.style.cssText = `
|
|
459
|
+
position: absolute;
|
|
460
|
+
top: 10px;
|
|
461
|
+
right: 10px;
|
|
462
|
+
background: none;
|
|
463
|
+
border: none;
|
|
464
|
+
font-size: 24px;
|
|
465
|
+
cursor: pointer;
|
|
466
|
+
color: #999;
|
|
467
|
+
`;
|
|
468
|
+
closeBtn.onclick = () => {
|
|
469
|
+
document.body.removeChild(overlay);
|
|
470
|
+
if (options === null || options === void 0 ? void 0 : options.onClose) {
|
|
471
|
+
options.onClose();
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
modal.appendChild(closeBtn);
|
|
475
|
+
// Add QR code display
|
|
476
|
+
const qrDisplay = createQRCodeDisplay(qrCodeData, {
|
|
477
|
+
title: (options === null || options === void 0 ? void 0 : options.title) || 'Scan QR Code to Authenticate',
|
|
478
|
+
description: (options === null || options === void 0 ? void 0 : options.description) || 'Use your mobile device to scan this QR code'
|
|
479
|
+
});
|
|
480
|
+
modal.appendChild(qrDisplay);
|
|
481
|
+
// Add loading spinner placeholder
|
|
482
|
+
const statusDiv = document.createElement('div');
|
|
483
|
+
statusDiv.id = 'desktop-auth-status';
|
|
484
|
+
statusDiv.style.cssText = `
|
|
485
|
+
text-align: center;
|
|
486
|
+
margin-top: 1rem;
|
|
487
|
+
color: #666;
|
|
488
|
+
font-size: 14px;
|
|
489
|
+
`;
|
|
490
|
+
modal.appendChild(statusDiv);
|
|
491
|
+
overlay.appendChild(modal);
|
|
492
|
+
document.body.appendChild(overlay);
|
|
493
|
+
// Close modal on overlay click
|
|
494
|
+
overlay.onclick = (e) => {
|
|
495
|
+
if (e.target === overlay) {
|
|
496
|
+
document.body.removeChild(overlay);
|
|
497
|
+
if (options === null || options === void 0 ? void 0 : options.onClose) {
|
|
498
|
+
options.onClose();
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
};
|
|
502
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strategy Handlers Export
|
|
3
|
+
* Provides implementations for TS43, Link, and Desktop authentication strategies
|
|
4
|
+
*/
|
|
5
|
+
export type { StrategyHandler } from './types';
|
|
6
|
+
export { TS43Handler } from './ts43';
|
|
7
|
+
export { LinkHandler } from './link';
|
|
8
|
+
export type { LinkAuthOptions, LinkAuthResult, PollingStatus as LinkPollingStatus } from './link';
|
|
9
|
+
export { DesktopHandler, createQRCodeDisplay, showQRCodeModal } from './desktop';
|
|
10
|
+
export type { DesktopAuthOptions, DesktopAuthResult, PollingStatus } from './desktop';
|
|
11
|
+
export type { TS43Data, LinkData } from '../types';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Strategy Handlers Export
|
|
4
|
+
* Provides implementations for TS43, Link, and Desktop authentication strategies
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.showQRCodeModal = exports.createQRCodeDisplay = exports.DesktopHandler = exports.LinkHandler = exports.TS43Handler = void 0;
|
|
8
|
+
var ts43_1 = require("./ts43");
|
|
9
|
+
Object.defineProperty(exports, "TS43Handler", { enumerable: true, get: function () { return ts43_1.TS43Handler; } });
|
|
10
|
+
var link_1 = require("./link");
|
|
11
|
+
Object.defineProperty(exports, "LinkHandler", { enumerable: true, get: function () { return link_1.LinkHandler; } });
|
|
12
|
+
var desktop_1 = require("./desktop");
|
|
13
|
+
Object.defineProperty(exports, "DesktopHandler", { enumerable: true, get: function () { return desktop_1.DesktopHandler; } });
|
|
14
|
+
Object.defineProperty(exports, "createQRCodeDisplay", { enumerable: true, get: function () { return desktop_1.createQRCodeDisplay; } });
|
|
15
|
+
Object.defineProperty(exports, "showQRCodeModal", { enumerable: true, get: function () { return desktop_1.showQRCodeModal; } });
|