Troubleshooting
webClientId is "autoDetect" but default_web_client_id was not found (Android)
- Add
google-services.jsontoandroid/app/ - Apply Google Services Gradle plugin (Android setup)
- Ensure JSON
package_namematches yourapplicationId - On Expo: set
expo.android.googleServicesFileand re-runprebuild --clean
Android default_web_client_id was not found
You use webClientId: 'autoDetect' but Gradle did not generate the string resource:
- Add
google-services.jsonunderandroid/app/with matchingpackage_name. - Add Google Services Gradle plugin (root
classpath+ appapply plugin). - Rebuild the app (not just reload Metro).
Or pass an explicit Web client ID instead of 'autoDetect'.
iOS sign-in stuck after browser / OAuth redirect
Interactive sign-in opens a browser or account sheet, then should return to your app. If it hangs:
- Confirm URL scheme =
REVERSED_CLIENT_IDfrom plist (iOS setup). - On bare React Native, add
GIDSignIn.sharedInstance.handle(url)inAppDelegate(details). - If you use Facebook or other
openURLhandlers, chain them with||before/after Google's handler.
iOS URL scheme / redirect errors (App crash on button tap)
If the app crashes immediately upon tapping the Google Sign-In button with a stack trace in HybridNitroGoogleSignin.signIn or an NSInvalidArgumentException stating Your app is missing support for the following URL schemes: com.googleusercontent.apps.XXXX, the custom URL scheme is missing:
- Required for both Simulators and Real Devices: This is a mandatory step for all environments because iOS needs the custom scheme to route the browser redirect back into your app.
- Add the
REVERSED_CLIENT_ID(e.g.com.googleusercontent.apps.XXXX) as a URL scheme inInfo.plist(underCFBundleURLTypes) or in Xcode -> Targets -> Info -> URL Types. - Expo: include
GoogleService-Info.plistoriosUrlSchemein the plugin configuration. - Re-run prebuild (
npx expo prebuild --clean) and compile the app again after changing the URL schemes.
iOS pod install fails — AppCheckCore / RecaptchaInterop (Expo 56+)
pod install or expo prebuild may fail with:
The Swift pod `AppCheckCore` depends upon `GoogleUtilities` and `RecaptchaInterop`,
which do not define modules. To opt into those targets generating module maps...
This happens when CocoaPods resolves AppCheckCore ≥ 11.3 (from Google Sign-In 9.2+ and/or @react-native-firebase/app-check) under Expo’s default static pod setup.
A related failure with Firebase App Check looks like:
CocoaPods could not find compatible versions for pod "AppCheckCore":
AppCheckCore (< 11.3.0)
AppCheckCore (~> 11.3)
Do not pin AppCheckCore below 11.3.0 — that conflicts with Firebase App Check. Use modular headers instead (below).
Why not NitroGoogleSignin.podspec?
CocoaPods does not let a podspec enable :modular_headers => true on dependencies — that option exists only in the app Podfile (or via a config plugin that edits it).
We also cannot add AppCheckCore as a direct s.dependency in the podspec: NitroGoogleSignin is a Swift pod, and CocoaPods then reports that AppCheckCore does not define modules (same class of error).
So the library handles this in two places:
| App type | Who patches the Podfile |
|---|---|
| Expo | react-native-nitro-google-signin config plugin (automatic on expo prebuild) |
| Bare RN | You add the pods below to ios/Podfile |
Expo (dev client)
The config plugin patches ios/Podfile during prebuild (modular headers for AppCheckCore, GoogleUtilities, and RecaptchaInterop — no AppCheckCore version pin). Ensure the plugin is listed in app.config.js, upgrade react-native-nitro-google-signin, then regenerate native projects:
npx expo prebuild --clean
No manual Podfile edits are needed for Expo.
Bare React Native
Add the following inside your app target block in ios/Podfile, before use_native_modules!:
# react-native-nitro-google-signin: AppCheckCore ≥ 11.3 + static CocoaPods needs modular headers.
pod 'AppCheckCore', :modular_headers => true
pod 'GoogleUtilities', :modular_headers => true
pod 'RecaptchaInterop', :modular_headers => true
Then reinstall pods from your app root:
bundle exec pod install --project-directory="ios"
See also Expo setup and iOS setup.
Release builds / R8 / ProGuard
Sign-in works in debug but crashes or no-ops in release after enabling minifyEnabled true:
- Consumer rules —
react-native-nitro-google-signinshipsconsumer-rules.prowith the AAR. Upgrade the package instead of copying keeps manually (Android setup — ProGuard / R8). - Do not add blanket
-keep class androidx.** { *; }for Credential Manager —androidx.credentialsandcredentials-play-services-authalready embed the rules they need. - Peer — install and rebuild with
react-native-nitro-modules(Nitro JNI). - Verify — reproduce on
assembleReleaseor your store build, then inspect logcat forClassNotFoundException/ missingHybridNitroGoogleSignin.
DEVELOPER_ERROR / sign-in fails immediately (Android)
- Wrong SHA-1 (or missing SHA-256 in Firebase) on the Android OAuth client
- Package name mismatch
- Android OAuth client ID passed to
configure()instead of the Web client ID — Credential ManagersetServerClientIdmust use the Web client (*.apps.googleusercontent.com) - Web client ID from a different Google Cloud project than the Android client
See Android — Credential Manager checklist.
type: 'cancelled' after picking a Google account (Android production)
Credential Manager often reports OAuth misconfiguration as user cancellation, not DEVELOPER_ERROR.
Symptom: Account picker / Sign in with Google UI appears → user selects an account → JS receives type: 'cancelled' (not a thrown error). Works in debug, fails on release / Play Store builds.
Cause: Google validates the app’s signing certificate against the Android OAuth client. Debug builds use the debug keystore SHA-1; Play builds use the Play App Signing certificate (different from your upload keystore). If that SHA-1 is missing, Play services closes the flow with RESULT_CANCELED, which this library maps to type: 'cancelled'.
Fix:
- Open Play Console → App integrity → App signing and copy the App signing key certificate SHA-1 (and SHA-256 if you use Firebase).
- Also register your upload key SHA-1 if you test local
assembleRelease/ sideloaded release APKs. - Add those fingerprints to the Android OAuth client (same package name as
applicationId) in Google Cloud Console and/or Firebase Project settings. - Confirm
configure({ webClientId })uses the Web client ID from the same Google Cloud project — never the Android client ID. - Rebuild and reinstall the production/release binary (Metro reload is not enough). Changes to OAuth clients can take a few minutes to propagate.
Logcat will include a GoogleSignInController warning when Credential Manager returns cancellation, pointing at SHA-1 checks.
See Android SHA-1 and DEVELOPER_ERROR.
Nitro / TurboModule not found
- Rebuild native app after installing the package
- Expo: create a new dev client (
expo prebuild --clean+expo run:ios|android) - Confirm
react-native-nitro-modulesis installed
Expo config plugin errors
Missing iosUrlScheme:
Configure either Firebase files or manual scheme:
["react-native-nitro-google-signin", { "iosUrlScheme": "com.googleusercontent.apps...." }]
Play Services (Android)
Call checkPlayServices() and use an emulator image with Google Play.
Platform-specific behavior
Platform differences for signOut(), revokeAccess(), hostedDomain, and scope requests are documented in Usage and the API reference.
Google Workspace: hostedDomain rejected on button flow (Android)
When hostedDomain is configured and you use presentExplicitSignIn() or GoogleSignInButton with signInBehavior="buttonFlow", sign-in fails if the signed-in account's JWT hd claim does not match the configured domain (for example a consumer @gmail.com account when you require corp.example.com).
Credential Manager flows (signIn, createAccount, signInBehavior="credentialManager") apply the domain filter during the request.
Always validate hd on your backend — client-side checks are not sufficient on their own.
iOS: revokeAccess throws "does not match the current signed-in user"
On iOS, revokeAccess(emailOrUniqueId) only disconnects the active session. If you pass an email or id that is not the currently signed-in user, the call throws.
Call signIn() (or ensure the target account is the current user) before revokeAccess(), or use signOut() for a local session clear.
iOS: Missing serverAuthCode on silent signIn() with offlineAccess: true
Even when offlineAccess: true is set, iOS silent signIn() and restorePreviousSignIn() return serverAuthCode: null. This is expected — Google only issues a server auth code during an interactive authorization flow.
Use createAccount() or presentExplicitSignIn() for the initial offline grant.
Android: Sign-in hangs after enabling offlineAccess: true
If sign-in (imperative API or useGoogleSignInFromButton) appears to hang after you add offlineAccess: true to configure(), update to a release that no longer forces AuthorizationRequest.Prompt.CONSENT on every offline authorization. Forcing consent immediately after Credential Manager could leave the PendingIntent unresolved.
Also ensure:
- You rebuild the native app after upgrading the library (Metro reload is not enough).
- The Web client ID in
configure({ webClientId })is correct (not the Android client ID). - Debug/release/Play App Signing SHA-1 fingerprints are registered on the Android OAuth client.
Android: requestScopes() returns { serverAuthCode: null } with no consent UI
serverAuthCode is always null unless configure({ offlineAccess: true }). That is expected when you only need on-device API access.
Use the accessToken field from requestScopes() to call Google APIs (Drive, Calendar, etc.):
const { accessToken, serverAuthCode } = await GoogleOneTapSignIn.requestScopes([
'https://www.googleapis.com/auth/drive.file',
])
// accessToken — call Google APIs from the device
// serverAuthCode — only non-null when offlineAccess: true
If consent still does not appear for a new scope:
- Confirm the user signed in successfully first (imperative or hook).
- Add the scope on the OAuth consent screen and enable the related Google API.
- Rebuild the native app after upgrading this library.
iOS: Missing serverAuthCode when scopes are already granted
On iOS, when calling requestScopes() or signIn() with scopes that the user has already approved in the past, the native iOS SDK returns a local error scopesAlreadyGranted (-8) and does not contact Google's authorization servers. As a result, no new one-time serverAuthCode is issued.
How to resolve:
- Force re-authorization via
presentExplicitSignIn(): Configure the library without the specific scopes (or with only basic scopes) and callpresentExplicitSignIn(). This forces the account chooser browser sheet to display. Since Google OAuth supports incremental/cumulative consent, when your backend exchanges this freshserverAuthCodefor a refresh token, Google will issue a token containing all scopes the user has previously authorized. - Or call
signOut()first: CallingsignOut()resets the SDK's local keychain state, forcing the next sign-in flow to contact Google's servers and request a freshserverAuthCode.
getTokens() returns the same access token on iOS
On iOS, getTokens() returns the current in-memory access token while it is still valid. Google only issues a new access token when the current one is expired or marked for refresh.
Expected: Two consecutive getTokens() calls without clearing may return the same access token string.
To force a refresh (for example after a Google API returns 401):
- Call
clearCachedAccessToken(accessToken)with the token that failed. - Call
getTokens()again — iOS performs a network refresh via AppAuth.
On Android, clearCachedAccessToken() removes the token from AuthorizationClient's local cache; the next getTokens() requests a new one from Google.
Android: GoogleSignInButton is laid out but invisible
On some Android 15+/Play services combinations the old GMS SignInButton Dynamite remote view could measure at the correct size and still paint nothing.
This library draws the Android button in-process with Play services branding assets. Upgrade react-native-nitro-google-signin and rebuild the native app (Metro reload is not enough).
If the button collapses to 0×0 under the New Architecture without an explicit style, ensure you are on a release that applies intrinsic size from the size prop (or set style yourself using GOOGLE_SIGN_IN_BUTTON_HEIGHT / GOOGLE_SIGN_IN_BUTTON_WIDTH).
Still stuck?
Open an issue with platform, RN version, Expo vs bare, and whether you use autoDetect or an explicit webClientId: