This one is confusing because nothing looks broken. The app builds, installs and runs. Sign-in works on your phone. Then people install it from Google Play, and sign-in fails. Or the map is a grey grid. Or notifications stop.
Google services identify an Android app by two things: its package name and the SHA fingerprint of the certificate that signed it. If the fingerprint they know doesn't match the build in the user's hands, the registration doesn't match.
A store app has three keys, not one
| Key | Signs | Where to get its fingerprint |
|---|---|---|
| Debug key | Builds you run from your IDE | ./gradlew signingReport |
| Upload key | Release builds you upload to Play |
keytool -list -v on your upload keystore |
| App signing key | What Play users actually download | Play Console, App signing page |
Most people register one SHA-1, the debug key on their own machine, and stop there. That is why sign-in works on their phone.
Under Play App Signing, Google re-signs your app with the app signing key before users download it. Google's client authentication guide notes that the upload certificate differs from the app signing certificate. So a build from Play is signed by a key your Firebase project has never heard of.
The symptom
On Android, Google Sign-In uses an OAuth client created with the package name and SHA-1. When those don't match the installed app, you get:
ApiException: 10
Google's reference lists status 10 as DEVELOPER_ERROR: the app is misconfigured. It is not a bug in your Dart code, so don't go looking for one there.
Maps keys fail the same way. Android API keys are restricted by package name plus SHA-1, so a key that only knows your debug fingerprint rejects the Play build.
Getting all three fingerprints
# Debug (and any other variants Gradle knows about)
cd android && ./gradlew signingReport
# Upload key: prints SHA-1 and SHA-256
keytool -list -v -keystore /absolute/path/to/upload-keystore.jks -alias upload
For the app signing key, open Play Console, go to your app, then the App signing page. The SHA-1 and SHA-256 of the app signing key certificate are listed there.
Registering them
In Firebase, open Project settings, select the Android app, and add each fingerprint in the SHA certificate fingerprints field.
Add both SHA-1 and SHA-256 for each key. Some features want SHA-256 specifically: App Check's Play Integrity provider asks for it, and phone authentication uses it for Play Integrity too. Add both hashes for all three keys and you won't have to come back.
Two cases that catch people later
- Upload key reset. If you lose the upload keystore and Google resets it, the upload fingerprint changes. Register the new one.
- Package name change. A new package name means a new Firebase Android app, so all three fingerprints need adding again there.
The checklist
- Debug SHA-1 and SHA-256: registered
- Upload key SHA-1 and SHA-256: registered
- App signing key SHA-1 and SHA-256 (from Play Console): registered
- Test sign-in on a build installed from a Play testing track, not only from your IDE
The last step is the one that actually proves it.
Further reading: package names, signing, lost keystores and iOS provisioning are covered in Flutter package name and app signing on our blog.
Did ApiException: 10 ever cost you an afternoon?
Top comments (0)