@glideidentity/web-client-sdk 4.4.8-beta.1 → 4.4.8-beta.2
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/dist/adapters/react/usePhoneAuth.d.ts +1 -1
- package/dist/adapters/vue/useClient.d.ts +3 -3
- package/dist/adapters/vue/usePhoneAuth.d.ts +1 -1
- package/dist/browser/web-client-sdk.min.js +1 -1
- package/dist/core/phone-auth/api-types.d.ts +112 -27
- package/dist/core/phone-auth/client.d.ts +13 -11
- package/dist/core/phone-auth/client.js +257 -248
- package/dist/core/phone-auth/index.d.ts +1 -1
- package/dist/core/phone-auth/index.js +7 -2
- package/dist/core/phone-auth/strategies/desktop.d.ts +1 -0
- package/dist/core/phone-auth/strategies/desktop.js +64 -18
- package/dist/core/phone-auth/type-guards.d.ts +61 -43
- package/dist/core/phone-auth/type-guards.js +82 -44
- package/dist/core/phone-auth/ui/modal.js +14 -1
- package/dist/core/version.js +1 -1
- package/dist/esm/adapters/react/usePhoneAuth.d.ts +1 -1
- package/dist/esm/adapters/vue/useClient.d.ts +3 -3
- package/dist/esm/adapters/vue/usePhoneAuth.d.ts +1 -1
- package/dist/esm/core/phone-auth/api-types.d.ts +112 -27
- package/dist/esm/core/phone-auth/client.d.ts +13 -11
- package/dist/esm/core/phone-auth/client.js +257 -248
- package/dist/esm/core/phone-auth/index.d.ts +1 -1
- package/dist/esm/core/phone-auth/index.js +3 -1
- package/dist/esm/core/phone-auth/strategies/desktop.d.ts +1 -0
- package/dist/esm/core/phone-auth/strategies/desktop.js +64 -18
- package/dist/esm/core/phone-auth/type-guards.d.ts +61 -43
- package/dist/esm/core/phone-auth/type-guards.js +76 -41
- package/dist/esm/core/phone-auth/ui/modal.js +14 -1
- package/dist/esm/core/version.js +1 -1
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/index.js +3 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +7 -2
- package/package.json +1 -1
|
@@ -54,7 +54,7 @@ const link_1 = require("./strategies/link");
|
|
|
54
54
|
const modal_1 = require("./ui/modal");
|
|
55
55
|
class PhoneAuthClient {
|
|
56
56
|
constructor(config = {}) {
|
|
57
|
-
var _a, _b, _c;
|
|
57
|
+
var _a, _b, _c, _d;
|
|
58
58
|
this.crossDeviceActive = false;
|
|
59
59
|
this.retryCount = 0;
|
|
60
60
|
this.sessionCache = new Map();
|
|
@@ -64,7 +64,8 @@ class PhoneAuthClient {
|
|
|
64
64
|
this.config = {
|
|
65
65
|
endpoints: {
|
|
66
66
|
prepare: ((_a = config.endpoints) === null || _a === void 0 ? void 0 : _a.prepare) || '/api/magic-auth/prepare',
|
|
67
|
-
process: ((_b = config.endpoints) === null || _b === void 0 ? void 0 : _b.process) || '/api/magic-auth/process'
|
|
67
|
+
process: ((_b = config.endpoints) === null || _b === void 0 ? void 0 : _b.process) || '/api/magic-auth/process',
|
|
68
|
+
polling: (_c = config.endpoints) === null || _c === void 0 ? void 0 : _c.polling // Pass through the polling endpoint
|
|
68
69
|
},
|
|
69
70
|
timeout: config.timeout || 30000,
|
|
70
71
|
pollingInterval: config.pollingInterval || 2000, // Default 2 seconds
|
|
@@ -87,7 +88,7 @@ class PhoneAuthClient {
|
|
|
87
88
|
custom: config.logger
|
|
88
89
|
});
|
|
89
90
|
// Initialize developer tools if configured
|
|
90
|
-
if (((
|
|
91
|
+
if (((_d = config.devtools) === null || _d === void 0 ? void 0 : _d.showMobileConsole) && typeof window !== 'undefined') {
|
|
91
92
|
Promise.resolve().then(() => __importStar(require('./ui/mobile-debug-console'))).then(({ MobileDebugConsole }) => {
|
|
92
93
|
MobileDebugConsole.init();
|
|
93
94
|
console.log('[PhoneAuth] Mobile debug console enabled');
|
|
@@ -487,23 +488,25 @@ class PhoneAuthClient {
|
|
|
487
488
|
* });
|
|
488
489
|
* ```
|
|
489
490
|
*
|
|
490
|
-
* @example
|
|
491
|
+
* @example Extended Mode (returns control methods)
|
|
491
492
|
* ```typescript
|
|
492
|
-
* // Get
|
|
493
|
+
* // Get control methods for custom implementation
|
|
493
494
|
* const result = await phoneAuth.invokeSecurePrompt(prepareResult, {
|
|
494
|
-
*
|
|
495
|
+
* executionMode: 'extended',
|
|
496
|
+
* preventDefaultUI: true // Desktop: no modal
|
|
495
497
|
* });
|
|
496
498
|
*
|
|
497
|
-
* if (result.strategy === '
|
|
498
|
-
* //
|
|
499
|
-
*
|
|
500
|
-
*
|
|
499
|
+
* if (result.strategy === 'desktop') {
|
|
500
|
+
* // Show custom QR UI
|
|
501
|
+
* showCustomQR(result.qr_code_data);
|
|
502
|
+
* // Start polling
|
|
503
|
+
* await result.start_polling();
|
|
501
504
|
* }
|
|
502
505
|
* ```
|
|
503
506
|
*
|
|
504
507
|
* @param prepareResponse - Response from prepare() with strategy and data
|
|
505
|
-
* @param options - Control UI behavior
|
|
506
|
-
* @returns Credential or
|
|
508
|
+
* @param options - Control UI behavior and response type
|
|
509
|
+
* @returns Credential or ExtendedResponse based on executionMode
|
|
507
510
|
*/
|
|
508
511
|
invokeSecurePrompt(prepareResponse, options) {
|
|
509
512
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -511,7 +514,7 @@ class PhoneAuthClient {
|
|
|
511
514
|
// This ensures we work with plain objects for browser APIs
|
|
512
515
|
// Vue's reactivity system wraps objects in Proxies which can interfere
|
|
513
516
|
// with browser APIs like Digital Credentials API that expect plain objects
|
|
514
|
-
var _a, _b, _c;
|
|
517
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
|
|
515
518
|
// Try structuredClone first (modern browsers), but catch errors and fallback to JSON method
|
|
516
519
|
let plainResponse;
|
|
517
520
|
try {
|
|
@@ -531,16 +534,17 @@ class PhoneAuthClient {
|
|
|
531
534
|
console.log('[PhoneAuth] Session cache size:', this.sessionCache.size);
|
|
532
535
|
console.log('[PhoneAuth] Retry count:', this.retryCount);
|
|
533
536
|
console.log('[PhoneAuth] PrepareResponse received:', JSON.stringify(plainResponse, null, 2));
|
|
534
|
-
// Check if we're
|
|
537
|
+
// Check if we're using new InvokeOptions format
|
|
535
538
|
// Properly detect InvokeOptions by checking for any of its properties
|
|
536
|
-
const invokeOptions = options && ('
|
|
539
|
+
const invokeOptions = options && ('preventDefaultUI' in options ||
|
|
540
|
+
'executionMode' in options ||
|
|
537
541
|
'theme' in options ||
|
|
538
542
|
'modalOptions' in options ||
|
|
539
543
|
'callbacks' in options) ? options : undefined;
|
|
540
|
-
//
|
|
544
|
+
// Get configuration from options
|
|
541
545
|
const strategy = plainResponse.authentication_strategy;
|
|
542
|
-
const
|
|
543
|
-
const
|
|
546
|
+
const preventDefaultUI = (_a = invokeOptions === null || invokeOptions === void 0 ? void 0 : invokeOptions.preventDefaultUI) !== null && _a !== void 0 ? _a : false;
|
|
547
|
+
const executionMode = (_b = invokeOptions === null || invokeOptions === void 0 ? void 0 : invokeOptions.executionMode) !== null && _b !== void 0 ? _b : 'standard';
|
|
544
548
|
// DesktopAuthOptions is only used if not InvokeOptions
|
|
545
549
|
const desktopOptions = options && !invokeOptions ? options : undefined;
|
|
546
550
|
// Handle based on authentication strategy
|
|
@@ -679,270 +683,275 @@ class PhoneAuthClient {
|
|
|
679
683
|
throw error;
|
|
680
684
|
}
|
|
681
685
|
});
|
|
682
|
-
//
|
|
683
|
-
//
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
686
|
+
// TS43 always auto-triggers (no SDK UI ever)
|
|
687
|
+
// The Digital Credentials API provides its own OS-level UI
|
|
688
|
+
let credentialPromise;
|
|
689
|
+
try {
|
|
690
|
+
// Always try to trigger immediately
|
|
691
|
+
const vpToken = yield enhancedTriggerTS43();
|
|
692
|
+
// Convert to AuthCredential format
|
|
693
|
+
credentialPromise = Promise.resolve({
|
|
694
|
+
credential: typeof vpToken === 'string' ? vpToken : Object.values(vpToken)[0],
|
|
695
|
+
session: plainResponse.session,
|
|
696
|
+
authenticated: true
|
|
697
|
+
});
|
|
698
|
+
}
|
|
699
|
+
catch (error) {
|
|
700
|
+
// If auto-trigger fails, create a rejected promise
|
|
701
|
+
credentialPromise = Promise.reject(error);
|
|
702
|
+
}
|
|
703
|
+
// Handle based on execution mode
|
|
704
|
+
if (executionMode === 'extended') {
|
|
705
|
+
// Extended mode - return control methods
|
|
701
706
|
return {
|
|
702
|
-
strategy:
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
707
|
+
strategy: 'ts43',
|
|
708
|
+
session: plainResponse.session,
|
|
709
|
+
credential: credentialPromise,
|
|
710
|
+
// Re-trigger credential request
|
|
711
|
+
trigger: () => __awaiter(this, void 0, void 0, function* () {
|
|
712
|
+
const vpToken = yield enhancedTriggerTS43();
|
|
713
|
+
// Update the credential promise
|
|
714
|
+
credentialPromise = Promise.resolve({
|
|
715
|
+
credential: typeof vpToken === 'string' ? vpToken : Object.values(vpToken)[0],
|
|
716
|
+
session: plainResponse.session,
|
|
717
|
+
authenticated: true
|
|
718
|
+
});
|
|
719
|
+
}),
|
|
720
|
+
cancel: () => {
|
|
721
|
+
// TS43 doesn't have a way to cancel once triggered
|
|
722
|
+
// but we can reject the promise
|
|
723
|
+
credentialPromise = Promise.reject(this.createError(error_utils_1.PhoneAuthErrorCode.USER_DENIED, 'Authentication cancelled'));
|
|
724
|
+
}
|
|
706
725
|
};
|
|
707
726
|
}
|
|
708
727
|
else {
|
|
709
|
-
//
|
|
710
|
-
//
|
|
711
|
-
|
|
728
|
+
// Standard mode - just return credential
|
|
729
|
+
// Wait for and return the credential
|
|
730
|
+
const credential = yield credentialPromise;
|
|
731
|
+
// Return in standard format
|
|
732
|
+
return {
|
|
733
|
+
[plainResponse.session.session_key]: credential.credential
|
|
734
|
+
};
|
|
712
735
|
}
|
|
713
736
|
}
|
|
714
737
|
else if (plainResponse.authentication_strategy === API.AUTHENTICATION_STRATEGY.DESKTOP) {
|
|
715
738
|
// Desktop strategy - QR code based authentication
|
|
716
739
|
const desktopData = plainResponse.data;
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
740
|
+
const handler = new desktop_1.DesktopHandler();
|
|
741
|
+
// Extract QR code data - convert to QRCodeData format for modal
|
|
742
|
+
const qrCodeData = {
|
|
743
|
+
iosQRCode: ((_c = desktopData.data) === null || _c === void 0 ? void 0 : _c.ios_qr_image) || desktopData.ios_qr_image ||
|
|
744
|
+
((_d = desktopData.data) === null || _d === void 0 ? void 0 : _d.qr_code_image) || desktopData.qr_code_image || desktopData.qr_code || '',
|
|
745
|
+
androidQRCode: ((_e = desktopData.data) === null || _e === void 0 ? void 0 : _e.android_qr_image) || desktopData.android_qr_image,
|
|
746
|
+
iosUrl: ((_f = desktopData.data) === null || _f === void 0 ? void 0 : _f.ios_url) || desktopData.ios_url,
|
|
747
|
+
androidUrl: ((_g = desktopData.data) === null || _g === void 0 ? void 0 : _g.android_url) || desktopData.android_url
|
|
748
|
+
};
|
|
749
|
+
// Also keep snake_case format for extended response
|
|
750
|
+
const qrCodeDataSnakeCase = {
|
|
751
|
+
ios_qr_image: ((_h = desktopData.data) === null || _h === void 0 ? void 0 : _h.ios_qr_image) || desktopData.ios_qr_image,
|
|
752
|
+
android_qr_image: ((_j = desktopData.data) === null || _j === void 0 ? void 0 : _j.android_qr_image) || desktopData.android_qr_image,
|
|
753
|
+
qr_code: ((_k = desktopData.data) === null || _k === void 0 ? void 0 : _k.qr_code_image) || desktopData.qr_code_image || desktopData.qr_code,
|
|
754
|
+
challenge: (_l = desktopData.data) === null || _l === void 0 ? void 0 : _l.challenge
|
|
755
|
+
};
|
|
756
|
+
// Polling options
|
|
757
|
+
const pollingEndpointToUse = (invokeOptions === null || invokeOptions === void 0 ? void 0 : invokeOptions.pollingEndpoint) ||
|
|
758
|
+
(desktopOptions === null || desktopOptions === void 0 ? void 0 : desktopOptions.pollingEndpoint) ||
|
|
759
|
+
((_m = this.config.endpoints) === null || _m === void 0 ? void 0 : _m.polling);
|
|
760
|
+
const pollingOptions = Object.assign(Object.assign({}, desktopOptions), { pollingEndpoint: pollingEndpointToUse, pollingInterval: (invokeOptions === null || invokeOptions === void 0 ? void 0 : invokeOptions.pollingInterval) || (desktopOptions === null || desktopOptions === void 0 ? void 0 : desktopOptions.pollingInterval) || this.config.pollingInterval || 2000, maxPollingAttempts: (invokeOptions === null || invokeOptions === void 0 ? void 0 : invokeOptions.maxPollingAttempts) || (desktopOptions === null || desktopOptions === void 0 ? void 0 : desktopOptions.maxPollingAttempts) || this.config.maxPollingAttempts || 150, onQRCodeReady: undefined, onStatusUpdate: undefined });
|
|
761
|
+
// Decide whether to show modal based on preventDefaultUI
|
|
762
|
+
const showModal = !preventDefaultUI;
|
|
763
|
+
let modal;
|
|
764
|
+
let modalRef = undefined;
|
|
765
|
+
console.log('[Desktop] Modal decision:', {
|
|
766
|
+
preventDefaultUI,
|
|
767
|
+
showModal,
|
|
768
|
+
executionMode,
|
|
769
|
+
hasInvokeOptions: !!invokeOptions,
|
|
770
|
+
hasDesktopOptions: !!desktopOptions
|
|
771
|
+
});
|
|
772
|
+
if (showModal) {
|
|
773
|
+
console.log('[Desktop] Creating modal with QR data:', qrCodeData);
|
|
774
|
+
// Create and setup modal
|
|
775
|
+
modal = new modal_1.AuthModal(invokeOptions === null || invokeOptions === void 0 ? void 0 : invokeOptions.modalOptions, invokeOptions === null || invokeOptions === void 0 ? void 0 : invokeOptions.callbacks);
|
|
776
|
+
modal.setCloseCallback(() => {
|
|
777
|
+
this.log('Desktop QR modal closed by user, cancelling polling');
|
|
778
|
+
handler.cancel();
|
|
779
|
+
});
|
|
780
|
+
// Add UI callbacks to polling options
|
|
781
|
+
pollingOptions.onQRCodeReady = (qrData) => {
|
|
782
|
+
console.log('[Desktop] onQRCodeReady callback triggered:', qrData);
|
|
783
|
+
modal.showQRCode(qrData, 'Scan with your mobile device');
|
|
784
|
+
};
|
|
785
|
+
pollingOptions.onStatusUpdate = (status) => {
|
|
786
|
+
if (status.status === 'pending') {
|
|
787
|
+
modal.updateStatus('Waiting for authentication...');
|
|
788
|
+
}
|
|
789
|
+
else if (status.status === 'authenticated') {
|
|
790
|
+
modal.updateStatus('Authentication successful!');
|
|
791
|
+
setTimeout(() => modal.close(), 1500);
|
|
745
792
|
}
|
|
746
|
-
else {
|
|
747
|
-
|
|
793
|
+
else if (status.status === 'expired') {
|
|
794
|
+
modal.updateStatus('QR code expired', true);
|
|
748
795
|
}
|
|
796
|
+
else if (status.status === 'error') {
|
|
797
|
+
modal.updateStatus('Authentication failed', true);
|
|
798
|
+
}
|
|
799
|
+
};
|
|
800
|
+
// Note: We don't show the QR code here. It will be shown by the onQRCodeReady callback
|
|
801
|
+
// that gets triggered immediately when handler.invoke() is called
|
|
802
|
+
modalRef = modal;
|
|
803
|
+
}
|
|
804
|
+
else {
|
|
805
|
+
console.log('[Desktop] Modal not shown - preventDefaultUI is true');
|
|
806
|
+
}
|
|
807
|
+
// Create credential promise
|
|
808
|
+
const startPolling = () => handler.invoke(plainResponse, pollingOptions).then(result => {
|
|
809
|
+
if (result.authenticated && result.credential) {
|
|
810
|
+
return {
|
|
811
|
+
credential: result.credential,
|
|
812
|
+
session: plainResponse.session,
|
|
813
|
+
authenticated: true
|
|
814
|
+
};
|
|
815
|
+
}
|
|
816
|
+
else {
|
|
817
|
+
throw this.createError(error_utils_1.PhoneAuthErrorCode.USER_DENIED, result.error || 'Desktop authentication failed');
|
|
818
|
+
}
|
|
819
|
+
});
|
|
820
|
+
// Handle based on execution mode
|
|
821
|
+
if (executionMode === 'extended') {
|
|
822
|
+
// Extended mode - return control methods
|
|
823
|
+
const credentialPromise = showModal ? startPolling() : Promise.resolve({
|
|
824
|
+
credential: '',
|
|
825
|
+
session: plainResponse.session,
|
|
826
|
+
authenticated: false
|
|
749
827
|
});
|
|
750
828
|
return {
|
|
751
|
-
strategy:
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
829
|
+
strategy: 'desktop',
|
|
830
|
+
session: plainResponse.session,
|
|
831
|
+
credential: credentialPromise,
|
|
832
|
+
qr_code_data: qrCodeDataSnakeCase,
|
|
833
|
+
modal_ref: modalRef,
|
|
834
|
+
start_polling: startPolling,
|
|
835
|
+
stop_polling: () => {
|
|
836
|
+
handler.cleanup();
|
|
837
|
+
if (modal)
|
|
838
|
+
modal.close();
|
|
839
|
+
},
|
|
840
|
+
cancel: () => {
|
|
841
|
+
handler.cancel();
|
|
842
|
+
handler.cleanup();
|
|
843
|
+
if (modal)
|
|
844
|
+
modal.close();
|
|
845
|
+
},
|
|
846
|
+
is_polling: showModal // If modal shown, polling already started
|
|
755
847
|
};
|
|
756
848
|
}
|
|
757
849
|
else {
|
|
758
|
-
//
|
|
759
|
-
|
|
760
|
-
const modal = new modal_1.AuthModal(invokeOptions === null || invokeOptions === void 0 ? void 0 : invokeOptions.modalOptions, invokeOptions === null || invokeOptions === void 0 ? void 0 : invokeOptions.callbacks);
|
|
761
|
-
// Default options with modal display
|
|
762
|
-
const options = Object.assign(Object.assign({}, desktopOptions), {
|
|
763
|
-
// Don't pass config.endpoints.polling - let backend-provided status_url take priority
|
|
764
|
-
// Only explicit desktopOptions.pollingEndpoint will override backend URL
|
|
765
|
-
pollingInterval: (desktopOptions === null || desktopOptions === void 0 ? void 0 : desktopOptions.pollingInterval) || this.config.pollingInterval || 2000, maxPollingAttempts: (desktopOptions === null || desktopOptions === void 0 ? void 0 : desktopOptions.maxPollingAttempts) || this.config.maxPollingAttempts || 150, onQRCodeReady: (qrCodeData) => {
|
|
766
|
-
// Show QR code in modal (supports both single and dual-platform)
|
|
767
|
-
modal.showQRCode(qrCodeData, 'Scan with your mobile device');
|
|
768
|
-
}, onStatusUpdate: (status) => {
|
|
769
|
-
// Update status in modal
|
|
770
|
-
if (status.status === 'pending') {
|
|
771
|
-
modal.updateStatus('Waiting for authentication...');
|
|
772
|
-
}
|
|
773
|
-
else if (status.status === 'authenticated') {
|
|
774
|
-
modal.updateStatus('Authentication successful!');
|
|
775
|
-
setTimeout(() => modal.close(), 1500);
|
|
776
|
-
}
|
|
777
|
-
else if (status.status === 'expired') {
|
|
778
|
-
modal.updateStatus('QR code expired', true);
|
|
779
|
-
}
|
|
780
|
-
else if (status.status === 'error') {
|
|
781
|
-
modal.updateStatus('Authentication failed', true);
|
|
782
|
-
}
|
|
783
|
-
} });
|
|
850
|
+
// Standard mode - return credential when complete
|
|
851
|
+
// Start polling and wait for result
|
|
784
852
|
try {
|
|
785
|
-
const
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
}
|
|
795
|
-
// Also check flat structure
|
|
796
|
-
if (!sessionId || sessionId === 'default') {
|
|
797
|
-
sessionId = desktopData.session_id || sessionId;
|
|
798
|
-
}
|
|
853
|
+
const credential = yield startPolling();
|
|
854
|
+
// Extract session ID for compatibility
|
|
855
|
+
let sessionId = 'default';
|
|
856
|
+
if (desktopData && typeof desktopData === 'object') {
|
|
857
|
+
if (desktopData.data && typeof desktopData.data === 'object') {
|
|
858
|
+
sessionId = desktopData.data.session_id || sessionId;
|
|
859
|
+
}
|
|
860
|
+
if (!sessionId || sessionId === 'default') {
|
|
861
|
+
sessionId = desktopData.session_id || sessionId;
|
|
799
862
|
}
|
|
800
|
-
// Return credential in expected format for subsequent API calls
|
|
801
|
-
return {
|
|
802
|
-
[sessionId]: result.credential
|
|
803
|
-
};
|
|
804
|
-
}
|
|
805
|
-
else {
|
|
806
|
-
throw this.createError(error_utils_1.PhoneAuthErrorCode.USER_DENIED, result.error || 'Desktop authentication failed');
|
|
807
|
-
}
|
|
808
|
-
}
|
|
809
|
-
catch (error) {
|
|
810
|
-
if (error instanceof Error && error.name === 'PhoneAuthError') {
|
|
811
|
-
throw error;
|
|
812
863
|
}
|
|
813
|
-
|
|
864
|
+
return { [sessionId]: credential.credential };
|
|
814
865
|
}
|
|
815
866
|
finally {
|
|
816
|
-
// Ensure handler cleanup
|
|
817
867
|
handler.cleanup();
|
|
818
|
-
modal
|
|
868
|
+
if (modal)
|
|
869
|
+
modal.close();
|
|
819
870
|
}
|
|
820
871
|
}
|
|
821
872
|
}
|
|
822
873
|
else if (plainResponse.authentication_strategy === API.AUTHENTICATION_STRATEGY.LINK) {
|
|
823
874
|
// Link strategy - app-based authentication (iOS/Android)
|
|
824
875
|
const linkData = plainResponse.data;
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
strategy: API.AUTHENTICATION_STRATEGY.LINK,
|
|
837
|
-
url: linkData.url,
|
|
838
|
-
success: true
|
|
839
|
-
});
|
|
840
|
-
}
|
|
841
|
-
catch (error) {
|
|
842
|
-
(_b = invokeOptions === null || invokeOptions === void 0 ? void 0 : invokeOptions.onTriggerAttempt) === null || _b === void 0 ? void 0 : _b.call(invokeOptions, {
|
|
843
|
-
strategy: API.AUTHENTICATION_STRATEGY.LINK,
|
|
844
|
-
url: linkData.url,
|
|
845
|
-
success: false,
|
|
846
|
-
error
|
|
847
|
-
});
|
|
848
|
-
}
|
|
849
|
-
};
|
|
850
|
-
// Auto-trigger by default for Link strategy
|
|
851
|
-
// This opens the App Clip immediately while preserving user gesture context
|
|
852
|
-
if ((invokeOptions === null || invokeOptions === void 0 ? void 0 : invokeOptions.autoTrigger) !== false) {
|
|
853
|
-
triggerLink();
|
|
876
|
+
const handler = new link_1.LinkHandler();
|
|
877
|
+
// Create reusable trigger function that ONLY opens the App Clip
|
|
878
|
+
const triggerLink = () => {
|
|
879
|
+
var _a, _b;
|
|
880
|
+
try {
|
|
881
|
+
window.open(linkData.url, '_blank');
|
|
882
|
+
(_a = invokeOptions === null || invokeOptions === void 0 ? void 0 : invokeOptions.onTriggerAttempt) === null || _a === void 0 ? void 0 : _a.call(invokeOptions, {
|
|
883
|
+
strategy: API.AUTHENTICATION_STRATEGY.LINK,
|
|
884
|
+
url: linkData.url,
|
|
885
|
+
success: true
|
|
886
|
+
});
|
|
854
887
|
}
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
888
|
+
catch (error) {
|
|
889
|
+
(_b = invokeOptions === null || invokeOptions === void 0 ? void 0 : invokeOptions.onTriggerAttempt) === null || _b === void 0 ? void 0 : _b.call(invokeOptions, {
|
|
890
|
+
strategy: API.AUTHENTICATION_STRATEGY.LINK,
|
|
891
|
+
url: linkData.url,
|
|
892
|
+
success: false,
|
|
893
|
+
error
|
|
894
|
+
});
|
|
895
|
+
}
|
|
896
|
+
};
|
|
897
|
+
// Link always auto-opens the app (no SDK UI by default)
|
|
898
|
+
// Open immediately to preserve user gesture context
|
|
899
|
+
if ((invokeOptions === null || invokeOptions === void 0 ? void 0 : invokeOptions.autoTrigger) !== false) {
|
|
900
|
+
triggerLink();
|
|
901
|
+
}
|
|
902
|
+
// Start polling in the background
|
|
903
|
+
const pollingOptions = {
|
|
904
|
+
pollingEndpoint: (invokeOptions === null || invokeOptions === void 0 ? void 0 : invokeOptions.pollingEndpoint) || ((_o = this.config.endpoints) === null || _o === void 0 ? void 0 : _o.polling),
|
|
905
|
+
pollingInterval: (invokeOptions === null || invokeOptions === void 0 ? void 0 : invokeOptions.pollingInterval) || this.config.pollingInterval || 2000,
|
|
906
|
+
maxPollingAttempts: (invokeOptions === null || invokeOptions === void 0 ? void 0 : invokeOptions.maxPollingAttempts) || this.config.maxPollingAttempts || 150,
|
|
907
|
+
onLinkOpened: undefined,
|
|
908
|
+
onStatusUpdate: undefined
|
|
909
|
+
};
|
|
910
|
+
// Create credential promise
|
|
911
|
+
const credentialPromise = handler.invoke(plainResponse, pollingOptions).then(result => {
|
|
912
|
+
if (result.authenticated && result.credential) {
|
|
913
|
+
return {
|
|
914
|
+
credential: result.credential,
|
|
915
|
+
session: plainResponse.session,
|
|
916
|
+
authenticated: true
|
|
917
|
+
};
|
|
918
|
+
}
|
|
919
|
+
else {
|
|
920
|
+
throw this.createError(error_utils_1.PhoneAuthErrorCode.USER_DENIED, result.error || 'Link authentication failed');
|
|
921
|
+
}
|
|
922
|
+
});
|
|
923
|
+
// Handle based on execution mode
|
|
924
|
+
if (executionMode === 'extended') {
|
|
925
|
+
// Extended mode - return control methods
|
|
880
926
|
return {
|
|
881
|
-
strategy:
|
|
882
|
-
url: linkData.url,
|
|
883
|
-
pollingPromise,
|
|
927
|
+
strategy: 'link',
|
|
884
928
|
session: plainResponse.session,
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
};
|
|
889
|
-
}
|
|
890
|
-
else {
|
|
891
|
-
// UI mode - show button to open app link
|
|
892
|
-
const modal = new modal_1.AuthModal(invokeOptions === null || invokeOptions === void 0 ? void 0 : invokeOptions.modalOptions, invokeOptions === null || invokeOptions === void 0 ? void 0 : invokeOptions.callbacks);
|
|
893
|
-
const handler = new link_1.LinkHandler();
|
|
894
|
-
// Show link button in modal - user MUST click the button to open the app link
|
|
895
|
-
// This is critical for iOS to recognize the app link (requires user interaction)
|
|
896
|
-
yield modal.showLinkButton(linkData.url);
|
|
897
|
-
// Link options - the app link will be opened by user clicking the modal button
|
|
898
|
-
const options = {
|
|
899
|
-
// Use configured polling endpoint if available
|
|
900
|
-
pollingEndpoint: (_c = this.config.endpoints) === null || _c === void 0 ? void 0 : _c.polling,
|
|
901
|
-
pollingInterval: this.config.pollingInterval || 2000, // Default 2 second interval
|
|
902
|
-
maxPollingAttempts: this.config.maxPollingAttempts || 150, // Default 5 minutes total
|
|
903
|
-
onLinkOpened: () => {
|
|
904
|
-
this.log('Authentication app link opened');
|
|
905
|
-
modal.updateStatus('Waiting for app authentication...');
|
|
929
|
+
credential: credentialPromise,
|
|
930
|
+
data: {
|
|
931
|
+
app_url: linkData.url
|
|
906
932
|
},
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
933
|
+
// Re-open the app link
|
|
934
|
+
trigger: triggerLink,
|
|
935
|
+
// Polling control
|
|
936
|
+
start_polling: () => handler.invoke(plainResponse, pollingOptions).then(result => ({
|
|
937
|
+
credential: result.credential || '',
|
|
938
|
+
session: plainResponse.session,
|
|
939
|
+
authenticated: result.authenticated
|
|
940
|
+
})),
|
|
941
|
+
stop_polling: () => handler.cleanup(),
|
|
942
|
+
cancel: () => {
|
|
943
|
+
handler.cancel();
|
|
944
|
+
handler.cleanup();
|
|
916
945
|
},
|
|
917
|
-
|
|
918
|
-
this.log('Link authentication timed out');
|
|
919
|
-
modal.updateStatus('Authentication timed out', true);
|
|
920
|
-
}
|
|
946
|
+
is_polling: true
|
|
921
947
|
};
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
return { [aggregatorId]: result.credential };
|
|
930
|
-
}
|
|
931
|
-
else {
|
|
932
|
-
throw this.createError(error_utils_1.PhoneAuthErrorCode.VERIFICATION_FAILED, result.error || 'Link authentication failed');
|
|
933
|
-
}
|
|
934
|
-
}
|
|
935
|
-
catch (error) {
|
|
936
|
-
modal.close();
|
|
937
|
-
if (error instanceof Error && error.name === 'PhoneAuthError') {
|
|
938
|
-
throw error;
|
|
939
|
-
}
|
|
940
|
-
throw this.createError(error_utils_1.PhoneAuthErrorCode.INTERNAL_SERVER_ERROR, `Link authentication error: ${error instanceof Error ? error.message : 'Unknown error'}`, { originalError: error });
|
|
941
|
-
}
|
|
942
|
-
finally {
|
|
943
|
-
// Ensure handler cleanup
|
|
944
|
-
handler.cleanup();
|
|
945
|
-
}
|
|
948
|
+
}
|
|
949
|
+
else {
|
|
950
|
+
// Standard mode - return credential when complete
|
|
951
|
+
// Wait for credential and return in standard format
|
|
952
|
+
const credential = yield credentialPromise;
|
|
953
|
+
const aggregatorId = this.config.aggregatorId || 'default';
|
|
954
|
+
return { [aggregatorId]: credential.credential };
|
|
946
955
|
}
|
|
947
956
|
}
|
|
948
957
|
else {
|
|
@@ -4,4 +4,4 @@ export { PhoneAuthErrorCode, isPhoneAuthError, isUserError, getUserMessage, isEr
|
|
|
4
4
|
export type { PhoneAuthErrorCode as PhoneAuthErrorCodeType } from './error-utils';
|
|
5
5
|
export { validatePhoneNumber, validatePlmn, validateUseCaseRequirements, validateConsentData, validateNonce } from './validation-utils';
|
|
6
6
|
export { MobileDebugConsole } from './ui/mobile-debug-console';
|
|
7
|
-
export {
|
|
7
|
+
export { isExtendedResponse, isCredential, isAuthCredential, isLinkStrategy, isTS43Strategy, isDesktopStrategy, getStrategy, hasPollingControls, hasTrigger, isHeadlessResult, requiresPolling, requiresUserAction } from './type-guards';
|