npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@glideidentity/glide-fe-sdk-web

v3.0.0

Published

Glide Phone Authentication SDK for web applications

Readme

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
  • Strategy-driven — One API across ts43 (Android Digital Credentials), link (mobile-web App Clip / native App Link), and desktop (cross-device QR); Glide's Magical Auth service selects the strategy server-side from the carrier + your user agent and returns it in prepare
  • Customizable UI — Built-in modal with themes (single universal QR on desktop)
  • Flexible Architecture — Use the full SDK or just the core types
  • Framework Support — React, Vue, and vanilla JavaScript
  • Tree-Shakeable — Import only what you need
  • SIM-swap & device-swap signals — first-party carrier risk metadata exposed alongside the verified phone number

Installation

npm install @glideidentity/glide-fe-sdk-web

Quick Start

Two integration shapes ship with the SDK. The Granular API is what we recommend for production — it lets you call prepare() early so the carrier prompt opens with no perceptible delay when the user clicks the CTA. The High-Level API (authenticate()) bundles all three steps into one call and is the fastest way to see the SDK working end-to-end the first time you wire it up.

Recommended: Granular API

prepare → invokeSecurePrompt → verifyPhoneNumber (or getPhoneNumber) as three separate calls. The win is eager prepare: kick the network round-trip off as soon as you have enough context (e.g. when the user types their phone number, when the input field gains focus, or right after step navigation lands the user on the verification screen). By the time the user actually taps the verify button, the prepare response is already cached locally — invokeSecurePrompt() opens the carrier UI immediately, with no spinner.

The browser's user-gesture security model means invokeSecurePrompt() itself must still run from a synchronous click handler. Eager prepare doesn't change that — it just removes the network wait that would otherwise sit between the click and the carrier prompt.

import { PhoneAuthClient, USE_CASE } from '@glideidentity/glide-fe-sdk-web';

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

// Step 1: Call prepare() AS EARLY AS YOU HAVE THE PHONE NUMBER.
//   e.g. on input blur, on form-step transition, or right after the user types it.
//   This pre-pays the prepare round-trip so the carrier prompt opens instantly later.
const prepared = await client.prepare({
  use_case: USE_CASE.VERIFY_PHONE_NUMBER,
  phone_number: '+14155551234',
});

// Step 2: From a click handler — must be a real user gesture.
verifyBtn.addEventListener('click', async () => {
  const invokeResult = await client.invokeSecurePrompt(prepared);  // opens carrier UI immediately
  const credential = await invokeResult.credential;

  // Step 3: Hand the credential to your backend (which calls Glide's verify-phone-number / get-phone-number).
  const result = await client.verifyPhoneNumber(credential, invokeResult.session);
  console.log('Verified:', result.verified);
});

High-Level API (quickest path to "it works")

authenticate() runs prepare → invokeSecurePrompt → verifyPhoneNumber (or getPhoneNumber) for you in a single call. Easiest to integrate the first time and great for demos / spikes — the trade-off is that the carrier prompt won't open until the prepare round-trip completes inside the click handler, so on slower networks the user sees a brief delay between tap and carrier UI.

import { PhoneAuthClient, USE_CASE } from '@glideidentity/glide-fe-sdk-web';

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

verifyBtn.addEventListener('click', async () => {
  // Get a phone number (use_case omitted for VERIFY_PHONE_NUMBER + a phone_number)
  const result = await client.authenticate({
    use_case: USE_CASE.GET_PHONE_NUMBER
  });
  console.log('Phone:', result.phone_number);
});

Configuration

Client Options

const client = new PhoneAuthClient({
  // Backend endpoints (with defaults shown)
  endpoints: {
    prepare: '/api/magical-auth/prepare',              // Prepare request endpoint
    process: '/api/magical-auth/process',              // Process credential endpoint (your backend long-polls Magical Auth)
    reportInvocation: '/api/magical-auth/report-invocation', // ASR tracking endpoint
    eligibility: '/api/magical-auth/eligibility',      // Eligibility check endpoint (optional)
  },

  timeout: 30000,             // API request timeout in ms (default: 30000)
  sessionTimeout: 300000,     // Max wait for desktop session completion — QR scan → mobile auth → backend `/process` chain (default: 5 min)

  // Strategy override — pin a specific strategy instead of letting the server auto-select.
  // Must be one of the strategies returned by checkEligibility() for the target phone number.
  // If not available, prepare() returns a STRATEGY_NOT_AVAILABLE error.
  authenticationStrategy: 'ts43', // Optional: 'ts43' | 'link' | 'desktop' (default: auto-selected by server)

  // 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:

const result = await client.invokeSecurePrompt(prepared, {
  // Prevent SDK from showing any UI (use your own)
  preventDefaultUI: false,

  // Modal customization (for desktop QR strategy)
  modalOptions: {
    theme: 'auto',              // 'dark' | 'light' | 'auto'
    title: 'Scan to Verify',    // Custom title text
    description: '',            // Optional subtitle shown below the QR
    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. It renders a single universal QR code — the Mobile Auth companion app detects the device platform at runtime, so there is no OS toggle / picker on desktop.

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:

import { getDesktopQRImage } from '@glideidentity/glide-fe-sdk-web';

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

if (result.strategy === 'desktop') {
  const qrImage = getDesktopQRImage(prepared.data); // base64 PNG data URI
  const url = prepared.data.data.url;               // universal URL inside the QR
  displayYourQRCode(qrImage, url);

  const credential = await result.credential;
  // result.cancel?.() to abort the flow
}

Authentication Strategies

Glide's Magical Auth service selects the strategy server-side based on the carrier (looked up from the phone number / PLMN) and your user agent (platform, browser, native vs mobile-web), then returns it in prepare as authentication_strategy. Branch your code on the returned value, not on navigator.userAgent or browser sniffing — Glide has already factored in the OS / browser, and the same OS may receive different strategies for different carriers.

| Strategy | Where it runs | Mechanism | |----------|---------------|-----------| | ts43 | Android Chrome 128+ | Native Digital Credentials API hand-off via the platform Credential Manager. | | link | Any mobile browser whose carrier uses an App Clip / App Link redirect-with-code handoff | The Web SDK runs the mobile-web mode of link: opens the carrier App Clip in a new tab, the result returns via a completion-page redirect, device binding uses an HttpOnly cookie (fe_code) plus a URL fragment (agg_code), and the original tab observes a localStorage signal to retrieve the result. (The native return-channel mode of link — Universal Link / App Link with Secure Enclave–backed binding — is handled by the React Native and Android SDKs, not this one.) | | desktop | Desktop browsers | QR code scanned by a Glide-aware mobile companion. |

Strategy-Specific Handling

const result = await client.invokeSecurePrompt(prepared);

switch (result.strategy) {
  case 'ts43':
    // Android: Credential returned directly
    const credential = await result.credential;
    break;
    
  case 'link':
    // Mobile-web mode of `link`: SDK opens the carrier App Clip in a new tab,
    // listens for the completion-page `localStorage` signal, then resolves the credential.
    const credential = await result.credential;
    // Optionally cancel the in-flight flow
    result.cancel?.();
    break;
    
  case 'desktop':
    // Desktop: QR modal; credential resolves when mobile completes auth (backend `/process` long-poll)
    const credential = await result.credential;
    result.cancel?.();
    break;
}

Framework Integration

React

import { usePhoneAuth, USE_CASE } from '@glideidentity/glide-fe-sdk-web/react';

function PhoneVerification() {
  const { authenticate, isLoading, error, result } = usePhoneAuth({
    endpoints: {
      prepare: '/api/magical-auth/prepare',
      reportInvocation: '/api/magical-auth/report-invocation',
      process: '/api/magical-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

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

const { authenticate, isLoading, error, result } = usePhoneAuth({
  endpoints: {
    prepare: '/api/magical-auth/prepare',
    reportInvocation: '/api/magical-auth/report-invocation',
    process: '/api/magical-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)

<script src="https://unpkg.com/@glideidentity/glide-fe-sdk-web/dist/browser/web-client-sdk.min.js"></script>
<script>
  const { PhoneAuthClient, USE_CASE } = GlideWebClientSDK;
  
  const client = new PhoneAuthClient({
    endpoints: {
      prepare: '/api/magical-auth/prepare',
      reportInvocation: '/api/magical-auth/report-invocation',
      process: '/api/magical-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>

Eligibility & Strategy Selection

Check Eligibility

Query available authentication strategies before starting a flow. The response is relative to the user's device: available_platforms names the phone the user needs, and requirements.target is self (this device) or companion (a separate phone, e.g. desktop QR/CTAP).

client_info.user_agent is required by the API; the web SDK auto-fills it from navigator.userAgent when you don't pass client_info, so a plain call works in the browser. Pass client_info explicitly to test a specific device.

const eligibility = await client.checkEligibility({
  phone_number: '+14155551234',
  // client_info optional in the browser — auto-filled from navigator.userAgent
});

// Example (desktop Chrome, T-Mobile):
// eligibility.available_options = [
//   { authentication_strategy: 'ts43', name: 'SIM-Based Authentication', available_platforms: ['android'],
//     requirements: { target: 'companion', android_min_version: 14, android_min_sdk: 34 } },
//   { authentication_strategy: 'desktop', name: 'Mobile Companion Authentication', available_platforms: ['android'],
//     requirements: { target: 'companion', android_min_version: 14, android_min_sdk: 34 } },
// ]

Strategy Override

Pass authentication_strategy in the prepare request to explicitly select a strategy from the eligible set:

const prepared = await client.prepare({
  use_case: USE_CASE.VERIFY_PHONE_NUMBER,
  phone_number: '+14155551234',
  authentication_strategy: 'link',  // override server-assigned strategy
});

If the requested strategy is not available, the API returns a STRATEGY_NOT_AVAILABLE error.

Advanced Features

Bring Your Own HTTP Client

The default HTTP client is intentionally minimal — it only handles JSON serialization, timeouts, abort signals, and error parsing. Anything else (custom headers, auth tokens, environment routing, retries, axios/ky/fetch wrappers, request/response interceptors) belongs in your own httpClient implementation:

const client = new PhoneAuthClient({
  endpoints: { ... },
  httpClient: {
    post: async (url, body, options) => {
      const response = await fetch(url, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Authorization': `Bearer ${await getAuthToken()}`,
          'X-Trace-Id': crypto.randomUUID(),
          ...options?.headers,
        },
        body: JSON.stringify(body),
        credentials: options?.credentials,
        signal: options?.signal,
      });
      if (!response.ok) throw await response.json();
      return response.json();
    },
    get: async (url, options) => {
      const response = await fetch(url, {
        headers: {
          'Authorization': `Bearer ${await getAuthToken()}`,
          ...options?.headers,
        },
        credentials: options?.credentials,
        signal: options?.signal,
      });
      if (!response.ok) throw await response.json();
      return response.json();
    },
  },
});

The SDK calls httpClient.post() / httpClient.get() for every request — you control transport, headers, retries, and observability end-to-end.

Bring Your Own Logger

The SDK is silent by default — no console output of any level. Two ways to opt in:

  1. Built-in console logger — set debug: true to enable verbose output (debug / info / warn / error all routed through console.*, with phone numbers, JWTs, and session keys auto-redacted):

    const client = new PhoneAuthClient({ endpoints: { ... }, debug: true });
  2. Bring your own — forward SDK logs to your existing logging infrastructure (Sentry, Datadog, Pino, etc.) by providing a logger that satisfies the Logger interface. Your logger receives every level the SDK emits; level filtering is your responsibility:

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

The SDK never logs caught errors before re-throwing — your try/catch sees them once. Routine flow events (prepare success, phone retrieved, desktop companion progress) are emitted at debug so they stay quiet unless you explicitly want them. warn is reserved for genuinely recoverable failures (modal failed to render, invocation report upload failed).

Core Package

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

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/glide-fe-sdk-web/core';

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

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

Error Handling

import { ERROR_CODES, isAuthError } from '@glideidentity/glide-fe-sdk-web';

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

type UseCase = 'GetPhoneNumber' | 'VerifyPhoneNumber';

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

Prepare Request

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

Invoke Result

interface InvokeResult {
  strategy: 'ts43' | 'link' | 'desktop';
  session: SessionInfo;
  credential: Promise<string>;  // Resolves to credential token
  cancel?: () => void;          // Available for link and desktop
  invocationReport: Promise<{ success: boolean; error?: string }>;
}

ASR Tracking (reportInvocation)

The SDK automatically tracks authentication attempts for Authentication Success Rate (ASR) metrics. This is handled via the reportInvocation endpoint.

Behavior

| Aspect | Description | |--------|-------------| | Fire-and-forget | Never blocks the main authentication flow | | Non-critical | Auth succeeds even if reporting fails | | Observable | Developers CAN check if it worked (optional) | | No latency impact | User doesn't wait for this API call |

Configuration

The reportInvocation endpoint is included in the default config:

const client = new PhoneAuthClient({
  endpoints: {
    prepare: '/api/magical-auth/prepare',
    process: '/api/magical-auth/process',
    reportInvocation: '/api/magical-auth/report-invocation',  // ASR tracking (default)
  }
});

Monitoring (Optional)

If you want to monitor whether ASR tracking succeeded:

const result = await client.invokeSecurePrompt(prepared);

// Main flow continues immediately
const credential = await result.credential;

// Optional: Check report status (doesn't block)
result.invocationReport?.then(({ success, error }) => {
  if (!success) {
    console.warn('ASR tracking failed:', error);
    // Optionally send to your own analytics
  }
});

Backend Endpoint

Your backend should implement a simple pass-through endpoint:

// POST /api/magical-auth/report-invocation
app.post('/api/magical-auth/report-invocation', async (req, res) => {
  const { session_id } = req.body;
  
  try {
    const result = await glide.magicalAuth.reportInvocation({ session_id });
    res.json({ success: result.success });
  } catch (error) {
    // Always return HTTP 200 - never fail the auth flow
    res.json({ success: false, error: error.message });
  }
});

Responses

interface GetPhoneNumberResponse {
  phone_number: string;
  aud?: string;               // Audience from carrier
  sim_swap?: SimSwapInfo;     // SIM swap detection info
  device_swap?: DeviceSwapInfo; // Device swap (IMEI change) detection info
}

interface VerifyPhoneNumberResponse {
  phone_number: string;
  verified: boolean;
  aud?: string;               // Audience from carrier
  sim_swap?: SimSwapInfo;     // SIM swap detection info
  device_swap?: DeviceSwapInfo; // Device swap (IMEI change) detection info
}

SIM Swap Detection

The SDK returns SIM swap detection information when available from carriers. This helps identify potential fraud.

Response Fields

interface SimSwapInfo {
  /** Whether the SIM swap check completed successfully */
  checked: boolean;
  /** Risk level based on SIM change recency */
  risk_level?: 'RISK_LEVEL_HIGH' | 'RISK_LEVEL_MEDIUM' | 'RISK_LEVEL_LOW' | 'RISK_LEVEL_UNKNOWN';
  /** Human-readable time since last SIM change (e.g., "0-4 hours", "more than 3 years") */
  age_band?: string;
  /** When the check was performed (RFC3339) */
  checked_at?: string;
  /** Reason for failure if checked=false */
  reason?: 'timeout' | 'carrier_not_supported' | 'disabled' | 'error';
}

Risk Levels

| Level | Description | Recommended Action | |-------|-------------|-------------------| | RISK_LEVEL_HIGH | SIM changed within 7 days | Block or require additional verification | | RISK_LEVEL_MEDIUM | SIM changed 7-30 days ago | Consider step-up authentication | | RISK_LEVEL_LOW | SIM changed 30+ days ago | Normal processing | | RISK_LEVEL_UNKNOWN | Could not determine | Use default policy |

Helper Functions

import { 
  isHighRiskSimSwap,
  isMediumOrHighRiskSimSwap,
  wasSimSwapChecked,
  getMinDaysSinceSimSwap,
} from '@glideidentity/glide-fe-sdk-web';

const result = await client.getPhoneNumber(credential, session);

// Check if SIM swap was checked
if (wasSimSwapChecked(result.sim_swap)) {
  // Check risk level
  if (isHighRiskSimSwap(result.sim_swap)) {
    // High risk - recent SIM change
    requireAdditionalVerification();
  } else if (isMediumOrHighRiskSimSwap(result.sim_swap)) {
    // Medium risk - consider step-up auth
    showWarning();
  }
  
  // Get minimum days since SIM change
  const minDays = getMinDaysSinceSimSwap(result.sim_swap);
  console.log(`SIM unchanged for at least ${minDays} days`);
}

Device Swap Detection

The SDK returns device swap (IMEI change) detection information when available from carriers. This is a parallel, independent signal alongside SIM swap detection. Not all carriers support this signal — the device_swap field is only present when the carrier provides it.

Response Fields

interface DeviceSwapInfo {
  /** Whether the device swap check completed successfully */
  checked: boolean;
  /** Risk level based on device change recency */
  risk_level?: 'RISK_LEVEL_HIGH' | 'RISK_LEVEL_MEDIUM' | 'RISK_LEVEL_LOW' | 'RISK_LEVEL_UNKNOWN';
  /** Human-readable time since last device (IMEI) change (e.g., "0-4 hours", "more than 3 years") */
  age_band?: string;
  /** When the check was performed (RFC3339) */
  checked_at?: string;
  /** Reason for failure if checked=false */
  reason?: 'timeout' | 'carrier_not_supported' | 'disabled' | 'error';
}

Risk Levels

| Level | Description | Recommended Action | |-------|-------------|-------------------| | RISK_LEVEL_HIGH | Device changed within 7 days | Block or require additional verification | | RISK_LEVEL_MEDIUM | Device changed 7-30 days ago | Consider step-up authentication | | RISK_LEVEL_LOW | Device changed 30+ days ago | Normal processing | | RISK_LEVEL_UNKNOWN | Could not determine | Use default policy |

Helper Functions

import { 
  isHighRiskDeviceSwap,
  isMediumOrHighRiskDeviceSwap,
  wasDeviceSwapChecked,
  getMinDaysSinceDeviceSwap,
} from '@glideidentity/glide-fe-sdk-web';

const result = await client.getPhoneNumber(credential, session);

// Check if device swap was checked
if (wasDeviceSwapChecked(result)) {
  // Check risk level
  if (isHighRiskDeviceSwap(result)) {
    // High risk - recent device change
    requireAdditionalVerification();
  } else if (isMediumOrHighRiskDeviceSwap(result)) {
    // Medium risk - consider step-up auth
    showWarning();
  }
  
  // Get minimum days since device change
  const minDays = getMinDaysSinceDeviceSwap(result);
  console.log(`Device unchanged for at least ${minDays} days`);
}

Browser Support

| Browser | Eligible strategies | Requirements | |---------|---------------------|--------------| | Chrome Android 128+ | ts43 (when the carrier supports TS43) or link (mobile-web mode, when the carrier uses the App Clip / App Link flow) | Digital Credentials API | | Safari iOS / Chrome iOS | link (mobile-web mode) | App Clip support on the device | | Chrome / Edge / Firefox Desktop | desktop | Any modern version |

Strategy assignment is done by Glide's Magical Auth service in prepare (using the carrier + your user agent) — the same browser may receive different strategies for different carriers, and link runs in mobile-web mode on every supported mobile browser.

Development (contributors)

npm ci
npm test
npm run typecheck
npm run build   # CJS, ESM, types, browser bundle
npm run clean
npm pack        # for local install testing

Support

License

See LICENSE in this package.