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

expo-telegram-auth

v0.1.0

Published

Native Telegram Login for Expo apps — wraps Telegram's official Android and iOS Login SDKs for a browser-free, app-to-app OAuth flow that returns an OIDC id_token

Downloads

145

Readme

expo-telegram-auth

Native Telegram Login for Expo apps — wraps Telegram's official Login SDKs (Android · iOS) so login runs app-to-app with the Telegram app: no browser, no Custom-Tab return hop, no "Open in app?" popup. Resolves with an OpenID Connect id_token (JWT) that you verify on your server.

  • Android: the forward hop is a tg:// intent straight into the Telegram app; the return hop is a VIEW intent fired by the Telegram app to your verified App Link. Chromium's same-app safeguard (the thing that makes browser-based OAuth returns show the "Continue to your app?" popup) can never apply — there is no browser in the loop.
  • iOS: same app-to-app flow via Universal Links; if Telegram isn't installed the official SDK falls back to an in-app ASWebAuthenticationSession sheet on its own.
  • Web: not supported (isNativeLoginSupported() returns false) — keep your existing web widget/OIDC flow.

Both official SDKs are vendored at pinned upstream commits (see telegramSdk in package.json and scripts/sync-upstream.mjs) — a zero-credential install: no GitHub Packages PAT, no extra Maven repos, no SPM wiring.

Installation

npx expo install expo-telegram-auth

BotFather setup (required)

Open @BotFather → your bot → Bot Settings → Login Widget → Native Login and register each app identity:

  • Android: package name + the SHA-256 signing-cert fingerprint (./gradlew signingReport, or read it off an installed build). ⚠️ Register every cert that will sign the app — local debug keystore, EAS keystore, and the Play App Signing key — or the App Link verification silently fails for the missing one and the return hop opens in the browser instead.
  • iOS: Bundle ID + your 10-character Apple Team ID.

Each registration mints a dedicated App URL like https://app123456-login.tg.dev — it is per-registration (NOT derived from your client id). That URL is your redirect URI; Telegram hosts the matching assetlinks.json / AASA for you.

Configuration

Add the config plugin with your per-identity App URLs:

// app.config.ts
plugins: [
  [
    'expo-telegram-auth',
    {
      android: { appLinkUrl: 'https://app123456-login.tg.dev/tglogin' },
      ios: { universalLink: 'https://app123456-login.tg.dev' },
      // ios: { fallbackScheme: 'myapp' } — custom-scheme fallback (see caveats)
    },
  ],
]

The plugin adds the Android autoVerify App Link intent filter, the iOS applinks: Associated Domain, LSApplicationQueriesSchemes: tg (required for detecting the Telegram app), and the optional fallback-scheme URL type. Rebuild natively after changing it (npx expo prebuild / a new dev build).

Usage

import * as TelegramAuth from 'expo-telegram-auth'

if (TelegramAuth.isNativeLoginSupported() && (await TelegramAuth.isTelegramAppInstalled())) {
  try {
    const { idToken } = await TelegramAuth.login({
      clientId: '<your BotFather client id>',
      redirectUri: 'https://app123456-login.tg.dev/tglogin', // = the plugin's appLinkUrl/universalLink
      scopes: ['openid', 'profile'],
    })
    // Send idToken to YOUR backend and verify it there (see "Verifying the id_token").
  } catch (error) {
    const code = TelegramAuth.getTelegramAuthErrorCode(error)
    if (code === 'ERR_CANCELLED' || code === 'ERR_DISMISSED') return // user backed out — no-op
    throw error
  }
} else {
  // fall back to your existing browser-based flow
}

Android gating tip: gate on isTelegramAppInstalled(). Without the Telegram app the Android SDK falls back to a Custom-Tab web login whose return hop reintroduces the exact browser→app popup this module exists to remove — prefer your own browser flow there. On iOS the built-in ASWebAuthenticationSession fallback is fine to use.

Error codes

login() rejections carry a stable error.code (read it with getTelegramAuthErrorCode):

| Code | Meaning | | --- | --- | | ERR_CANCELLED | User denied in Telegram / cancelled the iOS auth sheet. Silent no-op. | | ERR_DISMISSED | User came back from Telegram without deciding — no return hop arrived. Silent no-op. | | ERR_NO_AUTH_CODE | Return URL carried no authorization code. | | ERR_SERVER | Telegram's token endpoint returned a non-200. | | ERR_REQUEST_FAILED | Network/SDK failure (message has the native detail). | | ERR_NOT_CONFIGURED | iOS SDK not configured (shouldn't happen — config is per-call). | | ERR_CONCURRENT | A login is already in flight. | | ERR_NOT_SUPPORTED | Native module unavailable (web / excluded platform). |

expo-router apps: rewrite the return URL

The return-hop URL is also delivered to React Native's Linking (Expo's lifecycle listeners/app-delegate subscribers observe rather than consume), so expo-router will try to route https://app123456-login.tg.dev/tglogin?... and land on your not-found screen. Rewrite it in +native-intent.tsx:

// app/+native-intent.tsx
export function redirectSystemPath({ path }: { path: string; initial: boolean }) {
  try {
    if (new URL(path).host === 'app123456-login.tg.dev') {
      return '/' // or the screen the user started the login from
    }
  } catch {}
  return path
}

Verifying the id_token (server side)

The idToken is an RS256 JWT signed by Telegram. Verify it on your backend against Telegram's JWKS before trusting it:

  • JWKS: https://oauth.telegram.org/.well-known/jwks.json
  • iss: https://oauth.telegram.org · aud: your client id · alg RS256

See Telegram's docs on validating ID tokens.

Open question (roadmap): should this package ship a Node server adapter (e.g. an expo-telegram-auth/server entrypoint with a ready-made verifyTelegramIdToken())? Opinions welcome in the issues.

Behavior details & caveats

  • One login at a time; config (clientId/redirectUri/scopes) is passed per login() call — there is no init step.
  • Dismissal: the app-to-app branch is fire-and-forget; if the user backs out of Telegram undecided there is no callback. The module watches for the app returning to the foreground without a return URL and rejects with ERR_DISMISSED after a short grace period.
  • Process death: if Android kills your app while the user is in Telegram, the PKCE verifier dies with it — the (cold-start) return delivery is ignored safely and the user simply retries. Guarded: it cannot crash the app.
  • openid scope is always requested (the two SDKs disagree on adding it; the module normalizes so id_token claims are identical across platforms).
  • Free Apple developer teams cannot use Associated Domains — without a paid team the iOS app-installed branch cannot return to your app. The fallbackScheme only covers the SDK's web-session branch (< iOS 17.4). Plan on a paid team for production iOS.
  • Android minSdk 23, iOS 15+ (the pod targets Expo's floor).

Vendored upstream SDKs

android/src/main/java/org/telegram/login/ and ios/vendor/TelegramLogin.swift are byte-for-byte copies of Telegram's official SDK sources at the commits pinned in package.jsontelegramSdk, each with a provenance header. Never edit them by hand: bump the pin and run yarn sync-upstream. See THIRD_PARTY_LICENSES.md.

License

MIT — see LICENSE. Telegram SDK sources © Telegram FZ-LLC (see THIRD_PARTY_LICENSES.md).