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-react-native

v3.0.0

Published

Glide Identity SDK for React Native — carrier-grade SIM-based phone number verification and authentication

Readme

@glideidentity/glide-fe-sdk-react-native

React Native SDK for Glide Identity phone authentication. Verify phone numbers through carrier networks with hardware-backed security.

This is the client-side SDK. It calls into your backend's prepare and process endpoints, which integrate with Glide's Magic Auth API v2 via one of the Glide backend SDKs (Node.js, Java, or Go). See Backend Setup below for the partner-side proxy pattern.

Authentication strategies:

The SDK supports two carrier-driven strategies. The carrier (not the OS) determines which strategy a given prepare call returns — the SDK reads authentication_strategy: 'ts43' | 'link' from the prepare response and dispatches accordingly. Branch your code on the strategy, not on Platform.OS.

| Strategy | Mechanism | Partner setup | |---|---|---| | ts43 | Native Digital Credentials API intent — PLMN auto-detected from the SIM | compileSdkVersion 35, Android 9+ / API 28 at runtime | | link | Universal Link / App Link callback + Secure Enclave–signed device binding | Deep-link plumbing — see Link strategy setup |

The carrier may also be ineligible for both strategies (e.g. unsupported carrier, or an Android device that can't satisfy the Digital Credentials API requirements). In those cases prepare() rejects with a typed PhoneAuthError — wrap the call in try/catch and fall back to your alternative flow (SMS, OTP, etc.).

Prerequisites

| Requirement | Android | iOS | |-------------|---------|-----| | Runtime | React Native 0.70+ | React Native 0.70+ | | IDE | Android Studio, compileSdk 35+ | Xcode 15+, iOS 16+ SDK | | Device | Physical device with SIM card | Physical device (Secure Enclave) | | Tools | adb on PATH | CocoaPods, Apple Developer account | | Backend | Node.js 18+ (or Go/Java) | Node.js 18+ (or Go/Java) | | Credentials | Glide client_id + client_secret | Glide client_id + client_secret |

iOS additional (when authentication_strategy === 'link'): Associated Domains entitlement for Universal Links — see Link strategy setup below.

Note: Emulators/simulators do not support carrier authentication. A physical device with an active SIM is required.

Installation

npm install @glideidentity/glide-fe-sdk-react-native

# iOS only
cd ios && pod install

Android: The SDK installs on minSdkVersion 23+ (Android 6.0 Marshmallow). It needs compileSdkVersion 35 (required by the androidx.credentials library). The TS43 strategy uses the Android Digital Credentials API (Android 9+ / API 28, Google Play Services 24.0+, registered carrier credential provider). In your android/build.gradle:

buildscript {
    ext {
        compileSdkVersion = 35   // Required by Glide SDK
        targetSdkVersion = 35
        // minSdkVersion can stay at your current value (24+)
    }
}

Optional — for haptic feedback on Android, add to android/app/src/main/AndroidManifest.xml:

<uses-permission android:name="android.permission.VIBRATE" />

Quick Start — Direct Mode

Use usePhoneAuth() for React components. The hook manages state and deep link handling automatically.

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

function AuthScreen() {
  const { authenticate, isLoading, error, result } = usePhoneAuth({
    endpoints: {
      prepare: 'https://your-backend.com/api/phone-auth/prepare',
      process: 'https://your-backend.com/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);
      console.log('SIM swap risk:', result.sim_swap?.risk_level);
    } catch (err) {
      console.log('Error:', err.code, err.message);
    }
  };

  return <Button title="Verify Phone" onPress={handleVerify} disabled={isLoading} />;
}

Class-Based API

usePhoneAuth wraps PhoneAuthClient for React. Use the class directly for non-React contexts or when you need full control:

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

const glide = new PhoneAuthClient({
  endpoints: {
    prepare: 'https://your-backend.com/api/phone-auth/prepare',
    process: 'https://your-backend.com/api/phone-auth/process',
  },
  debug: true,
});

// High-level — one call handles prepare → invoke → process
const result = await glide.authenticate({
  use_case: USE_CASE.GET_PHONE_NUMBER,
});
console.log(result.phone_number);

Granular API

For more control, use the three-step flow:

const { prepare, invokeSecurePrompt, getPhoneNumber, verifyPhoneNumber } = usePhoneAuth({
  endpoints: {
    prepare: 'https://your-backend.com/api/phone-auth/prepare',
    process: 'https://your-backend.com/api/phone-auth/process',
  },
});

// Step 1: Prepare
const prepared = await prepare({
  use_case: USE_CASE.GET_PHONE_NUMBER,
});

// Step 2: Invoke carrier auth (must be from user gesture on iOS)
const invokeResult = await invokeSecurePrompt(prepared);
const credential = await invokeResult.credential;

// Step 3: Process
const result = await getPhoneNumber(credential, prepared.session);
console.log(result.phone_number);

Configuration

The full PhoneAuthConfig shape (all fields except endpoints are optional):

import type { PhoneAuthConfig, HttpClient, Logger } from '@glideidentity/glide-fe-sdk-react-native';

const config: PhoneAuthConfig = {
  endpoints: {
    // Required — your backend's prepare proxy.
    prepare: 'https://your-backend.com/api/phone-auth/prepare',

    // Required — your backend's process proxy. Routes to Glide's
    // /get-phone-number or /verify-phone-number based on use_case.
    process: 'https://your-backend.com/api/phone-auth/process',

    // Optional — your backend's report-invocation proxy for ASR (Auth
    // Success Rate) telemetry. Fired once per invokeSecurePrompt() in
    // a fire-and-forget POST { session_id }. Omit if you don't want
    // ASR metrics; the SDK silently skips the call.
    reportInvocation: 'https://your-backend.com/api/phone-auth/report-invocation',
  },

  // Optional — replace the default fetch-based HTTP client. Implement
  // the HttpClient interface to route through Axios/ky/etc. or to add
  // common headers / interceptors. Omit to use the built-in fetch.
  // httpClient: new MyAxiosClient(),

  // Optional — plug in your logging framework (Sentry, Datadog, etc.).
  // Defaults to console.{debug,info,warn,error}. The SDK always emits
  // info/warn/error; debug is gated by the `debug` flag below.
  // logger: pino(),

  // Optional — enable verbose debug-level logging. Default: false.
  debug: false,

  // Optional — per-request timeout in ms. Default: 30_000 (30s).
  timeout: 30_000,

  // Optional — maximum wait time in ms for the Link strategy to
  // receive its Universal Link / App Link callback after the carrier
  // hand-off. Default: 300_000 (5 min) — matches the backend session
  // TTL. Increase only if you have a non-default backend session TTL.
  sessionTimeout: 300_000,
};

The minimal config in practice:

const config: PhoneAuthConfig = {
  endpoints: {
    prepare: 'https://your-backend.com/api/phone-auth/prepare',
    process: 'https://your-backend.com/api/phone-auth/process',
  },
};

Backend Setup

Your backend holds Glide credentials and proxies requests. The process endpoint must route based on use_case — there is no single /process endpoint on the Glide API.

const express = require('express');
const app = express();
app.use(express.json());

const { GLIDE_CLIENT_ID, GLIDE_CLIENT_SECRET } = process.env;
const GLIDE_API = 'https://api.glideidentity.app';

let tokenCache = { token: '', expiresAt: 0 };

async function getToken() {
  if (tokenCache.token && Date.now() < tokenCache.expiresAt) return tokenCache.token;
  const res = await fetch(`${GLIDE_API}/oauth2-cc/token`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: `grant_type=client_credentials&client_id=${GLIDE_CLIENT_ID}&client_secret=${GLIDE_CLIENT_SECRET}`,
  });
  const data = await res.json();
  tokenCache = { token: data.access_token, expiresAt: Date.now() + (data.expires_in - 60) * 1000 };
  return tokenCache.token;
}

async function proxyToGlide(endpoint, body) {
  const token = await getToken();
  const headers = { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' };

  // Forward client_info.user_agent so Glide returns the correct strategy per platform
  if (body?.client_info?.user_agent) headers['User-Agent'] = body.client_info.user_agent;

  const res = await fetch(`${GLIDE_API}${endpoint}`, {
    method: 'POST', headers, body: JSON.stringify(body),
  });
  return { status: res.status, data: await res.json() };
}

app.post('/api/phone-auth/prepare', async (req, res) => {
  try {
    const { status, data } = await proxyToGlide('/magic-auth/v2/auth/prepare', req.body);
    res.status(status).json(data);
  } catch (err) {
    res.status(500).json({ error: 'PROXY_ERROR', message: err.message });
  }
});

app.post('/api/phone-auth/process', async (req, res) => {
  try {
    // use_case determines the Glide endpoint but is NOT forwarded to the API
    const { use_case, ...glideBody } = req.body;
    const endpoint = use_case === 'GetPhoneNumber'
      ? '/magic-auth/v2/auth/get-phone-number'
      : '/magic-auth/v2/auth/verify-phone-number';
    const { status, data } = await proxyToGlide(endpoint, glideBody);
    res.status(status).json(data);
  } catch (err) {
    res.status(500).json({ error: 'PROXY_ERROR', message: err.message });
  }
});

// Optional — Auth Success Rate (ASR) telemetry. The SDK fires-and-forgets
// POST { session_id } once per invokeSecurePrompt() so the carrier can
// distinguish "user saw the prompt" from "user never opened the app". Omit
// the route entirely if you set `endpoints.reportInvocation: undefined` in
// the SDK config — the SDK skips the call without erroring.
app.post('/api/phone-auth/report-invocation', async (req, res) => {
  try {
    const { status, data } = await proxyToGlide('/magic-auth/v2/auth/report-invocation', req.body);
    res.status(status).json(data);
  } catch (err) {
    // Fire-and-forget on the SDK side — keep this route quiet on error.
    res.status(202).json({});
  }
});

app.listen(3001);

Key backend behaviors:

  • Route on use_case: The SDK sends use_case in the process body. Use it to pick /get-phone-number or /verify-phone-number, then strip it before forwarding.
  • Forward User-Agent: The SDK sends client_info.user_agent. Forward it as the HTTP User-Agent header — Glide uses this for strategy selection.
  • Pass-through everything else: session, credential, signature, public_key — forward as-is.
  • Report-invocation is optional: Skip the route + endpoints.reportInvocation config entry if you don't need ASR metrics. With it wired, the SDK posts { session_id } once per invokeSecurePrompt() (fire-and-forget — your backend's response doesn't affect the user's flow).

Link strategy setup

When prepare returns authentication_strategy: 'link', the carrier hands off to a Universal Link / App Link that needs to return control to your app. Two pieces are required:

  1. Per-OS platform setup — described below. Currently iOS only (Android setup will be documented when the SDK ships native plumbing for it).
  2. Deep-link forwarding in your app — described in Forward deep links to the SDK. Same code on iOS and Android.

Forward deep links to the SDK

usePhoneAuth() already registers the listener for you on both iOS and Android — skip this section if you're using the hook.

If you're using PhoneAuthClient directly, register the Linking listener and gate it with isGlideDeepLink(url) so the SDK only sees URLs that are actually a carrier callback (any other deep link your app receives keeps flowing to your own router):

import { Linking } from 'react-native';
import { PhoneAuthClient, isGlideDeepLink } from '@glideidentity/glide-fe-sdk-react-native';

const glide = new PhoneAuthClient(config);

const sub = Linking.addEventListener('url', ({ url }) => {
  if (isGlideDeepLink(url)) {
    glide.handleDeepLink(url);
    return;
  }
  // Not ours — your app's own deep-link router takes it from here.
});

// Cold-start: handle a URL the OS used to launch the app, if any.
Linking.getInitialURL().then((url) => {
  if (url && isGlideDeepLink(url)) glide.handleDeepLink(url);
});

// Remember to call sub.remove() on unmount.

isGlideDeepLink(url) recognises a carrier callback by the standard OAuth shape (both code and state query parameters present). It does NOT match on your redirect domain or path — those are configured at carrier-onboarding time and are partner-specific.

If you prefer a single short-circuit gate, glide.handleDeepLink(url) itself returns a boolean: true if the SDK consumed the URL, false otherwise — equivalent shape to the example above.

iOS — Universal Links

1. Host an AASA file on your redirect domain

During onboarding, Glide configures the carrier to redirect to your redirect domain (e.g., auth.yourcompany.com) after authentication. iOS needs to know your app handles URLs on that domain — done via an Apple App Site Association (AASA) file.

Create a file at https://<your-redirect-domain>/.well-known/apple-app-site-association:

{
  "applinks": {
    "apps": [],
    "details": [
      {
        "appID": "TEAM_ID.com.yourcompany.yourapp",
        "paths": [
          "/glideid/callback",
          "/glideid/callback/*"
        ]
      }
    ]
  }
}
  • Replace TEAM_ID with your Apple Developer Team ID.
  • Replace com.yourcompany.yourapp with your app's bundle identifier.
  • The path /glideid/callback is a placeholder — pick any path you control on your redirect domain. Whatever you choose must match the redirect URI you provide to Glide during onboarding (Glide configures the carrier with that exact URL).
  • Include both the exact path and the /* wildcard variant — carriers may append session-specific path segments, and exact-only matching causes intermittent fall-through to Safari.
  • File must be served over HTTPS with Content-Type: application/json and no redirects.

2. Add the Associated Domains capability in Xcode

Project → Target → Signing & Capabilities+ CapabilityAssociated Domains, then add:

applinks:<your-redirect-domain>
applinks:<your-redirect-domain>?mode=developer

The ?mode=developer variant bypasses Apple's CDN cache during development.

iOS testing checklist

  • [ ] AASA file is accessible: curl https://<your-redirect-domain>/.well-known/apple-app-site-association
  • [ ] App is installed on a physical device (Universal Links don't fire on the Simulator)
  • [ ] App has been launched at least once after install (iOS caches the AASA on first launch)
  • [ ] If links don't work: delete the app, reinstall, launch once before testing

| Issue | Fix | |---|---| | Universal Link opens Safari instead of app | Delete app, reinstall, launch once. Verify AASA is reachable. | | AASA not found | Ensure it's at /.well-known/apple-app-site-association (no file extension), HTTPS, no redirects. | | Works on one device but not another | Each device caches AASA independently. Reinstall the app. |

Android Setup

The TS43 Digital Credentials API is accessed via native modules included in the SDK. Most configuration is automatic.

PLMN auto-detection: The SDK reads the carrier's MCC/MNC from the SIM card via TelephonyManager — no permissions required. This is included in the prepare request so Glide can identify the carrier and return the correct TS43 configuration.

Optional — haptic feedback on successful authentication:

<!-- android/app/src/main/AndroidManifest.xml -->
<uses-permission android:name="android.permission.VIBRATE" />

Error Handling

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

try {
  await authenticate(request);
} catch (err) {
  if (isAuthError(err)) {
    switch (err.code) {
      case ERROR_CODES.USER_CANCELLED:
        // User closed the system prompt
        break;
      case ERROR_CODES.TIMEOUT:
        // Request or Link return timed out
        break;
      case ERROR_CODES.PLATFORM_NOT_SUPPORTED:
        // TS43 not available (non-Android) or SE not available (non-iOS)
        break;
    }
    console.log(getUserMessage(err)); // User-friendly message
  }
}

Response Types

Both getPhoneNumber() and verifyPhoneNumber() include carrier fraud signals and (when available) parsed phone-number components:

interface GetPhoneNumberResponse {
  phone_number: string;                       // E.164, e.g. "+14155551234"
  aud?: string;                               // Carrier audience value
  sim_swap?: SimSwapInfo;                     // SIM card change detection
  device_swap?: DeviceSwapInfo;               // IMEI change detection
  phone_number_details?: PhoneNumberDetails;  // Parsed components (server-populated)
}

interface VerifyPhoneNumberResponse {
  phone_number: string;
  verified: boolean;                          // true if the phone matched
  aud?: string;
  sim_swap?: SimSwapInfo;
  device_swap?: DeviceSwapInfo;
  phone_number_details?: PhoneNumberDetails;
}

interface PhoneNumberDetails {
  country_code: number;       // ITU E.164 country calling code (1, 44, 972, …)
  national_number: string;    // National significant number, no trunk prefix
  region_code: string;        // ISO 3166-1 alpha-2 ("US", "GB", "IL")
}

interface SimSwapInfo {
  checked: boolean;
  risk_level?: 'RISK_LEVEL_HIGH' | 'RISK_LEVEL_MEDIUM' | 'RISK_LEVEL_LOW' | 'RISK_LEVEL_UNKNOWN';
  age_band?: string;          // e.g., "0-4 hours"
  checked_at?: string;        // RFC 3339
  reason?: 'timeout' | 'carrier_not_supported' | 'disabled' | 'error';
}
// DeviceSwapInfo has the same shape as SimSwapInfo.

API Reference

| Export | Kind | Description | |--------|------|-------------| | PhoneAuthClient | Class | Main client — direct programmatic use (non-React) | | usePhoneAuth | Hook | React wrapper: PhoneAuthClient + deep-link listener + state | | UsePhoneAuthReturn | Type | Return type of usePhoneAuth (authenticate, prepare, invokeSecurePrompt, getPhoneNumber, verifyPhoneNumber, isLoading, error, result) | | generateKeyPair | Function | Secure Enclave P-256 key generation (iOS) | | signData | Function | Secure Enclave ECDSA signing (iOS) | | deleteKey | Function | Clean up SE key after use (iOS) | | requestDigitalCredential | Function | TS43 Digital Credentials API (Android) | | getCarrierInfo | Function | SIM carrier info (MCC/MNC) | | parseDeepLink | Function | Extract code/state from callback URL | | isGlideDeepLink | Function | Check if URL is a Glide callback | | DeepLinkResult | Type | parseDeepLink's discriminated return type | | USE_CASE | Const | GET_PHONE_NUMBER, VERIFY_PHONE_NUMBER | | AUTHENTICATION_STRATEGY | Const | Strategy identifiers (ts43, link) | | ERROR_CODES | Const | All client-side error codes | | ErrorCode | Type | Union of ERROR_CODES values | | createAuthError | Function | Construct an AuthError (mainly for testing / re-wrapping) | | isAuthError | Function | Type guard for AuthError | | isRetryableError | Function | Returns true for transient errors safe to retry | | getUserMessage | Function | User-friendly error message for an AuthError | | PhoneAuthConfig, PrepareRequest, PrepareResponse, InvokeResult, SessionInfo, GetPhoneNumberResponse, VerifyPhoneNumberResponse, SimSwapInfo, DeviceSwapInfo, AuthError, HttpClient, Logger, … | Types | Full PhoneAuthConfig shape, request/response models, error type, and pluggable interfaces — see source for complete list. |