Why Google Sign-In Works on Your Emulator But Breaks on Play Store — and How to Fix It in 5 Minutes
Suggested tags: #Flutter, #Android, #GoogleSignIn, #PlayStore, #OAuth, #DebuggingDiaries
Reading time: ~6 minutes
The Bug That Only Shows Up After You Ship
Picture this: you've spent two weeks wiring Google Sign-In into your
Android app. It works perfectly on your emulator. It works on your
teammate's Pixel. The QA build, side-loaded over USB, works. You ship a
release build to Google Play, hit the green publish button, and crack open
a celebratory chai.
Five minutes later, a tester pings you:
"Hey, I downloaded the app from Play Store and Google Sign-In just…
doesn't do anything. Account picker doesn't even open."
You blink. You check your phone. You uninstall the dev build, install from
Play Store… and watch the same thing happen. The exact same APK that
worked thirty minutes ago is now broken.
Welcome to one of Android's most confusing OAuth bugs. The good news?
There is one specific cause, and it takes about five minutes to fix once
you know what to do. The bad news is that almost no one finds the answer
on the first Stack Overflow page they read, because the symptoms point at
ten different culprits.
This article walks through what's actually going wrong, why it only
happens on Play Store builds, and the precise fix that resolves it without
a single line of code change or a new release upload.
Three Signatures, One Confused OAuth Client
Here's the thing nobody tells you when you set up Google Sign-In: your
Android app gets signed with at least three different cryptographic
keys during its lifetime, and Google checks all of them.
SHA-1 fingerprint
─────────────────
[1] flutter run / debug builds → debug key AA:BB:CC:...
[2] flutter build appbundle → upload key DD:EE:FF:...
[3] Install from Play Store → Google's 11:22:33:...
app signing
key
When you run your app from Android Studio or flutter run, the APK is
signed with your machine's local debug keystore. That's signature [1].
When you build a release AAB and upload it to Play Store, you sign it with
your upload keystore — usually one you generated when you first set up
Play signing. That's signature [2].
And here's the kicker: Google does not actually distribute the AAB you
uploaded. Since 2021, Play Console mandates a feature called App
signing by Google Play. When you upload your .aab, Google strips your
signature, repackages the bundle into APKs for different device profiles,
and re-signs them with their own internal key. That's signature [3].
You never see this key. You can't access it. It lives entirely on
Google's servers.
So when a real user installs your app from the Play Store, the APK
running on their phone is signed with a key your machine has never
touched.
Why does this matter for Google Sign-In? Because Google's OAuth servers
verify the signature of the calling app on every Sign-In request. The
SHA-1 fingerprint of whichever key signed the running APK has to match
one that's registered against your Android OAuth client in Google Cloud
Console.
When you set up Google Sign-In during development, you almost certainly
registered key 1 or key 2. You probably never
registered key [3], because Play hadn't generated it yet — the App
signing key is created the first time you upload an AAB.
That's the bug. Your Cloud Console only knows about [1] and/or [2].
The Play Store APK is signed with [3]. Google's OAuth servers see a key
they don't recognize and silently drop the request. From the user's
perspective, Sign-In just… does nothing.
Symptoms That Lead Everyone Astray
The reason this bug eats hours of debugging time is that the failure mode
is almost too clean. There's no crash. No red snackbar. No useful logcat
line that says "SHA-1 mismatch."
What you see instead:
- The Google account picker briefly flickers, then closes
- A toast saying "Sign-in failed" appears with no further detail
-
PlatformException(sign_in_failed, …, status: 12500)— but only if you're already running throughadb logcat - Sometimes status code
10, which is documented asDEVELOPER_ERRORand is even more cryptic than12500
Status code 12500 and 10 are the two fingerprints of this specific
bug. If you grep your logcat output and see either, stop reading Stack
Overflow threads about scopes, plugin versions, or token expiry — you
have a SHA-1 problem.
The other reason people miss this for so long: everything works in
development. Emulator? Works. Local USB build? Works. Internal testing
track if you side-load the APK from a download link? Works. The bug
only appears once Google's app-signing pipeline gets involved, and that
only happens when a real user clicks Install on the Play Store listing.
The Fix, Plain and Simple
Now that we've named the cause, the fix is mechanical. There are exactly
two pieces:
- Find out what SHA-1 Google's app-signing key has (this lives in Play Console)
- Tell Google's OAuth servers about it (by registering it on a new Android OAuth client in Cloud Console)
No code changes. No rebuild. No new release upload. The whole thing is
configuration on Google's side, and the change propagates to your live
Play Store build within minutes.
Step 1 — Get the App Signing key SHA-1 from Play Console
Open Play Console, select your app,
and navigate:
Test and release → App integrity → App signing
You'll see two certificates listed:
- App signing key certificate ← copy the SHA-1 from this one
- Upload key certificate ← not this one
The distinction matters. The Upload key is yours; the App signing key is
Google's. Only the App signing key SHA-1 fixes this bug. Both certificates
are listed on the same page, which is exactly why people copy the wrong
one.
The SHA-1 looks something like:
9A:04:19:E7:48:48:3A:70:6B:5D:5B:h1:28:91:36:5C:B1:4C:3E:D7
40 hexadecimal characters separated by colons. Copy it.
Step 2 — Register a new Android OAuth client with that SHA-1
Open Google Cloud Console, make sure
the project selector at the top points to the same Cloud project your
app uses for Sign-In. (This is a common pitfall — if your team has
multiple Cloud projects, getting this wrong silently breaks everything.)
Then:
APIs & Services → Credentials → + CREATE CREDENTIALS → OAuth client ID
Fill in the form:
| Field | Value |
|---|---|
| Application type | Android |
| Name |
<your-app> (play signing) — anything descriptive |
| Package name | The exact applicationId from your build.gradle
|
| SHA-1 certificate fingerprint | The one you copied from Play Console |
Click Create.
You will now have two Android OAuth clients with the same package name
in your project — one with your debug/upload SHA-1, one with the Play
App Signing SHA-1. Don't delete the original. Both serve a purpose.
Google's OAuth servers accept whichever SHA-1 matches the running build,
so this configuration covers all three signing keys at once.
The reason you need two separate clients is a quirk of Cloud Console:
each Android OAuth client allows exactly one SHA-1. Firebase users get a
nicer UI here (multiple fingerprints per app), but if you're working with
the Cloud Console directly, the workaround is to make a second client.
Step 3 — Make sure your OAuth consent screen is published
This is the secondary cause that bites about a third of teams who fix
the SHA-1 issue. Even with a perfect SHA-1 setup, if your OAuth consent
screen is in Testing mode, only emails on the test users list can
sign in. Everyone else gets an error indistinguishable from the SHA-1
bug.
Navigate:
APIs & Services → OAuth consent screen → Audience
(Google recently renamed parts of this UI to Google Auth Platform; the
location of the Audience sub-page is the same.)
Look at Publishing status. If it says Testing, click PUBLISH
APP and confirm. For apps requesting only basic scopes (email,
profile, openid), publishing is instant. For apps requesting
sensitive or restricted scopes, Google will ask for verification, which
can take days — but Sign-In flows for typical apps don't need those
scopes.
Step 4 — Verify your Flutter code uses the Web client ID
This one isn't strictly part of the fix, but it's worth confirming. The
google_sign_in plugin needs the Web OAuth client ID to issue ID
tokens, not the Android client ID. If your code has the Android
client ID in serverClientId, the account picker will work but the
backend ID-token verification will fail.
Open the file where you initialize GoogleSignIn:
// Should look something like this — the ID belongs to a Web-type client.
const _googleWebClientId =
'338935556969-k446g8gdgbdxgbdgvdefmgr06rhs5.apps.googleusercontent.com';
Cross-reference it with Cloud Console. The corresponding OAuth client
should be type Web application, not Android. If it's Android, that's
your second bug.
Step 5 — Test
This is the part that surprises people the most: you don't need to
rebuild. The fix is purely server-side on Google's auth infrastructure.
Wait 5 to 10 minutes for propagation, then:
- Uninstall the Play Store version of your app from a real phone
- Re-install from Play Store (Internal testing track is fine)
- Open the app, tap Google Sign-In, pick an account
It should now work end-to-end. The same .aab you uploaded yesterday now
authenticates correctly because Google's OAuth servers learned a new
SHA-1.
How to Verify You Actually Fixed It
After ten minutes of normal use, return to your new OAuth client in Cloud
Console:
APIs & Services → Credentials → click your "(play signing)" client
In the right-hand panel you'll see a Last used date field:
Last used date May 7, 2026 (Note: this data could be delayed by a day
or more.)
If it shows today, congratulations: your Play Store APK is hitting this
exact OAuth client and the SHA-1 is matching successfully. That timestamp
is the single strongest signal that the fix worked. Until I saw it on my
own client, I couldn't quite believe a config change was enough — and
neither will the next person you tell about this fix.
Pitfalls Worth Calling Out
A few traps that snagged me along the way and might snag you too:
Don't copy the Upload key SHA-1. Both certificates are right next to
each other on the App signing page. Only the App signing key SHA-1
matters here.
Don't delete the old Android OAuth client. It still serves your
debug builds and any side-loaded local APKs. Two clients, same package
name, different SHA-1s, both alive — that's correct.
Make sure you're in the right Cloud project. Teams with multiple
Cloud projects (legacy ones, separate projects per environment) often
land on the wrong one. The Web client ID hard-coded in your Flutter app
tells you which project Sign-In is bound to — match it.
Don't try to "Link Cloud project" in Play Console looking for this
fix. That feature is for the Play Integrity API, which is unrelated to
Google Sign-In, and the most common error you'll see ("Project not
found") just means your account isn't an Owner on the Cloud project. You
don't need to link anything for Sign-In to work.
Don't pass the Android client ID as serverClientId. It has to be
the Web client ID. The plugin will sometimes appear to work and then
break in obscure ways during ID token verification.
A Lesson In Distributed Trust
The deeper takeaway here is that mobile app authentication, post-Play
App Signing, involves three trusted parties: your team's local
keystores, Google Play's signing infrastructure, and Google's identity
servers. They have to agree on the identity of your app, and the only
way they agree is via SHA-1 fingerprints registered against an OAuth
client.
It's elegant when it works. It's baffling when it doesn't, because the
machinery is invisible until something breaks. The first time you ship a
production Android app with Google Sign-In, this bug is almost
inevitable. The good news is that you only need to learn it once.
If you're setting up Google Sign-In on a new Android app, here's the
checklist I now run before the first Play Store release:
- One Android OAuth client with the debug keystore SHA-1
- One Android OAuth client with the upload keystore SHA-1 (if you ever share upload-signed APKs directly with testers)
- One Web OAuth client → use its Client ID as
serverClientId - OAuth consent screen → External, In production
- Upload first AAB, enable App signing, immediately read the App signing key SHA-1 from Play Console
- Create a third Android OAuth client with that SHA-1, same package name
- Confirm Sign-In works on Play Store install — Last used date updates within minutes
Tracking all the SHA-1s up front saves a release cycle of debugging
later, and you'll never wonder again why something that works on three
separate emulators doesn't work after Play touched it.
Closing
If this article saved you an evening, pass it on to whoever sets up
Sign-In on the next Android app at your company. The official Google
documentation describes most of these moving parts individually, but I
have yet to see them connected in one place — and the failure mode is
common enough that I suspect every Android team rediscovers it
independently.
Five minutes, two new lines in Cloud Console, no rebuild. That's all it
takes to turn "Sign-In is broken on Play Store" into a story you tell
junior devs at standup.
Top comments (0)