npm package: @glideidentity/web-client-sdk
Description: Glide Phone Authentication SDK for web applications
Latest version: 6.0.2

# Glide Web Client SDK

The official web SDK for integrating Glide's carrier-grade phone verification into your web applications.

## Features

- 🚀 **Instant Verification** - Direct carrier verification without SMS delays
- 🔐 **Fraud Resistant** - Can't be intercepted or spoofed like SMS codes
- 📱 **Multi-Platform** - Desktop QR codes, iOS App Clips, Android deep links
- 🎨 **Customizable UI** - Built-in modal with themes and view modes
- 🔧 **Flexible Architecture** - Use the full SDK or just the core types
- 🌐 **Framework Support** - React, Vue, Angular, and vanilla JavaScript
- 📦 **Tree-Shakeable** - Import only what you need

## Installation

```bash
npm install @glideidentity/web-client-sdk
```

## Quick Start

### High-Level API (Recommended)

The simplest way to integrate phone verification:

```javascript
import { PhoneAuthClient, USE_CASE } from '@glideidentity/web-client-sdk';

const client = new PhoneAuthClient({
  endpoints: {
    prepare: '/api/phone-auth/prepare',
    process: '/api/phone-auth/process',
  }
});

// Get a phone number
const result = await client.authenticate({
  use_case: USE_CASE.GET_PHONE_NUMBER
});
console.log('Phone:', result.phone_number);

// Or verify a specific number
const verified = await client.authenticate({
  use_case: USE_CASE.VERIFY_PHONE_NUMBER,
  phone_number: '+14155551234'
});
console.log('Verified:', verified.verified);
```

### Granular API

For more control over the authentication flow:

```javascript
import { PhoneAuthClient, USE_CASE } from '@glideidentity/web-client-sdk';

const client = new PhoneAuthClient({
  endpoints: {
    prepare: '/api/phone-auth/prepare',
    process: '/api/phone-auth/process',
  }
});

// Step 1: Prepare the request
const prepared = await client.prepare({
  use_case: USE_CASE.VERIFY_PHONE_NUMBER,
  phone_number: '+14155551234'
});

// Step 2: Invoke authentication (shows UI based on strategy)
const invokeResult = await client.invokeSecurePrompt(prepared);
const credential = await invokeResult.credential;

// Step 3: Process the credential
const result = await client.verifyPhoneNumber(credential, invokeResult.session);

console.log('Verified:', result.verified);
```

## Configuration

### Client Options

```typescript
const client = new PhoneAuthClient({
  // Required: Your backend endpoints
  endpoints: {
    prepare: '/api/phone-auth/prepare',   // Prepare request endpoint
    process: '/api/phone-auth/process',   // Process credential endpoint
    polling: '/api/phone-auth/status',    // Optional: Status polling endpoint
  },
  
  // Polling configuration
  pollingInterval: 2000,      // Polling interval in ms (default: 2000)
  maxPollingAttempts: 30,     // Max attempts before timeout (default: 30)
  timeout: 30000,             // API request timeout in ms (default: 30000)
  
  // Debug options
  debug: false,               // Enable console logging (default: false)
  devtools: {
    showMobileConsole: false  // Show on-screen console on mobile (default: false)
  }
});
```

### Invoke Options

Customize the authentication UI and behavior:

```typescript
const result = await client.invokeSecurePrompt(prepared, {
  // Prevent SDK from showing any UI (use your own)
  preventDefaultUI: false,
  
  // Polling configuration (overrides client config)
  pollingInterval: 2000,
  maxPollingAttempts: 30,
  
  // Modal customization (for desktop QR strategy)
  modalOptions: {
    theme: 'auto',              // 'dark' | 'light' | 'auto'
    viewMode: 'toggle',         // 'toggle' | 'dual' | 'pre-step'
    title: 'Scan to Verify',    // Custom title text
    description: '',            // Optional subtitle
    showCloseButton: true,      // Show X button
    closeOnBackdropClick: true, // Close on outside click
    closeOnEscape: true,        // Close on Escape key
  }
});
```

## Modal Customization

The SDK provides a built-in modal for desktop QR code display.

### View Modes

| Mode | Description | Best For |
|------|-------------|----------|
| `toggle` | Single QR with iOS/Android switch | Clean, minimal UI (default) |
| `dual` | Side-by-side QR codes | Users who may not know their OS |
| `pre-step` | OS selection screen first | Guided experience |

### Themes

| Theme | Description |
|-------|-------------|
| `auto` | Follows system preference (default) |
| `dark` | Dark background with light text |
| `light` | Light background with dark text |

### Custom UI

To use your own UI instead of the built-in modal:

```javascript
const result = await client.invokeSecurePrompt(prepared, {
  preventDefaultUI: true
});

// For desktop strategy, display your own QR code
if (result.strategy === 'desktop') {
  // Access QR data from the prepare response
  const qrData = prepared.data;
  displayYourQRCode(qrData.ios_qr_image, qrData.android_qr_image);
  
  // Wait for credential
  const credential = await result.credential;
  
  // Or cancel if needed
  result.cancel?.();
}
```

## Authentication Strategies

The SDK automatically selects the best strategy based on the device:

| Strategy | Platform | Description |
|----------|----------|-------------|
| `ts43` | Android Chrome 128+ | Native Digital Credentials API |
| `link` | iOS / Android | App Clip (iOS) or carrier app (Android) |
| `desktop` | Desktop browsers | QR code scanned by mobile |

### Strategy-Specific Handling

```javascript
const result = await client.invokeSecurePrompt(prepared);

switch (result.strategy) {
  case 'ts43':
    // Android: Credential returned directly
    const credential = await result.credential;
    break;
    
  case 'link':
    // iOS: Opens App Clip, polls for completion
    // Android: Opens carrier's privileged app, polls for completion
    const credential = await result.credential;
    // Optionally cancel polling
    result.cancel?.();
    break;
    
  case 'desktop':
    // Desktop: Shows QR modal, polls for completion
    const credential = await result.credential;
    result.cancel?.();
    break;
}
```

## Framework Integration

### React

```jsx
import { usePhoneAuth, USE_CASE } from '@glideidentity/web-client-sdk/react';

function PhoneVerification() {
  const { authenticate, isLoading, error, result } = usePhoneAuth({
    endpoints: {
      prepare: '/api/phone-auth/prepare',
      process: '/api/phone-auth/process',
    }
  });

  const handleVerify = async () => {
    try {
      const result = await authenticate({
        use_case: USE_CASE.VERIFY_PHONE_NUMBER,
        phone_number: '+14155551234'
      });
      console.log('Verified:', result.verified);
    } catch (err) {
      console.error('Failed:', err);
    }
  };

  return (
    <button onClick={handleVerify} disabled={isLoading}>
      {isLoading ? 'Verifying...' : 'Verify Phone'}
    </button>
  );
}
```

### Vue

```vue
<script setup>
import { usePhoneAuth, USE_CASE } from '@glideidentity/web-client-sdk/vue';

const { authenticate, isLoading, error, result } = usePhoneAuth({
  endpoints: {
    prepare: '/api/phone-auth/prepare',
    process: '/api/phone-auth/process',
  }
});

const handleVerify = async () => {
  await authenticate({
    use_case: USE_CASE.VERIFY_PHONE_NUMBER,
    phone_number: '+14155551234'
  });
};
</script>

<template>
  <button @click="handleVerify" :disabled="isLoading">
    {{ isLoading ? 'Verifying...' : 'Verify Phone' }}
  </button>
</template>
```

### Vanilla JavaScript (Browser)

```html
<script src="https://unpkg.com/@glideidentity/web-client-sdk/dist/browser/web-client-sdk.min.js"></script>
<script>
  const { PhoneAuthClient, USE_CASE } = GlideWebClientSDK;
  
  const client = new PhoneAuthClient({
    endpoints: {
      prepare: '/api/phone-auth/prepare',
      process: '/api/phone-auth/process',
    }
  });
  
  document.getElementById('verify-btn').onclick = async () => {
    const result = await client.authenticate({
      use_case: USE_CASE.VERIFY_PHONE_NUMBER,
      phone_number: '+14155551234'
    });
    console.log('Verified:', result.verified);
  };
</script>
```

## Advanced Features

### Custom HTTP Headers

Add custom headers to all SDK requests:

```javascript
const client = new PhoneAuthClient({
  endpoints: { ... },
  headers: {
    common: {
      'X-Custom-Header': 'your-value'
    }
  }
});
```

### Custom Logger

Provide your own logger implementation:

```javascript
const client = new PhoneAuthClient({
  endpoints: { ... },
  logger: {
    debug: (msg, data) => console.debug('[SDK]', msg, data),
    info: (msg, data) => console.info('[SDK]', msg, data),
    warn: (msg, data) => console.warn('[SDK]', msg, data),
    error: (msg, data) => console.error('[SDK]', msg, data),
  }
});
```

### Custom HTTP Client

Bring your own HTTP client for full control:

```javascript
const client = new PhoneAuthClient({
  endpoints: { ... },
  httpClient: {
    post: async (url, body, options) => {
      const response = await yourHttpLib.post(url, body, options);
      return response.data;
    },
    get: async (url, options) => {
      const response = await yourHttpLib.get(url, options);
      return response.data;
    }
  }
});
```

## Core Package

For advanced use cases, you can import just the types and validators without the full client:

```typescript
import { 
  // Types
  type PrepareRequest,
  type PrepareResponse,
  type InvokeResult,
  type SessionInfo,
  
  // Constants
  USE_CASE,
  AUTHENTICATION_STRATEGY,
  ERROR_CODES,
  
  // Validators
  validatePhoneNumber,
  validatePlmn,
  
  // Type Guards
  isTS43Strategy,
  isLinkStrategy,
  isDesktopStrategy,
  isAuthError,
} from '@glideidentity/web-client-sdk/core';

// Use validators
const { valid, error } = validatePhoneNumber('+14155551234');

// Use type guards
if (isDesktopStrategy(result)) {
  // TypeScript knows result has desktop-specific properties
}
```

## Error Handling

```javascript
import { ERROR_CODES, isAuthError } from '@glideidentity/web-client-sdk';

try {
  await client.authenticate({ ... });
} catch (error) {
  if (isAuthError(error)) {
    switch (error.code) {
      case ERROR_CODES.USER_CANCELLED:
        // User closed the modal or cancelled
        break;
      case ERROR_CODES.TIMEOUT:
        // Authentication timed out
        break;
      case ERROR_CODES.NETWORK_ERROR:
        // Network request failed
        break;
      default:
        // Handle other errors
        console.error(error.message);
    }
  }
}
```

## Type Reference

### Use Cases

```typescript
type UseCase = 'GetPhoneNumber' | 'VerifyPhoneNumber';

// Or use the constant
USE_CASE.GET_PHONE_NUMBER    // 'GetPhoneNumber'
USE_CASE.VERIFY_PHONE_NUMBER // 'VerifyPhoneNumber'
```

### Prepare Request

```typescript
interface PrepareRequest {
  use_case: UseCase;
  phone_number?: string;      // Required for VerifyPhoneNumber
  parent_session_id?: string; // For cross-device flows
}
```

### Invoke Result

```typescript
interface InvokeResult {
  strategy: 'ts43' | 'link' | 'desktop';
  session: SessionInfo;
  credential: Promise<AuthCredential>;
  cancel?: () => void;  // Available for link and desktop
}
```

### Responses

```typescript
interface GetPhoneNumberResponse {
  phone_number: string;
}

interface VerifyPhoneNumberResponse {
  phone_number: string;
  verified: boolean;
}
```

## Browser Support

| Browser | Strategy | Requirements |
|---------|----------|--------------|
| Chrome Android 128+ | TS43 | Digital Credentials API |
| Safari iOS | Link | App Clips support |
| Chrome/Edge/Firefox Desktop | Desktop | Any modern version |

## Support

- [Documentation](https://docs.glideidentity.com)
- [API Reference](https://docs.glideidentity.com/api-reference)

## License

MIT