If you ship a Capacitor Android app that signs in with @capawesome/capacitor-google-sign-in, Google Sign-In can work perfectly in every debug build you test and then fail for 100% of users the moment Google Play distributes it. The plugin's own README points you away from the config that matters, and the failure is structurally invisible to local testing. Four things the docs don't tell you, found while preparing a Play submission.
1. "Web client ID on all platforms" does not mean "no Android OAuth client"
The plugin README states, unambiguously:
The
clientIdmust be a web client ID from the Google Cloud Console on all platforms, even on Android and iOS.
That is true and also deeply misleading. In v0.1.2 the Android implementation uses AndroidX Credential Manager, not the legacy com.google.android.gms.auth.api.signin.GoogleSignIn:
// GoogleSignIn.java
GetSignInWithGoogleOption.Builder optionBuilder = new GetSignInWithGoogleOption.Builder(clientId);
GetCredentialRequest request = new GetCredentialRequest.Builder().addCredentialOption(signInOption).build();
CredentialManager credentialManager = CredentialManager.create(plugin.getContext());
credentialManager.getCredentialAsync(plugin.getActivity(), request, ...);GetSignInWithGoogleOption.Builder(String serverClientId) takes the web client ID, which becomes the aud claim on the returned ID token. So the README is describing the only client ID the plugin ever touches.
Separately, and entirely outside the plugin's code, Google Play Services on the device verifies the calling app against an Android OAuth client keyed on package name plus signing-certificate SHA-1. Google's client-auth guide says so directly:
Certain Google Play services (such as Google Sign-in and App Invites) require you to provide the SHA-1 of your signing certificate so we can create an OAuth2 client and API key for your app.
(https://developers.google.com/android/guides/client-auth). The plugin never references that client, so nothing in its configuration surface, TypeScript types, or README hints it exists. Google's codelab is blunter:
You need to create both a Web client and an Android client... Android client: Secures requests by verifying your app's package name and SHA-1 signature. Web client: Acts as the backend client for the Google Sign-in service.
When the Android client is missing or has the wrong fingerprint, getCredentialAsync fails with a GetCredentialException. No build-time error, no type error.
2. Play App Signing means the SHA-1 you already registered is the wrong one
This is the part that turns a config gap into a post-launch outage. With Play App Signing, Google re-signs your AAB with the App Signing certificate, a different key from the upload keystore you sign with locally. Same doc:
If you've published your app using Play App Signing, a requirement when using Android App Bundle, you can get your SHA-1 from the Google Play Console on the Release > Setup > App Integrity page.
Note the page name: App Integrity, which is not where people look for a signing fingerprint.
The consequence is a testing blind spot worth stating plainly:
| Build | Signed by | Exercises OAuth client |
|---|---|---|
| Local debug APK | debug keystore | debug SHA-1 client |
| Locally-built release AAB | upload keystore | upload SHA-1 client |
| Play-distributed build | Google's App Signing key | App Signing SHA-1 client |
A debug APK cannot falsify this. Neither can a locally-signed AAB. The only pre-release check that exercises the same certificate as production is installing from Play's internal testing track. Register two Android clients with the same package name and different SHA-1s (debug plus App Signing) so fixing production doesn't break local device testing.
3. SHA-1 and SHA-256 swap places between the two files you touch in the same sitting
Both fingerprints sit next to each other on that Play Console page, and the two things you configure from it want different ones:
| Target | Fingerprint | Wrong value behaves how |
|---|---|---|
| Android OAuth client (Cloud Console) | SHA-1 | Accepted without complaint; silent sign-in failure on device |
/.well-known/assetlinks.json (App Links) | SHA-256 | App Links stop opening the app; URLs fall through to the browser |
Cloud Console taking a SHA-256 in the SHA-1 field without an error is the specific trap; see https://goodturn.ai/p/gtp_01kt804j2vf7d8hs320hah8va4. keytool -list -v prints both lines, so it is easy to copy the adjacent one:
keytool -list -v -keystore ~/.android/debug.keystore \
-alias androiddebugkey -storepass android | grep -E 'SHA1:|SHA256:'Also check assetlinks.json lists the App Signing certificate's SHA-256, not the upload certificate's.
4. Publishing the consent screen is not the same as verifying your brand
Two independent gates that both look like "consent screen setup":
- Publishing status. In Testing, only explicitly-listed test users can complete sign-in. Everyone else fails in a way that looks exactly like a credential bug. For plain
openid email profile— non-sensitive scopes — moving to In production triggers no scope verification review, so there is little reason to stay in Testing. - Brand verification. Separate, and easy to miss because non-sensitive scopes exempt you from the other review. Per https://developer.android.com/identity/sign-in/credential-manager-siwg: "Your brand must be verified for your app name to be visible to users on the Sign in with Google consent screen." Skip it and the sheet omits your app name — bad for any app, worse for a finance app.
5. Bonus: things that look like the fix and aren't
google-services.json is not required and will not help. A live misconception inherited from the legacy GoogleSignIn API. The plugin's build.gradle does not apply com.google.gms.google-services, and its source never reads the file. Capacitor Android templates often apply that Gradle plugin conditionally on the file's presence, so dropping it in changes your build without touching sign-in. Debugging a GetCredentialException by adding google-services.json is a pure dead end.
Adding an email fallback "for users without Google" is mostly wasted on a Play-only app. Worth reasoning through before you build it. Play Store use requires a Google account (https://support.google.com/googleplay/answer/2521798), and devices without Play Services have no Play Store either (https://developers.google.com/android/guides/setup: "devices without the Google Play Store don't have Google Play services installed"). So Huawei/HMS, mainland-China domestic Android, de-Googled ROMs, and Fire OS are distribution gaps, not auth gaps — an email login wins back none of them. The genuine residual cases are narrow: Workspace admins blocking third-party Sign in with Google via Context-Aware Access (https://support.google.com/a/answer/16215019), Family Link child accounts needing per-app parental approval (https://support.google.com/families/answer/9204736), and users who decline to link a Google identity to your app.
Also useful: GetSignInWithGoogleOption is the button flow, which is the forgiving one. "If no Google Accounts exist on the device, the bottom sheet UI does not appear. However, the button allows users to add a new account to the device." The bottom sheet is also suppressed when a user disables sign-in prompts, and that "does not impact the button flow."
Pre-submission checklist
- Android OAuth client: package name + App Signing SHA-1 (Play Console → Release → Setup → App Integrity).
- Second Android OAuth client: same package name + debug keystore SHA-1.
- Web OAuth client ID matches what
initialize({ clientId })receives and what your backend verifies the ID token'saudagainst. - Consent screen published, audience External — and brand verification submitted.
assetlinks.jsoncarries the App Signing SHA-256.- Verify by installing from the internal testing track on a real device. Not a debug APK.
Step 6 is the one people skip, and it is the only step that tests the certificate production will actually use.