@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
package/README.md
ADDED
|
@@ -0,0 +1,938 @@
|
|
|
1
|
+
# Glide Web Client SDK
|
|
2
|
+
|
|
3
|
+
The official web SDK for integrating Glide's carrier-grade phone verification into your web applications. Supports React, Vue, Angular, and vanilla JavaScript/TypeScript.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🚀 **Instant Verification**: Direct carrier verification without SMS
|
|
8
|
+
- 🔐 **Fraud Resistant**: Can't be intercepted or spoofed like SMS codes
|
|
9
|
+
- ⚡ **Real-time Progress**: Track verification steps with detailed state management
|
|
10
|
+
- 📱 **Cross-Device Support**: Desktop QR code, mobile App Clips, and Android deep links
|
|
11
|
+
- 🤖 **Smart Defaults**: Automatic headless/UI mode selection based on strategy
|
|
12
|
+
- 🔄 **Unified Trigger Pattern**: Consistent API for Link and TS43 authentication
|
|
13
|
+
- 🌐 **Framework Support**: React, Vue 3, Angular, and vanilla JS/TS
|
|
14
|
+
- 🛡️ **Type Safe**: Full TypeScript support with type guards and IntelliSense
|
|
15
|
+
- 🎯 **Developer Friendly**: Clear error messages and browser compatibility checks
|
|
16
|
+
|
|
17
|
+
## What's New in v4.4
|
|
18
|
+
|
|
19
|
+
### 🎉 Smart Defaults & Better DX
|
|
20
|
+
- **Smart Mode Selection**: Link/TS43 default to headless, Desktop defaults to UI mode
|
|
21
|
+
- **Type Guards**: Helper functions for type-safe result handling
|
|
22
|
+
- **Unified Trigger Pattern**: Consistent `trigger()` function for all strategies
|
|
23
|
+
- **Auto-Trigger Support**: Automatic App Clip opening with retry capability
|
|
24
|
+
- **TypeScript Enhancements**: Full IntelliSense and type safety
|
|
25
|
+
- **Cleaner API**: Removed non-functional options (iframe, redundant callbacks)
|
|
26
|
+
|
|
27
|
+
## Table of Contents
|
|
28
|
+
|
|
29
|
+
- [Installation](#installation)
|
|
30
|
+
- [Quick Start](#quick-start)
|
|
31
|
+
- [Smart Defaults & Type Guards](#smart-defaults--type-guards)
|
|
32
|
+
- [Cross-Device Support](#cross-device-support)
|
|
33
|
+
- [Framework Examples](#framework-examples)
|
|
34
|
+
- [Error Handling](#error-handling)
|
|
35
|
+
- [Advanced Features](#advanced-features)
|
|
36
|
+
- [Browser Support](#browser-support)
|
|
37
|
+
- [API Reference](#api-reference)
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npm install glide-web-client-sdk
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Quick Start
|
|
46
|
+
|
|
47
|
+
### React
|
|
48
|
+
|
|
49
|
+
```jsx
|
|
50
|
+
import { usePhoneAuth } from 'glide-web-client-sdk/react'
|
|
51
|
+
|
|
52
|
+
function PhoneVerification() {
|
|
53
|
+
const {
|
|
54
|
+
getPhoneNumber,
|
|
55
|
+
verifyPhoneNumber,
|
|
56
|
+
retryLastRequest, // NEW: Manual retry capability
|
|
57
|
+
isLoading,
|
|
58
|
+
error,
|
|
59
|
+
result,
|
|
60
|
+
currentStep,
|
|
61
|
+
isSupported
|
|
62
|
+
} = usePhoneAuth({
|
|
63
|
+
endpoints: {
|
|
64
|
+
prepare: '/api/phone-auth/prepare',
|
|
65
|
+
process: '/api/phone-auth/process'
|
|
66
|
+
},
|
|
67
|
+
// Optional callbacks for monitoring
|
|
68
|
+
onCrossDeviceDetected: () => {
|
|
69
|
+
console.log('QR code detected - user completing on phone');
|
|
70
|
+
},
|
|
71
|
+
onRetryAttempt: (attempt, maxAttempts) => {
|
|
72
|
+
console.log(`Silent retry ${attempt}/${maxAttempts}`);
|
|
73
|
+
}
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
const handleVerify = async () => {
|
|
77
|
+
try {
|
|
78
|
+
// Verify a specific phone number
|
|
79
|
+
await verifyPhoneNumber('+1234567890')
|
|
80
|
+
} catch (error) {
|
|
81
|
+
// Error only thrown after all retries exhausted
|
|
82
|
+
console.error('Verification failed:', error)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<div>
|
|
88
|
+
<button onClick={handleVerify} disabled={isLoading}>
|
|
89
|
+
Verify My Phone
|
|
90
|
+
</button>
|
|
91
|
+
|
|
92
|
+
{/* Manual retry button (NEW) */}
|
|
93
|
+
{error && !isLoading && (
|
|
94
|
+
<button onClick={retryLastRequest}>
|
|
95
|
+
Retry
|
|
96
|
+
</button>
|
|
97
|
+
)}
|
|
98
|
+
|
|
99
|
+
{result && <p>Phone verified: {result.phone_number}</p>}
|
|
100
|
+
</div>
|
|
101
|
+
)
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## 🎨 Dual-Mode API: UI Mode vs Headless Mode
|
|
106
|
+
|
|
107
|
+
The SDK now supports two distinct modes for authentication, giving you complete control over the user experience:
|
|
108
|
+
|
|
109
|
+
### **UI Mode (Default)** - Ready-to-Use Components
|
|
110
|
+
The SDK provides built-in modals and buttons for authentication. Perfect for quick integration.
|
|
111
|
+
|
|
112
|
+
### **Headless Mode** - Complete Control
|
|
113
|
+
Returns raw data without any UI, allowing you to build completely custom experiences.
|
|
114
|
+
|
|
115
|
+
### When to Use Each Mode
|
|
116
|
+
|
|
117
|
+
| Use Case | Mode | Why |
|
|
118
|
+
|----------|------|-----|
|
|
119
|
+
| Quick integration | UI Mode | Get running in minutes with pre-built components |
|
|
120
|
+
| Custom design requirements | Headless | Match your exact brand guidelines |
|
|
121
|
+
| Mobile apps (React Native, etc) | Headless | Native UI components |
|
|
122
|
+
| Testing/Automation | Headless | Direct control over flow |
|
|
123
|
+
| Standard web apps | UI Mode | Optimized UX out of the box |
|
|
124
|
+
|
|
125
|
+
### UI Mode Example (Default)
|
|
126
|
+
|
|
127
|
+
```javascript
|
|
128
|
+
// Shows SDK's built-in UI based on authentication strategy
|
|
129
|
+
const prepareResult = await phoneAuth.preparePhoneRequest({
|
|
130
|
+
use_case: 'VerifyPhoneNumber',
|
|
131
|
+
phone_number: '+14155551234'
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// Behavior varies by strategy:
|
|
135
|
+
// - Desktop: Shows QR code modal
|
|
136
|
+
// - Link: Shows button modal to open app
|
|
137
|
+
// - TS43: Directly invokes Digital Credentials API (no modal - OS provides UI)
|
|
138
|
+
const credential = await phoneAuth.invokeSecurePrompt(prepareResult);
|
|
139
|
+
|
|
140
|
+
// Or customize the built-in UI
|
|
141
|
+
const credential = await phoneAuth.invokeSecurePrompt(prepareResult, {
|
|
142
|
+
modalOptions: {
|
|
143
|
+
title: 'Verify Your Identity',
|
|
144
|
+
description: 'Complete verification to continue',
|
|
145
|
+
buttonText: 'Verify with Verizon',
|
|
146
|
+
className: 'my-custom-modal'
|
|
147
|
+
},
|
|
148
|
+
callbacks: {
|
|
149
|
+
onOpen: () => console.log('Modal opened'),
|
|
150
|
+
onAuthComplete: (result) => console.log('Success!', result)
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Headless Mode Example
|
|
156
|
+
|
|
157
|
+
```javascript
|
|
158
|
+
// Get raw data without any UI
|
|
159
|
+
const prepareResult = await phoneAuth.preparePhoneRequest({
|
|
160
|
+
use_case: 'VerifyPhoneNumber',
|
|
161
|
+
phone_number: '+14155551234'
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// Returns data instead of showing UI
|
|
165
|
+
const result = await phoneAuth.invokeSecurePrompt(prepareResult, {
|
|
166
|
+
headless: true
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// Handle each strategy with your custom UI
|
|
170
|
+
if (result.strategy === 'link') {
|
|
171
|
+
// iOS/Android - App Clip or native app
|
|
172
|
+
console.log('URL to open:', result.url);
|
|
173
|
+
|
|
174
|
+
// Open in your custom way
|
|
175
|
+
window.open(result.url, '_blank');
|
|
176
|
+
|
|
177
|
+
// Wait for completion
|
|
178
|
+
const credential = await result.pollingPromise;
|
|
179
|
+
|
|
180
|
+
} else if (result.strategy === 'desktop') {
|
|
181
|
+
// Desktop - QR code
|
|
182
|
+
console.log('QR code image:', result.qrCode);
|
|
183
|
+
|
|
184
|
+
// Display QR in your custom UI
|
|
185
|
+
document.getElementById('qr').src = result.qrCode;
|
|
186
|
+
|
|
187
|
+
// Wait for mobile scan completion
|
|
188
|
+
const credential = await result.pollingPromise;
|
|
189
|
+
|
|
190
|
+
} else if (result.strategy === 'ts43') {
|
|
191
|
+
// Android/Chrome - Digital Credentials
|
|
192
|
+
console.log('Ready to invoke Digital Credentials');
|
|
193
|
+
|
|
194
|
+
// Trigger when user clicks your custom button
|
|
195
|
+
const credential = await result.trigger();
|
|
196
|
+
|
|
197
|
+
// Note: TS43 never shows a modal in UI mode - the OS provides its own UI
|
|
198
|
+
// The Digital Credentials API shows a native drawer/bottom sheet
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Smart Defaults & Type Guards (NEW in v4.4+)
|
|
203
|
+
|
|
204
|
+
The SDK now uses **smart defaults** that match the most common use case for each authentication strategy:
|
|
205
|
+
|
|
206
|
+
| Strategy | Default Mode | Returns | Why |
|
|
207
|
+
|----------|-------------|---------|-----|
|
|
208
|
+
| **Link** | Headless | `HeadlessResult` with `trigger()` | Developers want custom "Open App" buttons |
|
|
209
|
+
| **TS43** | Headless | `HeadlessResult` with `trigger()` | Browser provides native credential UI |
|
|
210
|
+
| **Desktop** | UI Mode | `Credential` directly | QR modal is complex to build from scratch |
|
|
211
|
+
|
|
212
|
+
#### Using Type Guards
|
|
213
|
+
|
|
214
|
+
Instead of manual type checking, use the SDK's built-in type guards for cleaner, type-safe code:
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
import {
|
|
218
|
+
invokeSecurePrompt,
|
|
219
|
+
isHeadlessResult,
|
|
220
|
+
isLinkStrategy,
|
|
221
|
+
isTS43Strategy,
|
|
222
|
+
isDesktopStrategy
|
|
223
|
+
} from 'glide-web-client-sdk';
|
|
224
|
+
|
|
225
|
+
// No need to specify headless for Link/TS43 - it's the default!
|
|
226
|
+
const result = await invokeSecurePrompt(prepareResult);
|
|
227
|
+
|
|
228
|
+
// Use type guards for type-safe handling
|
|
229
|
+
if (isHeadlessResult(result)) {
|
|
230
|
+
// TypeScript knows this is HeadlessResult
|
|
231
|
+
console.log('Strategy:', result.strategy);
|
|
232
|
+
|
|
233
|
+
if (isLinkStrategy(result)) {
|
|
234
|
+
// Handle Link: custom button to open app
|
|
235
|
+
myButton.onclick = () => result.trigger();
|
|
236
|
+
await result.pollingPromise;
|
|
237
|
+
}
|
|
238
|
+
else if (isTS43Strategy(result)) {
|
|
239
|
+
// Handle TS43: invoke immediately or on button
|
|
240
|
+
const credential = await result.trigger();
|
|
241
|
+
}
|
|
242
|
+
else if (isDesktopStrategy(result)) {
|
|
243
|
+
// Desktop in headless mode (explicitly requested)
|
|
244
|
+
showCustomQR(result.qrCodeUrl);
|
|
245
|
+
await result.pollingPromise;
|
|
246
|
+
}
|
|
247
|
+
} else {
|
|
248
|
+
// Got credential directly (Desktop UI mode by default)
|
|
249
|
+
const processedResult = await verifyPhoneNumberCredential(result, session);
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
#### Available Type Guards
|
|
254
|
+
|
|
255
|
+
```typescript
|
|
256
|
+
// Check if result is headless or UI mode
|
|
257
|
+
isHeadlessResult(result) // true if headless mode
|
|
258
|
+
isCredential(result) // true if UI mode (got credential)
|
|
259
|
+
|
|
260
|
+
// Strategy-specific guards (for HeadlessResult)
|
|
261
|
+
isLinkStrategy(result) // true if Link strategy
|
|
262
|
+
isTS43Strategy(result) // true if TS43 strategy
|
|
263
|
+
isDesktopStrategy(result) // true if Desktop strategy
|
|
264
|
+
|
|
265
|
+
// Helper functions
|
|
266
|
+
getStrategy(result) // Returns: 'link' | 'ts43' | 'desktop' | undefined
|
|
267
|
+
requiresPolling(result) // Returns: true for Link/Desktop, false for TS43
|
|
268
|
+
requiresUserAction(result)// Returns: true if user interaction needed
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
#### Override Defaults When Needed
|
|
272
|
+
|
|
273
|
+
You can still override the defaults when you have specific requirements:
|
|
274
|
+
|
|
275
|
+
```javascript
|
|
276
|
+
// Force UI mode for Link/TS43 (rarely needed)
|
|
277
|
+
const credential = await invokeSecurePrompt(prepareResult, {
|
|
278
|
+
headless: false // Show SDK modal instead of using custom UI
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
// Force headless mode for Desktop (for custom QR display)
|
|
282
|
+
const result = await invokeSecurePrompt(prepareResult, {
|
|
283
|
+
headless: true // Get QR data instead of showing modal
|
|
284
|
+
});
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### React Example with Custom UI
|
|
288
|
+
|
|
289
|
+
```jsx
|
|
290
|
+
function CustomPhoneAuth() {
|
|
291
|
+
const [qrCode, setQrCode] = useState(null);
|
|
292
|
+
const [appUrl, setAppUrl] = useState(null);
|
|
293
|
+
|
|
294
|
+
const handleVerify = async () => {
|
|
295
|
+
const prepareResult = await phoneAuth.preparePhoneRequest({
|
|
296
|
+
use_case: 'VerifyPhoneNumber',
|
|
297
|
+
phone_number: phoneNumber
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
// No need for headless: true for Link/TS43 - it's the default!
|
|
301
|
+
const result = await phoneAuth.invokeSecurePrompt(prepareResult);
|
|
302
|
+
|
|
303
|
+
// Use type guards for clean code
|
|
304
|
+
if (isHeadlessResult(result)) {
|
|
305
|
+
if (isDesktopStrategy(result)) {
|
|
306
|
+
// Desktop in headless (explicitly requested)
|
|
307
|
+
setQrCode(result.qrCode);
|
|
308
|
+
} else if (isLinkStrategy(result)) {
|
|
309
|
+
// Link defaults to headless
|
|
310
|
+
setAppUrl(result.url);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Handle completion
|
|
314
|
+
const credential = await result.pollingPromise;
|
|
315
|
+
console.log('Verified!');
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
return (
|
|
319
|
+
<div className="my-custom-design">
|
|
320
|
+
{qrCode && (
|
|
321
|
+
<div className="qr-container">
|
|
322
|
+
<img src={qrCode} alt="Scan to verify" />
|
|
323
|
+
<p>Scan with your phone</p>
|
|
324
|
+
</div>
|
|
325
|
+
)}
|
|
326
|
+
|
|
327
|
+
{appUrl && (
|
|
328
|
+
<button onClick={() => window.open(appUrl)}>
|
|
329
|
+
Open Verification App
|
|
330
|
+
</button>
|
|
331
|
+
)}
|
|
332
|
+
</div>
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
### Vue Example with Both Modes
|
|
338
|
+
|
|
339
|
+
```vue
|
|
340
|
+
<template>
|
|
341
|
+
<div>
|
|
342
|
+
<!-- Use SDK UI for quick setup -->
|
|
343
|
+
<button @click="verifyWithUI">
|
|
344
|
+
Verify (SDK UI)
|
|
345
|
+
</button>
|
|
346
|
+
|
|
347
|
+
<!-- Or build custom UI -->
|
|
348
|
+
<button @click="verifyHeadless">
|
|
349
|
+
Verify (Custom UI)
|
|
350
|
+
</button>
|
|
351
|
+
|
|
352
|
+
<!-- Custom QR display -->
|
|
353
|
+
<div v-if="qrCode" class="custom-qr">
|
|
354
|
+
<img :src="qrCode" />
|
|
355
|
+
</div>
|
|
356
|
+
</div>
|
|
357
|
+
</template>
|
|
358
|
+
|
|
359
|
+
<script setup>
|
|
360
|
+
import { usePhoneAuth } from 'glide-web-client-sdk/vue'
|
|
361
|
+
|
|
362
|
+
const phoneAuth = usePhoneAuth({
|
|
363
|
+
endpoints: {
|
|
364
|
+
prepare: '/api/phone-auth/prepare',
|
|
365
|
+
process: '/api/phone-auth/process'
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
const qrCode = ref(null);
|
|
370
|
+
|
|
371
|
+
// Using built-in UI
|
|
372
|
+
async function verifyWithUI() {
|
|
373
|
+
const prepared = await phoneAuth.preparePhoneRequest({
|
|
374
|
+
use_case: 'VerifyPhoneNumber',
|
|
375
|
+
phone_number: '+14155551234'
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
// Shows SDK modal
|
|
379
|
+
const credential = await phoneAuth.invokeSecurePrompt(prepared);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// Using headless mode
|
|
383
|
+
async function verifyHeadless() {
|
|
384
|
+
const prepared = await phoneAuth.preparePhoneRequest({
|
|
385
|
+
use_case: 'VerifyPhoneNumber',
|
|
386
|
+
phone_number: '+14155551234'
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
// Get raw data
|
|
390
|
+
const result = await phoneAuth.invokeSecurePrompt(prepared, {
|
|
391
|
+
headless: true
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
if (result.strategy === 'desktop') {
|
|
395
|
+
qrCode.value = result.qrCode;
|
|
396
|
+
await result.pollingPromise;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
</script>
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
### TypeScript Types
|
|
403
|
+
|
|
404
|
+
```typescript
|
|
405
|
+
interface InvokeOptions {
|
|
406
|
+
// Enable headless mode (no UI)
|
|
407
|
+
headless?: boolean;
|
|
408
|
+
|
|
409
|
+
// Customize built-in UI (only for UI mode)
|
|
410
|
+
modalOptions?: {
|
|
411
|
+
className?: string;
|
|
412
|
+
title?: string;
|
|
413
|
+
description?: string;
|
|
414
|
+
buttonText?: string;
|
|
415
|
+
showCloseButton?: boolean;
|
|
416
|
+
zIndex?: number;
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
// UI event callbacks (only for UI mode)
|
|
420
|
+
callbacks?: {
|
|
421
|
+
onOpen?: () => void;
|
|
422
|
+
onClose?: () => void;
|
|
423
|
+
onAuthStart?: () => void;
|
|
424
|
+
onAuthComplete?: (result: any) => void;
|
|
425
|
+
onError?: (error: Error) => void;
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
interface HeadlessResult {
|
|
430
|
+
strategy: 'ts43' | 'link' | 'desktop';
|
|
431
|
+
url?: string; // For Link strategy
|
|
432
|
+
qrCode?: string; // For Desktop strategy
|
|
433
|
+
pollingPromise?: Promise<any>; // Resolves when complete
|
|
434
|
+
trigger?: () => Promise<any>; // For TS43 strategy
|
|
435
|
+
session: SessionInfo;
|
|
436
|
+
}
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
## Smart Defaults & Type Guards
|
|
440
|
+
|
|
441
|
+
### Smart Mode Selection
|
|
442
|
+
|
|
443
|
+
The SDK automatically chooses the optimal mode based on the authentication strategy:
|
|
444
|
+
|
|
445
|
+
```javascript
|
|
446
|
+
// Link Strategy (App Clips) - Defaults to HEADLESS
|
|
447
|
+
const result = await phoneAuth.invokeSecurePrompt(prepareResponse);
|
|
448
|
+
// No need to specify headless: true - it's automatic!
|
|
449
|
+
|
|
450
|
+
// TS43 Strategy (Android) - Defaults to HEADLESS
|
|
451
|
+
const result = await phoneAuth.invokeSecurePrompt(prepareResponse);
|
|
452
|
+
// Automatically headless for custom UI control
|
|
453
|
+
|
|
454
|
+
// Desktop Strategy (QR Code) - Defaults to UI MODE
|
|
455
|
+
const result = await phoneAuth.invokeSecurePrompt(prepareResponse);
|
|
456
|
+
// Shows built-in QR modal automatically
|
|
457
|
+
|
|
458
|
+
// Override defaults if needed
|
|
459
|
+
const result = await phoneAuth.invokeSecurePrompt(prepareResponse, {
|
|
460
|
+
headless: false // Force UI mode for any strategy
|
|
461
|
+
});
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
### Type Guards for Safe Result Handling
|
|
465
|
+
|
|
466
|
+
Use built-in type guards for type-safe result handling:
|
|
467
|
+
|
|
468
|
+
```typescript
|
|
469
|
+
import {
|
|
470
|
+
isHeadlessResult,
|
|
471
|
+
isCredential,
|
|
472
|
+
isLinkStrategy,
|
|
473
|
+
isTS43Strategy,
|
|
474
|
+
isDesktopStrategy,
|
|
475
|
+
requiresPolling,
|
|
476
|
+
requiresUserAction
|
|
477
|
+
} from 'glide-web-client-sdk';
|
|
478
|
+
|
|
479
|
+
const result = await phoneAuth.invokeSecurePrompt(prepareResponse);
|
|
480
|
+
|
|
481
|
+
// Check result type
|
|
482
|
+
if (isHeadlessResult(result)) {
|
|
483
|
+
// TypeScript knows this is HeadlessResult
|
|
484
|
+
console.log('Strategy:', result.strategy);
|
|
485
|
+
|
|
486
|
+
// Check specific strategies
|
|
487
|
+
if (isLinkStrategy(result)) {
|
|
488
|
+
// Handle Link (App Clip) flow
|
|
489
|
+
await result.trigger(); // Opens App Clip
|
|
490
|
+
const credential = await result.pollingPromise;
|
|
491
|
+
} else if (isTS43Strategy(result)) {
|
|
492
|
+
// Handle TS43 (Android) flow
|
|
493
|
+
const credential = await result.trigger(); // Invokes credential API
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// Check capabilities
|
|
497
|
+
if (requiresPolling(result)) {
|
|
498
|
+
// Link or Desktop - needs polling
|
|
499
|
+
const credential = await result.pollingPromise;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
if (requiresUserAction(result)) {
|
|
503
|
+
// Link or TS43 - needs user interaction
|
|
504
|
+
button.onclick = () => result.trigger();
|
|
505
|
+
}
|
|
506
|
+
} else if (isCredential(result)) {
|
|
507
|
+
// Direct credential (UI mode handled everything)
|
|
508
|
+
const verified = await phoneAuth.processCredential(result);
|
|
509
|
+
}
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
### Unified Trigger Pattern
|
|
513
|
+
|
|
514
|
+
All headless strategies use the same `trigger()` pattern:
|
|
515
|
+
|
|
516
|
+
```javascript
|
|
517
|
+
// Link Strategy - trigger opens App Clip
|
|
518
|
+
const linkResult = await phoneAuth.invokeSecurePrompt(prepareResponse, {
|
|
519
|
+
autoTrigger: false // Don't auto-open, let user click
|
|
520
|
+
});
|
|
521
|
+
button.onclick = () => linkResult.trigger(); // User controls when to open
|
|
522
|
+
|
|
523
|
+
// TS43 Strategy - trigger invokes credential API
|
|
524
|
+
const ts43Result = await phoneAuth.invokeSecurePrompt(prepareResponse, {
|
|
525
|
+
autoTrigger: false // Preserve user gesture
|
|
526
|
+
});
|
|
527
|
+
button.onclick = async () => {
|
|
528
|
+
const credential = await ts43Result.trigger(); // Must be in click handler
|
|
529
|
+
};
|
|
530
|
+
|
|
531
|
+
// Auto-trigger (default for Link)
|
|
532
|
+
const autoResult = await phoneAuth.invokeSecurePrompt(prepareResponse);
|
|
533
|
+
// App Clip opens immediately, retry available
|
|
534
|
+
retryButton.onclick = () => autoResult.trigger(); // Retry if needed
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
## Cross-Device Support
|
|
538
|
+
|
|
539
|
+
The SDK automatically handles cross-device authentication flows (QR code scenarios) without any configuration needed.
|
|
540
|
+
|
|
541
|
+
### How It Works
|
|
542
|
+
|
|
543
|
+
1. **Detection**: After 3 seconds with credential prompt open, the SDK detects likely QR code flow
|
|
544
|
+
2. **Timeout Extension**: Automatically extends timeout from 30 seconds to 2 minutes
|
|
545
|
+
3. **User Experience**: Users have ample time to scan QR and complete on their phone
|
|
546
|
+
|
|
547
|
+
### Monitoring Cross-Device Flows
|
|
548
|
+
|
|
549
|
+
```javascript
|
|
550
|
+
const { getPhoneNumber } = usePhoneAuth({
|
|
551
|
+
endpoints: { /* ... */ },
|
|
552
|
+
|
|
553
|
+
// Optional: Track when QR codes are shown
|
|
554
|
+
onCrossDeviceDetected: () => {
|
|
555
|
+
// Analytics: Track QR code usage
|
|
556
|
+
analytics.track('phone_auth_qr_code_shown');
|
|
557
|
+
}
|
|
558
|
+
});
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
## Silent Retry Pattern
|
|
562
|
+
|
|
563
|
+
The SDK automatically retries transient failures without showing errors to users.
|
|
564
|
+
|
|
565
|
+
### Default Behavior
|
|
566
|
+
|
|
567
|
+
- **2 automatic retries** on network/timeout errors
|
|
568
|
+
- **Exponential backoff** between attempts (1s, 2s)
|
|
569
|
+
- **Error only shown** after all retries are exhausted
|
|
570
|
+
- **Session caching** to recover from interruptions
|
|
571
|
+
|
|
572
|
+
### Example Flow
|
|
573
|
+
|
|
574
|
+
```
|
|
575
|
+
User clicks verify →
|
|
576
|
+
Try 1 fails (network glitch) →
|
|
577
|
+
[Silent retry - no error shown] →
|
|
578
|
+
Try 2 succeeds →
|
|
579
|
+
✓ Success shown to user
|
|
580
|
+
|
|
581
|
+
User never sees the transient failure!
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
### Monitoring Retries
|
|
585
|
+
|
|
586
|
+
```javascript
|
|
587
|
+
const { verifyPhoneNumber } = usePhoneAuth({
|
|
588
|
+
endpoints: { /* ... */ },
|
|
589
|
+
|
|
590
|
+
// Optional: Track retry patterns
|
|
591
|
+
onRetryAttempt: (attempt, maxAttempts) => {
|
|
592
|
+
// Log to monitoring service
|
|
593
|
+
logger.info('Phone auth retry', {
|
|
594
|
+
attempt,
|
|
595
|
+
maxAttempts
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
});
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
## Advanced Features
|
|
602
|
+
|
|
603
|
+
### Manual Retry
|
|
604
|
+
|
|
605
|
+
Users can manually retry failed verifications:
|
|
606
|
+
|
|
607
|
+
```javascript
|
|
608
|
+
const { retryLastRequest, error } = usePhoneAuth(config);
|
|
609
|
+
|
|
610
|
+
// Show retry button after error
|
|
611
|
+
if (error) {
|
|
612
|
+
<button onClick={retryLastRequest}>Try Again</button>
|
|
613
|
+
}
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
### Callbacks
|
|
617
|
+
|
|
618
|
+
Both callbacks are optional and have no performance impact when not used:
|
|
619
|
+
|
|
620
|
+
| Callback | Purpose | Use Case |
|
|
621
|
+
|----------|---------|----------|
|
|
622
|
+
| `onCrossDeviceDetected` | Fires when QR code flow detected | Analytics, custom UI |
|
|
623
|
+
| `onRetryAttempt` | Fires on each retry attempt | Monitoring, debugging |
|
|
624
|
+
|
|
625
|
+
### TypeScript Support
|
|
626
|
+
|
|
627
|
+
Full TypeScript support with exported types:
|
|
628
|
+
|
|
629
|
+
```typescript
|
|
630
|
+
import {
|
|
631
|
+
usePhoneAuth,
|
|
632
|
+
PhoneAuthResult,
|
|
633
|
+
AuthError,
|
|
634
|
+
UsePhoneAuthOptions
|
|
635
|
+
} from 'glide-web-client-sdk/react';
|
|
636
|
+
|
|
637
|
+
const config: UsePhoneAuthOptions = {
|
|
638
|
+
endpoints: {
|
|
639
|
+
prepare: '/api/prepare',
|
|
640
|
+
process: '/api/process'
|
|
641
|
+
},
|
|
642
|
+
onCrossDeviceDetected: () => void,
|
|
643
|
+
onRetryAttempt: (attempt: number, max: number) => void
|
|
644
|
+
};
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
## Error Handling
|
|
648
|
+
|
|
649
|
+
### Enhanced Error Context
|
|
650
|
+
|
|
651
|
+
Errors now include additional context for debugging:
|
|
652
|
+
|
|
653
|
+
```javascript
|
|
654
|
+
catch (error) {
|
|
655
|
+
console.log(error.code); // 'REQUEST_TIMEOUT'
|
|
656
|
+
console.log(error.message); // User-friendly message
|
|
657
|
+
console.log(error.context); // { attemptNumber: 3, maxAttempts: 3 }
|
|
658
|
+
console.log(error.browserError); // Browser-specific error details
|
|
659
|
+
}
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
### Cross-Device Error Types
|
|
663
|
+
|
|
664
|
+
The SDK provides specific error information for cross-device issues:
|
|
665
|
+
|
|
666
|
+
```javascript
|
|
667
|
+
if (error.details?.errorType === 'CROSS_DEVICE_TIMEOUT') {
|
|
668
|
+
// QR code expired
|
|
669
|
+
}
|
|
670
|
+
if (error.details?.errorType === 'CROSS_DEVICE_CONNECTION_LOST') {
|
|
671
|
+
// Connection lost during phone verification
|
|
672
|
+
}
|
|
673
|
+
```
|
|
674
|
+
|
|
675
|
+
## Browser Support
|
|
676
|
+
|
|
677
|
+
### Requirements
|
|
678
|
+
|
|
679
|
+
- Chrome 118+ or Edge 118+
|
|
680
|
+
- Digital Credentials API flag enabled
|
|
681
|
+
|
|
682
|
+
### Checking Support
|
|
683
|
+
|
|
684
|
+
```javascript
|
|
685
|
+
const { isSupported, browserSupportInfo } = usePhoneAuth();
|
|
686
|
+
|
|
687
|
+
if (!isSupported) {
|
|
688
|
+
console.log(browserSupportInfo.message);
|
|
689
|
+
// "Please enable chrome://flags/#web-identity-digital-credentials"
|
|
690
|
+
}
|
|
691
|
+
```
|
|
692
|
+
|
|
693
|
+
## Vue 3 Example
|
|
694
|
+
|
|
695
|
+
```vue
|
|
696
|
+
<template>
|
|
697
|
+
<div>
|
|
698
|
+
<button @click="verify" :disabled="isLoading">
|
|
699
|
+
Verify Phone
|
|
700
|
+
</button>
|
|
701
|
+
|
|
702
|
+
<!-- Retry button -->
|
|
703
|
+
<button v-if="error" @click="retryLastRequest">
|
|
704
|
+
Retry
|
|
705
|
+
</button>
|
|
706
|
+
|
|
707
|
+
<div v-if="result">
|
|
708
|
+
Phone: {{ result.phone_number }}
|
|
709
|
+
</div>
|
|
710
|
+
</div>
|
|
711
|
+
</template>
|
|
712
|
+
|
|
713
|
+
<script setup>
|
|
714
|
+
import { usePhoneAuth } from 'glide-web-client-sdk/vue'
|
|
715
|
+
|
|
716
|
+
const {
|
|
717
|
+
verifyPhoneNumber,
|
|
718
|
+
retryLastRequest,
|
|
719
|
+
isLoading,
|
|
720
|
+
error,
|
|
721
|
+
result
|
|
722
|
+
} = usePhoneAuth({
|
|
723
|
+
endpoints: {
|
|
724
|
+
prepare: '/api/phone-auth/prepare',
|
|
725
|
+
process: '/api/phone-auth/process'
|
|
726
|
+
},
|
|
727
|
+
onCrossDeviceDetected: () => {
|
|
728
|
+
console.log('QR code shown');
|
|
729
|
+
}
|
|
730
|
+
})
|
|
731
|
+
|
|
732
|
+
const verify = async () => {
|
|
733
|
+
await verifyPhoneNumber('+1234567890')
|
|
734
|
+
}
|
|
735
|
+
</script>
|
|
736
|
+
```
|
|
737
|
+
|
|
738
|
+
## Angular Example
|
|
739
|
+
|
|
740
|
+
```typescript
|
|
741
|
+
import { Component } from '@angular/core';
|
|
742
|
+
import { PhoneAuthService } from 'glide-web-client-sdk/angular';
|
|
743
|
+
|
|
744
|
+
@Component({
|
|
745
|
+
selector: 'app-phone-verify',
|
|
746
|
+
template: `
|
|
747
|
+
<button (click)="verify()" [disabled]="isLoading$ | async">
|
|
748
|
+
Verify
|
|
749
|
+
</button>
|
|
750
|
+
|
|
751
|
+
<button *ngIf="error$ | async" (click)="retry()">
|
|
752
|
+
Retry
|
|
753
|
+
</button>
|
|
754
|
+
|
|
755
|
+
<div *ngIf="result$ | async as result">
|
|
756
|
+
Phone: {{ result.phone_number }}
|
|
757
|
+
</div>
|
|
758
|
+
`
|
|
759
|
+
})
|
|
760
|
+
export class PhoneVerifyComponent {
|
|
761
|
+
isLoading$ = this.phoneAuth.isLoading$;
|
|
762
|
+
error$ = this.phoneAuth.error$;
|
|
763
|
+
result$ = this.phoneAuth.result$;
|
|
764
|
+
|
|
765
|
+
constructor(private phoneAuth: PhoneAuthService) {
|
|
766
|
+
this.phoneAuth.configure({
|
|
767
|
+
endpoints: {
|
|
768
|
+
prepare: '/api/phone-auth/prepare',
|
|
769
|
+
process: '/api/phone-auth/process'
|
|
770
|
+
},
|
|
771
|
+
onCrossDeviceDetected: () => {
|
|
772
|
+
console.log('QR code detected');
|
|
773
|
+
}
|
|
774
|
+
});
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
async verify() {
|
|
778
|
+
await this.phoneAuth.verifyPhoneNumber('+1234567890');
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
async retry() {
|
|
782
|
+
await this.phoneAuth.retryLastRequest();
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
```
|
|
786
|
+
|
|
787
|
+
## Vanilla JavaScript
|
|
788
|
+
|
|
789
|
+
```javascript
|
|
790
|
+
import { PhoneAuthClient } from 'glide-web-client-sdk';
|
|
791
|
+
|
|
792
|
+
const client = new PhoneAuthClient({
|
|
793
|
+
endpoints: {
|
|
794
|
+
prepare: '/api/phone-auth/prepare',
|
|
795
|
+
process: '/api/phone-auth/process'
|
|
796
|
+
},
|
|
797
|
+
onCrossDeviceDetected: () => {
|
|
798
|
+
console.log('QR code detected');
|
|
799
|
+
},
|
|
800
|
+
onRetryAttempt: (attempt, max) => {
|
|
801
|
+
console.log(`Retry ${attempt}/${max}`);
|
|
802
|
+
}
|
|
803
|
+
});
|
|
804
|
+
|
|
805
|
+
// Verify phone number with automatic retry
|
|
806
|
+
try {
|
|
807
|
+
const result = await client.verifyPhoneNumber('+1234567890');
|
|
808
|
+
console.log('Verified:', result.verified);
|
|
809
|
+
} catch (error) {
|
|
810
|
+
// Only thrown after all retries exhausted
|
|
811
|
+
console.error('Failed after retries:', error);
|
|
812
|
+
}
|
|
813
|
+
```
|
|
814
|
+
|
|
815
|
+
## Migration from v3
|
|
816
|
+
|
|
817
|
+
No breaking changes! The SDK is fully backward compatible. New features are additive:
|
|
818
|
+
|
|
819
|
+
```javascript
|
|
820
|
+
// v3 code continues to work
|
|
821
|
+
const { getPhoneNumber } = usePhoneAuth(config);
|
|
822
|
+
|
|
823
|
+
// v4 adds new optional features
|
|
824
|
+
const { getPhoneNumber, retryLastRequest } = usePhoneAuth({
|
|
825
|
+
...config,
|
|
826
|
+
onCrossDeviceDetected: () => {}, // Optional
|
|
827
|
+
onRetryAttempt: () => {} // Optional
|
|
828
|
+
});
|
|
829
|
+
```
|
|
830
|
+
|
|
831
|
+
## Debug Tools
|
|
832
|
+
|
|
833
|
+
For development and testing, debug components are available in the `/examples/debug-components/` folder:
|
|
834
|
+
|
|
835
|
+
- `PhoneAuthDebugger.jsx` - Comprehensive debugging interface
|
|
836
|
+
- `AppEnhanced.jsx` - Production app with debug mode
|
|
837
|
+
|
|
838
|
+
These are not included in the main bundle. Copy them to your project if needed.
|
|
839
|
+
|
|
840
|
+
## API Reference
|
|
841
|
+
|
|
842
|
+
### Configuration Options
|
|
843
|
+
|
|
844
|
+
```typescript
|
|
845
|
+
interface AuthConfig {
|
|
846
|
+
// Required endpoints
|
|
847
|
+
endpoints: {
|
|
848
|
+
prepare: string; // Your backend prepare endpoint
|
|
849
|
+
process: string; // Your backend process endpoint
|
|
850
|
+
polling?: string; // Optional custom polling endpoint
|
|
851
|
+
};
|
|
852
|
+
|
|
853
|
+
// Optional settings
|
|
854
|
+
pollingInterval?: number; // Polling interval in ms (default: 2000)
|
|
855
|
+
maxPollingAttempts?: number; // Max polling attempts (default: 150)
|
|
856
|
+
timeout?: number; // API timeout in ms (default: 30000)
|
|
857
|
+
debug?: boolean; // Enable debug logging (default: false)
|
|
858
|
+
|
|
859
|
+
// Optional callbacks
|
|
860
|
+
onCrossDeviceDetected?: () => void; // QR code shown
|
|
861
|
+
onRetryAttempt?: (attempt: number, maxAttempts: number) => void;
|
|
862
|
+
onTimeout?: () => void;
|
|
863
|
+
onCancel?: () => void;
|
|
864
|
+
}
|
|
865
|
+
```
|
|
866
|
+
|
|
867
|
+
### Modal Customization (UI Mode)
|
|
868
|
+
|
|
869
|
+
```typescript
|
|
870
|
+
interface ModalOptions {
|
|
871
|
+
className?: string; // Custom CSS class
|
|
872
|
+
title?: string; // Modal title
|
|
873
|
+
description?: string; // Modal description
|
|
874
|
+
buttonText?: string; // Button label
|
|
875
|
+
showCloseButton?: boolean; // Show close button (default: true)
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
// Usage
|
|
879
|
+
const result = await phoneAuth.invokeSecurePrompt(prepareResponse, {
|
|
880
|
+
headless: false,
|
|
881
|
+
modalOptions: {
|
|
882
|
+
title: 'Verify Your Identity',
|
|
883
|
+
buttonText: 'Continue'
|
|
884
|
+
}
|
|
885
|
+
});
|
|
886
|
+
```
|
|
887
|
+
|
|
888
|
+
### Methods
|
|
889
|
+
|
|
890
|
+
```typescript
|
|
891
|
+
// High-level methods (handle everything)
|
|
892
|
+
getPhoneNumber(): Promise<GetPhoneNumberResponse>
|
|
893
|
+
verifyPhoneNumber(phoneNumber?: string): Promise<VerifyPhoneNumberResponse>
|
|
894
|
+
|
|
895
|
+
// Low-level methods (granular control)
|
|
896
|
+
preparePhoneRequest(options: PrepareRequest): Promise<PrepareResponse>
|
|
897
|
+
invokeSecurePrompt(response: PrepareResponse, options?: InvokeOptions): Promise<HeadlessResult | Credential>
|
|
898
|
+
getPhoneNumberCredential(credential: any, session: SessionInfo): Promise<GetPhoneNumberResponse>
|
|
899
|
+
verifyPhoneNumberCredential(credential: any, session: SessionInfo): Promise<VerifyPhoneNumberResponse>
|
|
900
|
+
|
|
901
|
+
// Utility methods
|
|
902
|
+
retryLastRequest(): Promise<any>
|
|
903
|
+
cancelCurrentRequest(): void
|
|
904
|
+
```
|
|
905
|
+
|
|
906
|
+
### Response Types
|
|
907
|
+
|
|
908
|
+
```typescript
|
|
909
|
+
interface GetPhoneNumberResponse {
|
|
910
|
+
phone_number: string;
|
|
911
|
+
aud?: string;
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
interface VerifyPhoneNumberResponse {
|
|
915
|
+
phone_number: string;
|
|
916
|
+
verified: boolean;
|
|
917
|
+
aud?: string;
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
interface HeadlessResult {
|
|
921
|
+
strategy: 'link' | 'ts43' | 'desktop';
|
|
922
|
+
trigger?: () => void | Promise<any>;
|
|
923
|
+
pollingPromise?: Promise<any>;
|
|
924
|
+
session: SessionInfo;
|
|
925
|
+
url?: string; // For Link strategy
|
|
926
|
+
qrCode?: string; // For Desktop strategy
|
|
927
|
+
}
|
|
928
|
+
```
|
|
929
|
+
|
|
930
|
+
## Support
|
|
931
|
+
|
|
932
|
+
- [Documentation](https://docs.glideidentity.com)
|
|
933
|
+
- [API Reference](https://api.glideidentity.com/docs)
|
|
934
|
+
- [Support](https://support.glideidentity.com)
|
|
935
|
+
|
|
936
|
+
## License
|
|
937
|
+
|
|
938
|
+
MIT
|