@glideidentity/web-client-sdk 4.4.8-beta.1 → 4.4.8-beta.3
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 +263 -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/strategies/link.js +97 -5
- 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 +263 -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/strategies/link.js +97 -5
- 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
|
@@ -3,4 +3,6 @@ export * from './types';
|
|
|
3
3
|
export { PhoneAuthErrorCode, isPhoneAuthError, isUserError, getUserMessage, isErrorCode, getRetryDelay, isRetryableError, serializeError, createErrorBreadcrumb } from './error-utils';
|
|
4
4
|
export { validatePhoneNumber, validatePlmn, validateUseCaseRequirements, validateConsentData, validateNonce } from './validation-utils';
|
|
5
5
|
export { MobileDebugConsole } from './ui/mobile-debug-console';
|
|
6
|
-
export {
|
|
6
|
+
export { isExtendedResponse, isCredential, isAuthCredential, isLinkStrategy, isTS43Strategy, isDesktopStrategy, getStrategy, hasPollingControls, hasTrigger,
|
|
7
|
+
// Deprecated aliases
|
|
8
|
+
isHeadlessResult, requiresPolling, requiresUserAction } from './type-guards';
|
|
@@ -55,6 +55,7 @@ export declare class DesktopHandler implements StrategyHandler {
|
|
|
55
55
|
private pollingAttempts;
|
|
56
56
|
private isCancelled;
|
|
57
57
|
private onCancel?;
|
|
58
|
+
private pollingReject?;
|
|
58
59
|
/**
|
|
59
60
|
* Maps backend HTTP status codes to client status
|
|
60
61
|
* @param httpStatus HTTP status code from backend
|
|
@@ -57,7 +57,20 @@ export class DesktopHandler {
|
|
|
57
57
|
if (desktopData && desktopData.data && typeof desktopData.data === 'object') {
|
|
58
58
|
const innerData = desktopData.data;
|
|
59
59
|
// Try to extract from inner data
|
|
60
|
-
|
|
60
|
+
// Support both single QR (qr_code_image) and dual-platform QR (ios/android)
|
|
61
|
+
if (innerData.ios_qr_image || innerData.android_qr_image) {
|
|
62
|
+
// Dual-platform QR format
|
|
63
|
+
qrCode = {
|
|
64
|
+
iosQRCode: innerData.ios_qr_image,
|
|
65
|
+
androidQRCode: innerData.android_qr_image,
|
|
66
|
+
iosUrl: innerData.ios_url,
|
|
67
|
+
androidUrl: innerData.android_url
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
// Single QR format (legacy)
|
|
72
|
+
qrCode = innerData.qr_code_image || innerData.qr_code;
|
|
73
|
+
}
|
|
61
74
|
sessionId = innerData.session_id;
|
|
62
75
|
pollingEndpoint = innerData.status_url;
|
|
63
76
|
pollingInterval = innerData.polling_interval;
|
|
@@ -84,33 +97,44 @@ export class DesktopHandler {
|
|
|
84
97
|
options.onQRCodeReady(qrCode);
|
|
85
98
|
}
|
|
86
99
|
// Use polling endpoint with this priority:
|
|
87
|
-
// 1. Developer-provided endpoint from options
|
|
100
|
+
// 1. Developer-provided endpoint from options (highest priority)
|
|
88
101
|
// 2. Backend-provided endpoint
|
|
89
|
-
// 3.
|
|
90
|
-
|
|
91
|
-
|
|
102
|
+
// 3. Hardcoded fallback
|
|
103
|
+
console.log('[Desktop QR] Polling endpoint selection:');
|
|
104
|
+
console.log(' - options?.pollingEndpoint:', options === null || options === void 0 ? void 0 : options.pollingEndpoint);
|
|
105
|
+
console.log(' - backend pollingEndpoint:', pollingEndpoint);
|
|
106
|
+
let finalPollingEndpoint = options === null || options === void 0 ? void 0 : options.pollingEndpoint;
|
|
107
|
+
let endpointSource = 'options';
|
|
92
108
|
if (!finalPollingEndpoint) {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
session: data.session,
|
|
96
|
-
error: 'No polling endpoint provided - configure endpoints.polling or ensure backend provides status_url'
|
|
97
|
-
};
|
|
109
|
+
finalPollingEndpoint = pollingEndpoint; // Backend-provided status_url
|
|
110
|
+
endpointSource = 'backend';
|
|
98
111
|
}
|
|
112
|
+
if (!finalPollingEndpoint) {
|
|
113
|
+
// Use hardcoded fallback - this will be constructed in the polling function
|
|
114
|
+
console.log('[Desktop QR] No polling endpoint provided, will use hardcoded fallback');
|
|
115
|
+
endpointSource = 'fallback';
|
|
116
|
+
}
|
|
117
|
+
console.log('[Desktop QR] Selected endpoint:', finalPollingEndpoint, 'from source:', endpointSource);
|
|
99
118
|
// Start polling for authentication status
|
|
100
119
|
const finalPollingInterval = (options === null || options === void 0 ? void 0 : options.pollingInterval) || pollingInterval || 2000;
|
|
101
120
|
const maxAttempts = (options === null || options === void 0 ? void 0 : options.maxPollingAttempts) || 150; // Default to 5 minutes
|
|
102
|
-
|
|
103
|
-
|
|
121
|
+
console.log(`[Desktop QR] Starting polling - endpoint source: ${endpointSource}, interval: ${finalPollingInterval}ms, max attempts: ${maxAttempts}`);
|
|
122
|
+
return this.startPolling(finalPollingEndpoint || '', // Pass empty string if undefined, will use fallback
|
|
123
|
+
sessionId, finalPollingInterval, maxAttempts, expiresIn || 300, // Default 5 minutes expiry
|
|
124
|
+
options, endpointSource // Pass the endpoint source for logging
|
|
125
|
+
);
|
|
104
126
|
});
|
|
105
127
|
}
|
|
106
128
|
/**
|
|
107
129
|
* Start polling for authentication status
|
|
108
130
|
*/
|
|
109
|
-
startPolling(
|
|
110
|
-
return __awaiter(this,
|
|
131
|
+
startPolling(endpoint_1, sessionId_1, interval_1, maxAttempts_1, expiresIn_1, options_1) {
|
|
132
|
+
return __awaiter(this, arguments, void 0, function* (endpoint, sessionId, interval, maxAttempts, expiresIn, options, endpointSource = 'unknown') {
|
|
111
133
|
const startTime = Date.now();
|
|
112
134
|
const expiryTime = startTime + (expiresIn * 1000);
|
|
113
135
|
return new Promise((resolve, reject) => {
|
|
136
|
+
// Store the reject function so we can call it from cancel()
|
|
137
|
+
this.pollingReject = reject;
|
|
114
138
|
const poll = () => __awaiter(this, void 0, void 0, function* () {
|
|
115
139
|
try {
|
|
116
140
|
// Check if cancelled
|
|
@@ -125,9 +149,10 @@ export class DesktopHandler {
|
|
|
125
149
|
message: 'Authentication cancelled'
|
|
126
150
|
});
|
|
127
151
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
152
|
+
// Reject the promise with a cancellation error
|
|
153
|
+
reject({
|
|
154
|
+
code: 'USER_DENIED',
|
|
155
|
+
message: 'Authentication cancelled by user'
|
|
131
156
|
});
|
|
132
157
|
return;
|
|
133
158
|
}
|
|
@@ -175,12 +200,19 @@ export class DesktopHandler {
|
|
|
175
200
|
const url = new URL(endpoint);
|
|
176
201
|
statusUrl = `${url.protocol}//${url.host}/public/public/status/${sessionId}`;
|
|
177
202
|
}
|
|
203
|
+
else if (endpoint && endpoint !== '') {
|
|
204
|
+
// Relative path provided (e.g. '/api/phone-auth/status')
|
|
205
|
+
// Append session ID to the provided endpoint
|
|
206
|
+
statusUrl = `${endpoint}/${sessionId}`;
|
|
207
|
+
}
|
|
178
208
|
else {
|
|
179
|
-
//
|
|
209
|
+
// No endpoint provided - use hardcoded fallback
|
|
180
210
|
// This ensures it goes through the same domain and uses the app's API configuration
|
|
181
211
|
statusUrl = `/api/phone-auth/status/${sessionId}`;
|
|
182
212
|
}
|
|
183
213
|
// Poll the public endpoint - no authentication required
|
|
214
|
+
console.log(`[Desktop QR] Polling status (attempt ${this.pollingAttempts}/${maxAttempts})`);
|
|
215
|
+
console.log(`[Desktop QR] Using ${endpointSource} endpoint: ${statusUrl}`);
|
|
184
216
|
const response = yield fetch(statusUrl, {
|
|
185
217
|
method: 'GET',
|
|
186
218
|
headers: {
|
|
@@ -188,6 +220,7 @@ export class DesktopHandler {
|
|
|
188
220
|
// No Authorization header needed for public endpoint
|
|
189
221
|
}
|
|
190
222
|
});
|
|
223
|
+
console.log(`[Desktop QR] Status response: ${response.status} ${response.statusText}`);
|
|
191
224
|
// Backend Status Code Mapping:
|
|
192
225
|
// - 200 OK: Session exists (body has 'pending' or 'completed' status)
|
|
193
226
|
// - 410 Gone: Session expired after timeout
|
|
@@ -215,6 +248,7 @@ export class DesktopHandler {
|
|
|
215
248
|
});
|
|
216
249
|
}
|
|
217
250
|
// Return the session data for next steps
|
|
251
|
+
this.pollingReject = undefined; // Clear the reject function on success
|
|
218
252
|
resolve({
|
|
219
253
|
authenticated: true,
|
|
220
254
|
credential: result.credential || result.session_key || sessionId,
|
|
@@ -339,10 +373,12 @@ export class DesktopHandler {
|
|
|
339
373
|
*/
|
|
340
374
|
stopPolling() {
|
|
341
375
|
if (this.pollingIntervalId) {
|
|
376
|
+
console.log('[Desktop QR] Stopping polling');
|
|
342
377
|
clearInterval(this.pollingIntervalId);
|
|
343
378
|
this.pollingIntervalId = undefined;
|
|
344
379
|
}
|
|
345
380
|
this.pollingAttempts = 0;
|
|
381
|
+
// Don't clear pollingReject here - it's needed by cancel()
|
|
346
382
|
}
|
|
347
383
|
/**
|
|
348
384
|
* Format response for backend processing
|
|
@@ -374,15 +410,25 @@ export class DesktopHandler {
|
|
|
374
410
|
this.stopPolling();
|
|
375
411
|
this.isCancelled = false;
|
|
376
412
|
this.onCancel = undefined;
|
|
413
|
+
this.pollingReject = undefined;
|
|
377
414
|
}
|
|
378
415
|
/**
|
|
379
416
|
* Cancel the ongoing authentication
|
|
380
417
|
*/
|
|
381
418
|
cancel() {
|
|
382
419
|
var _a;
|
|
420
|
+
console.log('[Desktop QR] Cancelling authentication');
|
|
383
421
|
this.isCancelled = true;
|
|
384
422
|
this.stopPolling();
|
|
385
423
|
(_a = this.onCancel) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
424
|
+
// Immediately reject the polling promise
|
|
425
|
+
if (this.pollingReject) {
|
|
426
|
+
this.pollingReject({
|
|
427
|
+
code: 'USER_DENIED',
|
|
428
|
+
message: 'Authentication cancelled by user'
|
|
429
|
+
});
|
|
430
|
+
this.pollingReject = undefined;
|
|
431
|
+
}
|
|
386
432
|
}
|
|
387
433
|
}
|
|
388
434
|
/**
|
|
@@ -23,12 +23,21 @@ export class LinkHandler {
|
|
|
23
23
|
*/
|
|
24
24
|
invoke(data, options) {
|
|
25
25
|
return __awaiter(this, void 0, void 0, function* () {
|
|
26
|
+
console.log('[Link Auth] 🔗 SDK Version: 4.4.8-beta.2 (with enhanced error logging)');
|
|
27
|
+
console.log('[Link Auth] 🔗 invoke() called with data:', JSON.stringify(data, null, 2));
|
|
28
|
+
console.log('[Link Auth] Options:', options ? JSON.stringify({
|
|
29
|
+
pollingInterval: options.pollingInterval,
|
|
30
|
+
maxPollingAttempts: options.maxPollingAttempts,
|
|
31
|
+
pollingEndpoint: options.pollingEndpoint
|
|
32
|
+
}) : 'none');
|
|
26
33
|
// Extract link data from prepare response
|
|
27
34
|
const linkData = data.data;
|
|
28
35
|
if (!linkData || !linkData.url) {
|
|
29
36
|
throw new Error('Invalid link data: missing URL');
|
|
30
37
|
}
|
|
31
38
|
const sessionKey = data.session.session_key;
|
|
39
|
+
console.log('[Link Auth] Session key:', sessionKey);
|
|
40
|
+
console.log('[Link Auth] Link URL:', linkData.url);
|
|
32
41
|
// Open authentication app without navigating away from current page
|
|
33
42
|
this.openAuthenticationLink(linkData.url);
|
|
34
43
|
// Notify that link was opened
|
|
@@ -60,12 +69,19 @@ export class LinkHandler {
|
|
|
60
69
|
const interval = (options === null || options === void 0 ? void 0 : options.pollingInterval) || 2000; // Fixed 2 second interval
|
|
61
70
|
const maxAttempts = (options === null || options === void 0 ? void 0 : options.maxPollingAttempts) || 150; // 5 minutes with 2s interval
|
|
62
71
|
let attempts = 0;
|
|
72
|
+
console.log('[Link Auth] 🚀 Starting polling:', {
|
|
73
|
+
sessionKey,
|
|
74
|
+
interval: `${interval}ms`,
|
|
75
|
+
maxAttempts,
|
|
76
|
+
linkDataAvailable: !!linkData
|
|
77
|
+
});
|
|
63
78
|
return new Promise((resolve, reject) => {
|
|
64
79
|
this.isPolling = true;
|
|
65
80
|
const poll = () => __awaiter(this, void 0, void 0, function* () {
|
|
66
81
|
if (!this.isPolling) {
|
|
67
82
|
return; // Polling was stopped
|
|
68
83
|
}
|
|
84
|
+
let statusUrl = ''; // Declare at function scope for catch block access
|
|
69
85
|
try {
|
|
70
86
|
attempts++;
|
|
71
87
|
// Check max attempts
|
|
@@ -84,16 +100,54 @@ export class LinkHandler {
|
|
|
84
100
|
return;
|
|
85
101
|
}
|
|
86
102
|
// Build public status endpoint URL
|
|
87
|
-
|
|
88
|
-
//
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
103
|
+
// Use the same priority logic as Desktop strategy:
|
|
104
|
+
// 1. options?.pollingEndpoint (already contains invoke options OR client config from client.ts)
|
|
105
|
+
// 2. Backend-provided status_url from linkData
|
|
106
|
+
// 3. Hardcoded fallback to API server
|
|
107
|
+
let endpoint = options === null || options === void 0 ? void 0 : options.pollingEndpoint;
|
|
108
|
+
let endpointSource = 'options';
|
|
109
|
+
if (!endpoint && linkData.status_url) {
|
|
110
|
+
endpoint = linkData.status_url;
|
|
111
|
+
endpointSource = 'backend';
|
|
112
|
+
}
|
|
113
|
+
console.log('[Link Auth] Polling endpoint selection:');
|
|
114
|
+
console.log(' - options?.pollingEndpoint:', options === null || options === void 0 ? void 0 : options.pollingEndpoint);
|
|
115
|
+
console.log(' - linkData.status_url:', linkData.status_url);
|
|
116
|
+
console.log(' - selected endpoint:', endpoint, 'from source:', endpointSource);
|
|
117
|
+
// Build the status URL based on endpoint format (same as Desktop)
|
|
118
|
+
if (endpoint && (endpoint.startsWith('http://') || endpoint.startsWith('https://'))) {
|
|
119
|
+
// Full URL provided
|
|
120
|
+
if (endpoint.includes('{{session_id}}')) {
|
|
121
|
+
statusUrl = endpoint.replace('{{session_id}}', sessionKey);
|
|
122
|
+
}
|
|
123
|
+
else if (!endpoint.includes(sessionKey)) {
|
|
124
|
+
// If it doesn't already contain the session ID, check if it's a base URL
|
|
125
|
+
const url = new URL(endpoint);
|
|
126
|
+
statusUrl = `${url.protocol}//${url.host}/public/public/status/${sessionKey}`;
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
statusUrl = endpoint;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
else if (endpoint && endpoint !== '') {
|
|
133
|
+
// Relative path provided (e.g. '/api/phone-auth/status')
|
|
134
|
+
const origin = typeof window !== 'undefined' ? window.location.origin : '';
|
|
135
|
+
if (endpoint.includes('{{session_id}}')) {
|
|
136
|
+
statusUrl = origin + endpoint.replace('{{session_id}}', sessionKey);
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
// Append session ID to the provided endpoint
|
|
140
|
+
statusUrl = origin + endpoint + '/' + sessionKey;
|
|
141
|
+
}
|
|
92
142
|
}
|
|
93
143
|
else {
|
|
144
|
+
// No endpoint provided - use hardcoded fallback
|
|
94
145
|
statusUrl = `https://api.glideidentity.app/public/public/status/${sessionKey}`;
|
|
146
|
+
endpointSource = 'fallback';
|
|
95
147
|
}
|
|
148
|
+
console.log(`[Link Auth] Using ${endpointSource} endpoint: ${statusUrl}`);
|
|
96
149
|
// Poll public status endpoint - no authentication required
|
|
150
|
+
console.log(`[Link Auth] Polling status (attempt ${attempts}/${maxAttempts}): ${statusUrl}`);
|
|
97
151
|
const response = yield fetch(statusUrl, {
|
|
98
152
|
method: 'GET',
|
|
99
153
|
headers: {
|
|
@@ -101,12 +155,16 @@ export class LinkHandler {
|
|
|
101
155
|
// No Authorization header needed for public endpoint
|
|
102
156
|
}
|
|
103
157
|
});
|
|
158
|
+
console.log(`[Link Auth] Poll response - Status: ${response.status}, OK: ${response.ok}`);
|
|
104
159
|
// Handle based on HTTP status code
|
|
105
160
|
if (response.status === 200) {
|
|
106
161
|
// Session is active (pending or completed)
|
|
107
162
|
const result = yield response.json();
|
|
163
|
+
console.log('[Link Auth] Poll response data:', JSON.stringify(result, null, 2));
|
|
108
164
|
if (result.status === 'completed') {
|
|
109
165
|
// Authentication completed successfully
|
|
166
|
+
console.log('[Link Auth] ✅ Authentication COMPLETED! Session:', sessionKey);
|
|
167
|
+
console.log('[Link Auth] Full completion result:', JSON.stringify(result, null, 2));
|
|
110
168
|
this.stopPolling();
|
|
111
169
|
// Authentication completed successfully
|
|
112
170
|
if (options === null || options === void 0 ? void 0 : options.onStatusUpdate) {
|
|
@@ -131,6 +189,7 @@ export class LinkHandler {
|
|
|
131
189
|
}
|
|
132
190
|
else if (result.status === 'pending') {
|
|
133
191
|
// Continue polling
|
|
192
|
+
console.log('[Link Auth] Status still pending, continuing to poll...');
|
|
134
193
|
if (options === null || options === void 0 ? void 0 : options.onStatusUpdate) {
|
|
135
194
|
options.onStatusUpdate({
|
|
136
195
|
status: 'pending',
|
|
@@ -138,9 +197,14 @@ export class LinkHandler {
|
|
|
138
197
|
});
|
|
139
198
|
}
|
|
140
199
|
}
|
|
200
|
+
else {
|
|
201
|
+
// Unexpected status value
|
|
202
|
+
console.log('[Link Auth] ⚠️ Unexpected status value:', result.status, 'Full result:', JSON.stringify(result, null, 2));
|
|
203
|
+
}
|
|
141
204
|
}
|
|
142
205
|
else if (response.status === 410) {
|
|
143
206
|
// Session expired
|
|
207
|
+
console.log('[Link Auth] ❌ Session expired (410)');
|
|
144
208
|
this.stopPolling();
|
|
145
209
|
const errorData = yield response.json().catch(() => ({ message: 'Session expired' }));
|
|
146
210
|
if (options === null || options === void 0 ? void 0 : options.onTimeout) {
|
|
@@ -156,8 +220,10 @@ export class LinkHandler {
|
|
|
156
220
|
}
|
|
157
221
|
else if (response.status === 422) {
|
|
158
222
|
// Authentication failed
|
|
223
|
+
console.log('[Link Auth] ❌ Authentication failed (422)');
|
|
159
224
|
this.stopPolling();
|
|
160
225
|
const errorData = yield response.json().catch(() => ({ message: 'Authentication failed' }));
|
|
226
|
+
console.log('[Link Auth] Error data:', JSON.stringify(errorData, null, 2));
|
|
161
227
|
const isUserCancelled = errorData.code === 'USER_CANCELLED';
|
|
162
228
|
const errorMsg = isUserCancelled
|
|
163
229
|
? 'User cancelled authentication'
|
|
@@ -173,6 +239,7 @@ export class LinkHandler {
|
|
|
173
239
|
}
|
|
174
240
|
else if (response.status === 404) {
|
|
175
241
|
// Session not found
|
|
242
|
+
console.log('[Link Auth] ❌ Session not found (404)');
|
|
176
243
|
this.stopPolling();
|
|
177
244
|
if (options === null || options === void 0 ? void 0 : options.onStatusUpdate) {
|
|
178
245
|
options.onStatusUpdate({
|
|
@@ -184,16 +251,40 @@ export class LinkHandler {
|
|
|
184
251
|
}
|
|
185
252
|
else if (response.status === 400) {
|
|
186
253
|
// Invalid session key
|
|
254
|
+
console.log('[Link Auth] ❌ Invalid session key (400)');
|
|
187
255
|
this.stopPolling();
|
|
188
256
|
const errorData = yield response.json().catch(() => ({ message: 'Invalid session key' }));
|
|
257
|
+
console.log('[Link Auth] Error data:', JSON.stringify(errorData, null, 2));
|
|
189
258
|
reject(new Error(errorData.message || 'Invalid session key'));
|
|
190
259
|
}
|
|
191
260
|
else {
|
|
192
261
|
// Unexpected status - continue polling
|
|
262
|
+
console.log('[Link Auth] ⚠️ Unexpected HTTP status:', response.status, 'continuing to poll...');
|
|
263
|
+
try {
|
|
264
|
+
const body = yield response.text();
|
|
265
|
+
console.log('[Link Auth] Response body:', body);
|
|
266
|
+
}
|
|
267
|
+
catch (e) {
|
|
268
|
+
console.log('[Link Auth] Could not read response body');
|
|
269
|
+
}
|
|
193
270
|
}
|
|
194
271
|
}
|
|
195
272
|
catch (error) {
|
|
196
273
|
// Network or other error - continue polling
|
|
274
|
+
console.error('[Link Auth] 🔴 Polling error:', error.message || error);
|
|
275
|
+
console.error('[Link Auth] Error details:', {
|
|
276
|
+
name: error.name,
|
|
277
|
+
message: error.message,
|
|
278
|
+
stack: error.stack,
|
|
279
|
+
statusUrl: statusUrl,
|
|
280
|
+
attempt: attempts,
|
|
281
|
+
error: error
|
|
282
|
+
});
|
|
283
|
+
// Check if it's a CORS error (common on mobile)
|
|
284
|
+
if (error.message && error.message.toLowerCase().includes('failed')) {
|
|
285
|
+
console.error('[Link Auth] ⚠️ Possible CORS issue. Status URL:', statusUrl);
|
|
286
|
+
console.error('[Link Auth] Make sure the API endpoint allows CORS from your ngrok domain');
|
|
287
|
+
}
|
|
197
288
|
if (options === null || options === void 0 ? void 0 : options.onStatusUpdate) {
|
|
198
289
|
options.onStatusUpdate({
|
|
199
290
|
status: 'pending',
|
|
@@ -213,6 +304,7 @@ export class LinkHandler {
|
|
|
213
304
|
* Stop polling
|
|
214
305
|
*/
|
|
215
306
|
stopPolling() {
|
|
307
|
+
console.log('[Link Auth] 🏁 Stopping polling');
|
|
216
308
|
this.isPolling = false;
|
|
217
309
|
if (this.pollingInterval) {
|
|
218
310
|
clearInterval(this.pollingInterval);
|
|
@@ -4,29 +4,29 @@
|
|
|
4
4
|
* These utilities help developers work with the SDK responses in a type-safe way
|
|
5
5
|
* without having to write their own type checking logic.
|
|
6
6
|
*/
|
|
7
|
-
import type {
|
|
7
|
+
import type { AnyExtendedResponse, DesktopExtendedResponse, LinkExtendedResponse, TS43ExtendedResponse, AuthCredential } from './api-types';
|
|
8
8
|
/**
|
|
9
|
-
* Type guard to check if the result is
|
|
10
|
-
* or a Credential (
|
|
9
|
+
* Type guard to check if the result is an ExtendedResponse (extended mode)
|
|
10
|
+
* or a Credential (standard mode).
|
|
11
11
|
*
|
|
12
12
|
* @example
|
|
13
13
|
* ```typescript
|
|
14
|
-
* const result = await invokeSecurePrompt(sdkRequest);
|
|
14
|
+
* const result = await invokeSecurePrompt(sdkRequest, { executionMode: 'extended' });
|
|
15
15
|
*
|
|
16
|
-
* if (
|
|
17
|
-
* // TypeScript knows this is
|
|
16
|
+
* if (isExtendedResponse(result)) {
|
|
17
|
+
* // TypeScript knows this is ExtendedResponse
|
|
18
18
|
* console.log(result.strategy);
|
|
19
|
-
* await result.
|
|
19
|
+
* await result.cancel();
|
|
20
20
|
* } else {
|
|
21
21
|
* // TypeScript knows this is a Credential
|
|
22
22
|
* const processedResult = await verifyPhoneNumberCredential(result, session);
|
|
23
23
|
* }
|
|
24
24
|
* ```
|
|
25
25
|
*/
|
|
26
|
-
export declare function
|
|
26
|
+
export declare function isExtendedResponse(result: any): result is AnyExtendedResponse;
|
|
27
27
|
/**
|
|
28
|
-
* Type guard to check if the result is a Credential (
|
|
29
|
-
* A credential is either a string token or an object without
|
|
28
|
+
* Type guard to check if the result is a Credential (standard mode response).
|
|
29
|
+
* A credential is either a string token or an object without ExtendedResponse properties.
|
|
30
30
|
*
|
|
31
31
|
* @example
|
|
32
32
|
* ```typescript
|
|
@@ -40,55 +40,61 @@ export declare function isCredential(result: any): result is string | {
|
|
|
40
40
|
[aggregator_id: string]: string | string[];
|
|
41
41
|
};
|
|
42
42
|
/**
|
|
43
|
-
* Type guard to check if
|
|
43
|
+
* Type guard to check if the result is an AuthCredential object.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```typescript
|
|
47
|
+
* if (isAuthCredential(result)) {
|
|
48
|
+
* console.log(result.credential);
|
|
49
|
+
* console.log(result.authenticated);
|
|
50
|
+
* }
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export declare function isAuthCredential(result: any): result is AuthCredential;
|
|
54
|
+
/**
|
|
55
|
+
* Type guard to check if an ExtendedResponse is using the Link strategy.
|
|
44
56
|
* Link strategy involves opening an app link (App Clip on iOS, app on Android).
|
|
45
57
|
*
|
|
46
58
|
* @example
|
|
47
59
|
* ```typescript
|
|
48
|
-
* if (
|
|
49
|
-
* //
|
|
50
|
-
*
|
|
51
|
-
* await result.
|
|
60
|
+
* if (isExtendedResponse(result) && isLinkStrategy(result)) {
|
|
61
|
+
* // Re-trigger app opening if needed
|
|
62
|
+
* result.trigger();
|
|
63
|
+
* await result.credential;
|
|
52
64
|
* }
|
|
53
65
|
* ```
|
|
54
66
|
*/
|
|
55
|
-
export declare function isLinkStrategy(result:
|
|
56
|
-
strategy: 'link';
|
|
57
|
-
};
|
|
67
|
+
export declare function isLinkStrategy(result: AnyExtendedResponse): result is LinkExtendedResponse;
|
|
58
68
|
/**
|
|
59
|
-
* Type guard to check if
|
|
69
|
+
* Type guard to check if an ExtendedResponse is using the TS43 strategy.
|
|
60
70
|
* TS43 strategy uses the browser's Digital Credentials API.
|
|
61
71
|
*
|
|
62
72
|
* @example
|
|
63
73
|
* ```typescript
|
|
64
|
-
* if (
|
|
65
|
-
* //
|
|
66
|
-
*
|
|
74
|
+
* if (isExtendedResponse(result) && isTS43Strategy(result)) {
|
|
75
|
+
* // Re-trigger credential request if needed
|
|
76
|
+
* await result.trigger();
|
|
67
77
|
* }
|
|
68
78
|
* ```
|
|
69
79
|
*/
|
|
70
|
-
export declare function isTS43Strategy(result:
|
|
71
|
-
strategy: 'ts43';
|
|
72
|
-
};
|
|
80
|
+
export declare function isTS43Strategy(result: AnyExtendedResponse): result is TS43ExtendedResponse;
|
|
73
81
|
/**
|
|
74
|
-
* Type guard to check if
|
|
82
|
+
* Type guard to check if an ExtendedResponse is using the Desktop strategy.
|
|
75
83
|
* Desktop strategy involves QR codes for cross-device authentication.
|
|
76
84
|
*
|
|
77
85
|
* @example
|
|
78
86
|
* ```typescript
|
|
79
|
-
* if (
|
|
87
|
+
* if (isExtendedResponse(result) && isDesktopStrategy(result)) {
|
|
80
88
|
* // Show custom QR code UI
|
|
81
|
-
* displayQRCode(result.
|
|
82
|
-
* await result.
|
|
89
|
+
* displayQRCode(result.qr_code_data);
|
|
90
|
+
* await result.start_polling();
|
|
83
91
|
* }
|
|
84
92
|
* ```
|
|
85
93
|
*/
|
|
86
|
-
export declare function isDesktopStrategy(result:
|
|
87
|
-
strategy: 'desktop';
|
|
88
|
-
};
|
|
94
|
+
export declare function isDesktopStrategy(result: AnyExtendedResponse): result is DesktopExtendedResponse;
|
|
89
95
|
/**
|
|
90
96
|
* Helper function to safely get the authentication strategy from any result.
|
|
91
|
-
* Returns undefined if the result is not
|
|
97
|
+
* Returns undefined if the result is not an ExtendedResponse.
|
|
92
98
|
*
|
|
93
99
|
* @example
|
|
94
100
|
* ```typescript
|
|
@@ -100,26 +106,38 @@ export declare function isDesktopStrategy(result: HeadlessResult): result is Hea
|
|
|
100
106
|
*/
|
|
101
107
|
export declare function getStrategy(result: any): 'link' | 'ts43' | 'desktop' | undefined;
|
|
102
108
|
/**
|
|
103
|
-
* Helper function to determine if a result
|
|
104
|
-
* Link and Desktop strategies
|
|
109
|
+
* Helper function to determine if a result has polling controls.
|
|
110
|
+
* Link and Desktop strategies have polling controls in extended mode.
|
|
105
111
|
*
|
|
106
112
|
* @example
|
|
107
113
|
* ```typescript
|
|
108
|
-
* if (
|
|
109
|
-
* await result.
|
|
114
|
+
* if (hasPollingControls(result)) {
|
|
115
|
+
* await result.start_polling();
|
|
110
116
|
* }
|
|
111
117
|
* ```
|
|
112
118
|
*/
|
|
113
|
-
export declare function
|
|
119
|
+
export declare function hasPollingControls(result: any): boolean;
|
|
114
120
|
/**
|
|
115
|
-
* Helper function to determine if a result
|
|
116
|
-
*
|
|
121
|
+
* Helper function to determine if a result has a trigger method.
|
|
122
|
+
* Link and TS43 strategies have trigger methods in extended mode.
|
|
117
123
|
*
|
|
118
124
|
* @example
|
|
119
125
|
* ```typescript
|
|
120
|
-
* if (
|
|
121
|
-
*
|
|
126
|
+
* if (hasTrigger(result)) {
|
|
127
|
+
* result.trigger();
|
|
122
128
|
* }
|
|
123
129
|
* ```
|
|
124
130
|
*/
|
|
125
|
-
export declare function
|
|
131
|
+
export declare function hasTrigger(result: any): boolean;
|
|
132
|
+
/**
|
|
133
|
+
* @deprecated Use isExtendedResponse instead
|
|
134
|
+
*/
|
|
135
|
+
export declare const isHeadlessResult: typeof isExtendedResponse;
|
|
136
|
+
/**
|
|
137
|
+
* @deprecated Use hasPollingControls instead
|
|
138
|
+
*/
|
|
139
|
+
export declare const requiresPolling: typeof hasPollingControls;
|
|
140
|
+
/**
|
|
141
|
+
* @deprecated This function is no longer needed as extended mode handles user actions differently
|
|
142
|
+
*/
|
|
143
|
+
export declare const requiresUserAction: (result: any) => boolean;
|