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-native-google-signin

v0.1.0

Published

Native Google Sign-In for Expo apps using Android Credential Manager and the iOS GoogleSignIn SDK — no legacy play-services-auth, no @react-native-google-signin/google-signin dependency.

Readme

expo-native-google-signin

Native Google sign-in for Expo apps on Android (Credential Manager) and iOS (the GoogleSignIn SDK). It returns a Google ID token that any backend can verify — Supabase, Firebase, or your own server.

Why: "Google Sign-In for Android" (GoogleSignInClient in play-services-auth) is deprecated in favour of Credential Manager, and play-services-auth 22.0.0 already removed those classes from the SDK. This module talks only to Google's first-party SDKs — no dependency on @react-native-google-signin/google-signin or any other community wrapper. MIT-licensed.

Status and requirements

  • Pre-1.0. 0.1.0 is on npm (latest; see CHANGELOG.md). Still pre-1.0, so a minor bump may break the API — pin an exact version:

    npm install [email protected] --save-exact
  • Expo SDK 57.

  • A development build — this module is not available in Expo Go.

  • Android: minSdk 24.

  • iOS: 15.1+, and Xcode 26+ (Expo SDK 57 itself requires it — expo-modules-jsi needs Swift tools 6.2).

  • GoogleSignIn-iOS ~> 9.2 (pinned; see docs/ARCHITECTURE.md for why).

Install

npx expo install expo-native-google-signin
# or, pinned exactly:
npm install [email protected] --save-exact

Add the config plugin in app.json / app.config.*:

{
  "expo": {
    "plugins": [
      [
        "expo-native-google-signin",
        { "iosUrlScheme": "com.googleusercontent.apps.YOUR_IOS_CLIENT_ID" }
      ]
    ]
  }
}

Then rebuild the native projects:

npx expo prebuild
npx expo run:android
npx expo run:ios

(or an EAS build). iosUrlScheme is required for the sign-in sheet to hand control back to your app on iOS — without it, the plugin only logs a warning.

Google Cloud setup

Create OAuth 2.0 client IDs in Google Cloud Console:

  1. Web client — its ID is webClientId. It becomes the aud claim of the ID token this module returns, on both platforms.
  2. iOS client — its ID is iosClientId. Its reversed ID is iosUrlScheme (com.googleusercontent.apps.…, shown as "URL scheme" on the client's page in the console).
  3. Android client — registered with your app's package name and signing SHA-1. There is no iosClientId-equivalent to pass at runtime for Android: Google identifies the calling app by package name + SHA-1 on this client, and the ID token's audience is the web client ID passed as webClientId. See docs/ARCHITECTURE.md → "Platform mechanics" for the full rationale.

[!WARNING] Play App Signing re-signs your app, so the Android OAuth client needs both the upload-key SHA-1 and the Play app-signing SHA-1. If you only register the upload-key SHA-1, sign-in fails only for builds installed from Google Play — usually surfacing as NO_GOOGLE_ACCOUNT even though the device has one. Also register your debug keystore's and any EAS build profile's SHA-1s, or sign-in won't work in development builds either.

Use placeholders like YOUR_WEB_CLIENT_ID.apps.googleusercontent.com when configuring — never commit a real client ID, SHA-1 or keystore (this repo is public).

Usage

import { isErrorWithCode, signIn, signOut } from "expo-native-google-signin";

async function handleSignIn() {
  try {
    const result = await signIn({
      webClientId: "YOUR_WEB_CLIENT_ID.apps.googleusercontent.com",
      iosClientId: "YOUR_IOS_CLIENT_ID.apps.googleusercontent.com", // required on iOS only
    });

    if (result.type === "cancelled") {
      return;
    }

    // result.type === "success"
    console.log(result.user.email, result.idToken);
    // There is no `result.user.id` -- decode `result.idToken` (a JWT) and read its `sub`
    // claim if you need the stable Google account identifier.
  } catch (error) {
    if (isErrorWithCode(error)) {
      console.warn(error.code, error.message);
    } else {
      throw error;
    }
  }
}

async function handleSignOut() {
  await signOut();
}

API reference

signIn(options): Promise<SignInResult>

| Option | Type | Required | Notes | | ------------- | -------- | ------------------ | -------------------------------------------------------------| | webClientId | string | Yes (both platforms) | OAuth web client ID; becomes the token's aud claim. | | iosClientId | string | iOS only | OAuth iOS client ID; ignored on Android. | | nonce | string | No | Forwarded to the native SDK and embedded in the ID token. |

type SignInResult =
  | { type: "success"; idToken: string; user: GoogleUser }
  | { type: "cancelled" };

type GoogleUser = {
  email: string;
  name?: string;
  givenName?: string;
  familyName?: string;
  photoUrl?: string;
};

There is deliberately no user.id: Android's GoogleIdTokenCredential.id is the account's email address, not the Google subject. Decode idToken and read its sub claim instead.

A user-initiated cancel resolves { type: "cancelled" } — it does not reject.

signOut(): Promise<void>

Clears the locally cached Google credential state on both platforms.

isErrorWithCode(error): error is Error & { code: ErrorCode }

Narrows a caught error to one with a known code from ERROR_CODES below.

ERROR_CODES

signIn/signOut reject with an Error whose code is one of these. The native error message is always preserved on error.message for logging.

| Code | When | Platform(s) | | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------| ----------- | | CONFIGURATION_ERROR | Missing/blank webClientId; missing/blank iosClientId on iOS; an invalid nonce type; or the module is unavailable (e.g. running in Expo Go instead of a development build) | Both | | NO_GOOGLE_ACCOUNT | No Google account is available to choose from (Credential Manager's NoCredentialException) | Android only | | PROVIDER_UNAVAILABLE | The credential provider isn't configured or supported on the device (e.g. no Play services, or an emulator without a Play Store image) | Android only | | NO_PRESENTER | No current Activity (Android) / view controller (iOS) to present the sign-in UI from — e.g. the app was backgrounded mid-tap | Both | | UNEXPECTED_CREDENTIAL | The returned credential isn't a parseable Google ID token credential, or is missing a required field (e.g. no email/profile on iOS) | Both | | SIGN_IN_FAILED | Any other failure. On iOS, every non-cancel failure uses this one code — GoogleSignIn has a single flat error domain that doesn't distinguish "no account" or "provider unavailable" the way Android's Credential Manager does. The native domain, code and message are always kept in error.message. On Android, [16] Account reauth failed (an unregistered package + signing SHA-1) rejects with SIGN_IN_FAILED rather than resolving as cancelled. | Both |

Backends

Supabase — signInWithIdToken with a nonce

Verifying the nonce ties the ID token to this specific sign-in attempt. Generate a raw nonce, SHA-256-hash it, pass the hashed nonce into signIn, and pass the raw nonce to Supabase (it hashes it again internally to compare against the token's nonce claim):

import * as Crypto from "expo-crypto";
import { signIn } from "expo-native-google-signin";
import { createClient } from "@supabase/supabase-js";

const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY);

async function signInWithGoogle() {
  const rawNonce = Crypto.randomUUID();
  const hashedNonce = await Crypto.digestStringAsync(
    Crypto.CryptoDigestAlgorithm.SHA256,
    rawNonce,
  );

  const result = await signIn({
    webClientId: "YOUR_WEB_CLIENT_ID.apps.googleusercontent.com",
    iosClientId: "YOUR_IOS_CLIENT_ID.apps.googleusercontent.com",
    nonce: hashedNonce,
  });

  if (result.type === "cancelled") {
    return;
  }

  const { data, error } = await supabase.auth.signInWithIdToken({
    provider: "google",
    token: result.idToken,
    nonce: rawNonce,
  });
}

(expo-crypto and @supabase/supabase-js are not dependencies of this module — install them yourself if you use this pattern.)

Firebase

import { getAuth, GoogleAuthProvider, signInWithCredential } from "firebase/auth";

const credential = GoogleAuthProvider.credential(result.idToken);
await signInWithCredential(getAuth(), credential);

A custom server

Verify, on the server: the ID token's signature (against Google's published JWKS), aud (must equal your web client ID), iss (accounts.google.com / https://accounts.google.com), exp, and — if you passed one to signIn — the (hashed) nonce claim.

Migrating from @react-native-google-signin/google-signin

| @react-native-google-signin/google-signin | expo-native-google-signin | | ---------------------------------------------------------------------| ----------------------------------------------------------------------------------------------| | GoogleSignin.configure({ webClientId, iosClientId }) then GoogleSignin.signIn() | One call: signIn({ webClientId, iosClientId, nonce? }) — no separate configure step | | Result { type: "success" \| "cancelled", ... } | Same type values and meaning — a cancel resolves, it doesn't throw | | user.id (Google subject) | No id — decode idToken and read its sub claim instead | | androidClientId config option | Removed — not needed; Google matches by package name + SHA-1, and webClientId is the token audience on both platforms | | GoogleSignin.signOut() | signOut() | | Config plugin option iosUrlScheme | Same option name, same purpose |

This module exists because the free version of @react-native-google-signin/google-signin stays on the legacy, deprecated Android sign-in API — its Credential Manager support is part of a paid product (see docs/ARCHITECTURE.md → "Why this project exists").

Troubleshooting

  • NO_GOOGLE_ACCOUNT only on builds installed from Google Play: the Android OAuth client is missing the Play app-signing SHA-1 (see the warning above) — register both SHA-1s.
  • SIGN_IN_FAILED with [16] Account reauth failed on Android: the Android OAuth client is missing this build's signing SHA-1 (debug, upload or Play app-signing). An Android OAuth client holds one SHA-1, so register one client per key, with the same package name, in the same Google Cloud project as webClientId.
  • No accounts on an emulator: use an emulator image that includes the Play Store, and sign into a Google account on it first.
  • CONFIGURATION_ERROR in Expo Go: this module requires a development build; Expo Go can't load it.
  • iOS never returns to the app after choosing an account: the config plugin's iosUrlScheme is missing or wrong — check Info.plist's CFBundleURLTypes after expo prebuild.
  • iOS build fails to resolve packages / Xcode errors: this module (via Expo SDK 57) requires Xcode 26+.

Example app

cd example
cp .env.example .env
# fill in EXPO_PUBLIC_WEB_CLIENT_ID, EXPO_PUBLIC_IOS_CLIENT_ID, EXPO_PUBLIC_IOS_URL_SCHEME
npm install
npx expo run:android   # or: npx expo run:ios

Contributing / releasing (maintainers)

See CLAUDE.md for the full command reference (npm run build, lint, typecheck, test, build:plugin, test:plugin, etc.).

Release:

  1. Bump the version in package.json (and android/build.gradle's version/versionName, which is not read from package.json), and add a CHANGELOG.md entry.
  2. Merge to main.
  3. Tag vX.Y.Z from main and push the tag — release.yml publishes to npm with provenance (prereleases go to the next dist-tag, stable versions to latest), but only if the tag matches package.json's version and the tagged commit is on main.

Auth: release.yml publishes only through npm trusted publishing (OIDC), configured on npmjs.com: package settings → Trusted Publisher → GitHub Actions, repo ele-ap/expo-native-google-signin, workflow release.yml, environment npm. There is no npm token or NPM_TOKEN secret, and you shouldn't add one. If you rename the workflow file or the environment, update the npmjs.com config too, or publishing fails with ENEEDAUTH. The first version (0.1.0-beta.0) was published manually with 2FA, because npm only lets a 2FA-bypassing token publish to a staging area for a package that doesn't exist yet (E_STAGE_REQUIRED).

Disclaimer

This project is not affiliated with or endorsed by Google.

License

MIT