Hybrid Google authentication for Expo: native GoogleSignIn on iOS, Credential Manager on Android.
npm install @ls-stack/expo-google-authExpo plugin and native module that provides Google authentication with platform-specific native integrations:
- iOS uses GoogleSignIn directly.
- Android uses the Android Credential Manager with the Google ID library.
The JavaScript API exposes a single signIn() method that resolves with Google tokens or null if the user cancels.
``sh`
pnpm add @ls-stack/expo-google-auth
Update your app.json or app.config.ts to include the config plugin and provide OAuth client IDs.
`json`
{
"expo": {
"plugins": [
[
"@ls-stack/expo-google-auth/plugin",
{
"iosClientId": "YOUR_IOS_CLIENT_ID.apps.googleusercontent.com",
"androidServerClientId": "YOUR_WEB_CLIENT_ID.apps.googleusercontent.com"
}
]
]
}
}
- iosUrlScheme: Override the URL scheme used for callbacks. Defaults to the reversed iosClientId.
1. Ensure your app has a GoogleService-Info.plist or add GIDClientID to Info.plist.iosClientId
2. Add the reversed client ID as a URL type (the plugin will add this if you provide ).pod install
3. After installing the package, run in your iOS directory.
The plugin patches AppDelegate to forward auth callbacks to GIDSignIn.
1. Supply the androidServerClientId from your Google Cloud credentials (the Web client ID).android/app/src/main/res/values/strings.xml
2. The config plugin writes the ID into .
No additional code changes are required.
`ts
import { signIn } from '@ls-stack/expo-google-auth';
const result = await signIn();
if (!result) {
// user cancelled
return;
}
console.log(result.idToken);
`
`ts`
type GoogleLoginResult = {
idToken: string;
accessToken?: string;
email?: string;
name?: string;
picture?: string;
};
signIn() throws an ExpoGoogleAuthError when the native flow fails. The helper isExpoGoogleAuthError and the code property let you branch on known scenarios.
`ts
import {
signIn,
isExpoGoogleAuthError,
ExpoGoogleAuthError,
} from '@ls-stack/expo-google-auth';
try {
const result = await signIn();
if (!result) return; // user cancelled
// handle success
} catch (error) {
if (isExpoGoogleAuthError(error)) {
switch (error.code) {
case 'PLAY_SERVICES_NOT_AVAILABLE':
showToast('Google Play services are not available on this device.');
return;
case 'MISSING_CLIENT_ID':
console.error('Check the plugin configuration in app.json/app.config.ts');
return;
case 'SIGN_IN_IN_PROGRESS':
return; // already handling another sign-in flow
default:
showToast(error.message);
}
} else {
console.error('Unexpected error during Google login', error);
}
}
`
| Code | Meaning |
| ---- | ------- |
| SIGN_IN_IN_PROGRESS | The native module already has an active sign-in flow. |NO_ACTIVITY
| | Android activity was unavailable (usually when the app is backgrounded). |NO_VIEW_CONTROLLER
| | iOS could not find a presented view controller. |MISSING_CLIENT_ID
| | OAuth client ID is missing; check plugin configuration. |AUTHENTICATION_ERROR
| | GoogleSignIn returned an authentication error. |SIGNIN_ERROR
| | General sign-in failure from GoogleSignIn. |CREDENTIAL_ERROR
| | Credential Manager could not produce a credential. |CREDENTIAL_SECURITY_ERROR
| | Credential Manager blocked the request due to security policy. |CREDENTIAL_UNSUPPORTED
| | Credential Manager cannot handle the requested option on this device. |PLAY_SERVICES_NOT_AVAILABLE
| | Google Play services (or the Google credential provider) is unavailable. |UNEXPECTED_ERROR
| | Platform threw an unexpected error. |UNSUPPORTED_PLATFORM
| | The library only runs on iOS and Android. |UNKNOWN
| | Fallback when no additional detail is available. |
- Build: pnpm run buildpnpm run clean
- Clean: pnpm run lint`
- Lint: