DEV Community

Sagnikpal
Sagnikpal

Posted on

I Built a Free, Modern Alternative to Paid Universal Sign-In for Expo

If you've tried adding Google sign-in to a React Native app recently, you've probably ended up at @react-native-google-signin/google-signin.

It is the de-facto standard. It works. It also has a paid Universal Sign-In tier that uses Google's modern Credential Manager APIs on Android, while the free tier still wraps the older legacy Sign-In SDK.

That paywall is fair. The maintainers have built and maintained a serious package. But for many teams โ€” indie developers, side projects, MVPs, and Android-first Expo apps โ€” the gap still hurts.

So I built expo-google-credential-auth: an MIT-licensed, Android-first package built on the modern Credential Manager + Google Identity Services stack.

This is the comparison I wish existed when I was choosing between the available options.

Who this is for

TL;DR

  • Use paid Universal Sign-In if you need iOS today, SLA support, passkeys, or broader federated identity support.
  • Use expo-google-credential-auth if you're Android-first, want the modern Google auth stack for free, and prefer a smaller dependency you can audit end-to-end.

Use the paid Universal Sign-In product if you need iOS today, enterprise support, or passkey / federated sign-in flows beyond Google. It is a mature commercial product and a sensible choice for production apps that need full coverage immediately.

Use expo-google-credential-auth if you're shipping Android first, want the modern Credential Manager stack without a license fee, want a small dependency you can read end-to-end, and can wait for iOS and web support.

Both options rely on modern Google APIs on Android. The real question is not which one is universally better. It is which one fits your product right now.

Comparison table

Feature google-signin free google-signin paid expo-google-credential-auth
Android SDK Legacy Google Sign-In SDK Modern Credential Manager Modern Credential Manager
iOS support โœ… Yes โœ… Yes ๐ŸŸก Coming soon
Web support Separate flow โœ… Yes ๐ŸŸก Coming soon
License MIT Commercial MIT
Auth and authorization separation Conflated into one call Conflated into one call Explicit separate flows
Cancellation handling Throws exception Throws exception Returns discriminated union
Revoke clears Play Services caches Partial Yes Yes
Native code auditability Large codebase Large codebase + closed paid bits Small focused Kotlin module
Bundle impact Larger Larger Smaller focused module
Support model Community / paid tier Commercial SLA GitHub issues, best effort

A table alone does not explain the trade-offs. These are the four differences that matter most in practice.

1. Authentication and authorization should be separate flows

This is the design decision I care about most.

Many Google sign-in integrations blur two different OAuth concepts into one signIn() call. That call often returns an idToken and sometimes an accessToken, which encourages developers to treat them as interchangeable.

They are not interchangeable.

Concept Authentication Authorization
Question answered Who is this user? What can my app do on their behalf?
Token type ID token Access token
Typical use Verify user identity on your backend Call Google APIs
Revocable? No, it expires naturally Yes
User interface Account picker Consent screen

Common bug: the /userinfo 401 usually happens when an app sends an ID token to an endpoint that expects an access token.

In expo-google-credential-auth, authentication and authorization are separate calls:

// Authentication โ€” for your backend
const { idToken, user } = await GoogleAuth.signIn();
Enter fullscreen mode Exit fullscreen mode
// Authorization โ€” for Google APIs
const { accessToken } = await GoogleAuth.requestAuthorization({
  scopes: ['https://www.googleapis.com/auth/drive.readonly'],
});
Enter fullscreen mode Exit fullscreen mode

This means you can:

  • sign in without requesting API permissions,
  • request additional scopes later,
  • revoke API access without signing the user out,
  • model your app flow closer to the OAuth protocol underneath.

2. Cancellation is not an error

In many existing integrations, dismissing the sign-in sheet throws an exception:

try {
  await GoogleSignin.signIn();
} catch (e) {
  if (e.code === statusCodes.SIGN_IN_CANCELLED) {
    // Not actually an error
  } else if (e.code === statusCodes.NO_SAVED_CREDENTIAL_FOUND) {
    // Also not really an error
  } else {
    // Actual failure
  }
}
Enter fullscreen mode Exit fullscreen mode

A user closing a sheet is not exceptional. It is a normal branch in the user flow.

expo-google-credential-auth models this as a discriminated union:

const result = await GoogleAuth.signIn();

switch (result.type) {
  case 'success':
    return handleUser(result.idToken, result.user);

  case 'cancelled':
    return;

  case 'noSavedCredentialFound':
    return promptCreateAccount();
}
Enter fullscreen mode Exit fullscreen mode

Actual failures, such as network errors or configuration problems, can still throw. But normal user behavior stays in the return type, where TypeScript can narrow it properly.

3. Android revoke behavior needs more than one cleanup step

Most teams do not notice this until production: revoking access on the server is not always enough to clear local Android auth state.

A complete Android revoke flow needs to handle multiple layers:

  1. Server-side revocation

    Call Google's revoke endpoint to invalidate the grant.

  2. Credential Manager state

    Clear Credential Manager's local credential state.

  3. Play Services OAuth cache

    Clear sign-in and revoke state through Google Play Services APIs.

  4. Android AccountManager token cache

    Invalidate the underlying Android OAuth token cache.

Each cleanup step should be best-effort and isolated. If one layer fails, the others should still run.

The implementation in expo-google-credential-auth is intentionally small and auditable. The point is not that the code is clever. The point is that the auth behavior is visible instead of hidden.

4. Small dependencies are easier to trust

This is more subjective, but it matters for auth libraries.

When authentication breaks, you want to understand the dependency. You want to trace what native code does. You want to audit the token flow. You want to know whether the package is doing something surprising.

expo-google-credential-auth is intentionally focused: a small Kotlin implementation with thin TypeScript bindings.

Small is a feature when the package sits on your login path.

When you should still pay

The paid Universal Sign-In tier is still the right choice if:

  • You need iOS in production today. expo-google-credential-auth is Android-first right now.
  • You need passkeys or non-Google federated identity. This package focuses only on Google auth.
  • You need commercial support or an SLA. GitHub issues are not a substitute for vendor support.
  • Your current setup already works. Migration risk is real. Do not rewrite working auth code for a marginal improvement.

This is not a "never pay" argument. It is a "choose the right trade-off" argument.

For many Android-first Expo apps, free + focused + modern is enough.

Try it

Install the package:

npm install expo-google-credential-auth
Enter fullscreen mode Exit fullscreen mode

You will need an Android development build because Credential Manager requires native code. It will not work inside Expo Go.

The setup guide and Android configuration notes are available in the npm README.

Final thoughts

Google sign-in in React Native has historically been harder than it should be, especially in Expo projects that want to use modern Android APIs without paying for a commercial tier.

expo-google-credential-auth is my attempt to make that path smaller, clearer, and free.

If you're building an Android-first Expo app and want modern Google auth without a license fee, try it and see whether it fits your project.


๐Ÿ“ฆ Package: expo-google-credential-auth
๐Ÿ’ป Source & issues: https://github.com/sagnik2001/expo-google-credential-auth

About me: I build mobile and developer tools. Find me on GitHub ยท LinkedIn.

If this post saved you time, a โญ on the repo or a follow here on Dev.to means a lot.

Top comments (0)