From aa0b931bc0d7eb8e73cbc26370cc159678db64d4 Mon Sep 17 00:00:00 2001 From: Ahmed Sbai Date: Wed, 17 Sep 2025 14:58:18 +0200 Subject: [PATCH 1/3] feat(plugin): add expo config plugin for google auth - Plugin setup with TypeScript - Configuration for iOS Info.plist and Android manifest - Client ID handling from Google services files - Workspace and package configuration updates --- README.md | 1487 ++++++++++++++-------------------- package.json | 6 +- plugin/package.json | 26 + plugin/src/index.ts | 26 + plugin/src/withGoogleAuth.ts | 245 ++++++ plugin/tsconfig.json | 23 + tsconfig.build.json | 8 +- yarn.lock | 309 ++++++- 8 files changed, 1226 insertions(+), 904 deletions(-) create mode 100644 plugin/package.json create mode 100644 plugin/src/index.ts create mode 100644 plugin/src/withGoogleAuth.ts create mode 100644 plugin/tsconfig.json diff --git a/README.md b/README.md index dd6c5f6..c2235b0 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,21 @@ **react-native-google-auth** is a comprehensive React Native Google Authentication library that provides seamless Google Sign-In integration for iOS and Android applications. Built with modern APIs including Google Sign-In SDK for iOS and Android Credential Manager, this library offers the most up-to-date Google authentication solution for React Native developers. +## 📋 Table of Contents + +- [Why Choose react-native-google-auth?](#why-choose-react-native-google-auth) +- [Key Features & Benefits](#key-features--benefits) +- [Installation](#installation) +- [🚀 Quick Start](#-quick-start) +- [📱 Platform Setup](#-platform-setup) + - [Expo Setup (Recommended)](#expo-setup-recommended) + - [React Native CLI Setup](#react-native-cli-setup) +- [🔧 Google Cloud Console Setup](#-google-cloud-console-setup) +- [💻 Usage](#-usage) +- [🔍 API Reference](#-api-reference) +- [🐛 Troubleshooting](#-troubleshooting) +- [📚 Examples](#-examples) + ## Why Choose react-native-google-auth? This React Native Google Sign-In library stands out from other Google authentication solutions by leveraging the latest Google APIs and providing a unified, type-safe interface for both iOS and Android platforms. Perfect for developers looking to implement Google OAuth, Google Login, or Google SSO in their React Native mobile applications. @@ -23,7 +38,7 @@ This React Native Google Sign-In library stands out from other Google authentica ### 💻 Developer Experience - ✅ **Full TypeScript Support**: Complete TypeScript definitions and IntelliSense - ✅ **Cross-Platform Compatibility**: Works seamlessly on both iOS and Android -- ✅ **Zero Configuration**: Minimal setup required with sensible defaults +- ✅ **Expo Config Plugin**: Zero-configuration setup for Expo projects - ✅ **Comprehensive Documentation**: Detailed guides and API reference ### 🔒 Security & Reliability @@ -32,18 +47,6 @@ This React Native Google Sign-In library stands out from other Google authentica - ✅ **Production Ready**: Battle-tested authentication flows - ✅ **Google Play Services**: Automatic Google Play Services availability checking -### 🍎 iOS Configuration Improvements (New!) -- ✅ **Automatic Client ID Detection**: Reads client ID from Info.plist when not provided -- ✅ **Configuration Validation**: Comprehensive validation for all configuration parameters -- ✅ **Enhanced Scope Management**: Support for additional OAuth scopes beyond default -- ✅ **Better Error Handling**: Detailed error messages for missing or invalid configuration - -### 🤖 Android Configuration Improvements (New!) -- ✅ **Automatic Client ID Detection**: Reads client ID from google-services.json when not provided -- ✅ **Configuration Validation**: Comprehensive validation for all configuration parameters -- ✅ **Enhanced Scope Management**: Support for additional OAuth scopes beyond default -- ✅ **Better Error Handling**: Detailed error messages for missing or invalid configuration - ### 🎯 Use Cases - **React Native Google Login**: Implement Google sign-in in React Native apps - **Mobile OAuth Authentication**: Secure user authentication for mobile apps @@ -51,9 +54,7 @@ This React Native Google Sign-In library stands out from other Google authentica - **Enterprise SSO**: Single Sign-On for enterprise applications - **User Profile Management**: Access Google user profile information -## Installation & Setup Guide - -### NPM Installation +## Installation Install the React Native Google Authentication library using your preferred package manager: @@ -66,9 +67,12 @@ npm install react-native-google-auth # Using pnpm pnpm add react-native-google-auth + +# For Expo projects +npx expo install react-native-google-auth ``` -### Quick Start +## 🚀 Quick Start Get started with Google Sign-In in your React Native app in just 3 steps: @@ -88,263 +92,123 @@ await GoogleAuth.configure({ // Sign in users const response = await GoogleAuth.signIn(); +if (response.type === 'success') { + console.log('User signed in:', response.data.user); +} ``` -### iOS Setup - -1. Install pods: - ```bash - cd ios && pod install - ``` - - *Note: The GoogleSignIn dependency is automatically included via the library's podspec.* - -2. **Configure Client ID (Choose one method):** - - **Method A: Automatic Detection from Info.plist (Recommended)** - - Add your iOS client ID to `ios/Info.plist`: - ```xml - GIDClientID - YOUR_IOS_CLIENT_ID - ``` - - With this method, you can configure without providing `iosClientId`: - ```typescript - await GoogleAuth.configure({ - androidClientId: 'YOUR_ANDROID_CLIENT_ID' - // iosClientId automatically detected from Info.plist - }); - ``` - - **Method B: Manual Configuration** - - Provide the client ID directly in your configuration: - ```typescript - await GoogleAuth.configure({ - iosClientId: 'YOUR_IOS_CLIENT_ID', - androidClientId: 'YOUR_ANDROID_CLIENT_ID' - }); - ``` - - **Method C: GoogleService-Info.plist (Alternative)** - - If you have a `GoogleService-Info.plist` file, the library will automatically detect the `CLIENT_ID` from it. - -3. Add URL schemes to `ios/Info.plist` (replace with your actual iOS client ID): - ```xml - CFBundleURLTypes - - - CFBundleURLName - googleauth - CFBundleURLSchemes - - com.googleusercontent.apps.YOUR_IOS_CLIENT_ID - - - - ``` - - **Note:** Remove `.apps.googleusercontent.com` from your iOS client ID when adding it to URL schemes. - -4. Configure URL handling in your `AppDelegate.swift`: - ```swift - import GoogleSignIn - - // Add this method to handle URL schemes - func application( - _ app: UIApplication, - open url: URL, - options: [UIApplication.OpenURLOptionsKey: Any] = [:] - ) -> Bool { - return GIDSignIn.sharedInstance.handle(url) - } - ``` +## 📱 Platform Setup -### Android Setup +### Expo Setup (Recommended) -1. Add the following to your `android/app/build.gradle`: - ```gradle - dependencies { - implementation 'androidx.credentials:credentials:1.3.0' - implementation 'androidx.credentials:credentials-play-services-auth:1.3.0' - implementation 'com.google.android.libraries.identity.googleid:googleid:1.1.0' - } - ``` - - *Note: These dependencies are automatically included via the library's build.gradle.* +> **⚠️ Important**: This library requires native code and is **not compatible with Expo Go**. You must use **Expo Development Build** or eject to a bare React Native project. -2. **Configure Client ID (Choose one method):** +#### Prerequisites - **Method A: Automatic Detection from google-services.json (Recommended)** - - Add your `google-services.json` file to `android/app/` directory: - ``` - android/ - └── app/ - └── google-services.json - ``` - - With this method, you can configure without providing `androidClientId`: - ```typescript - await GoogleAuth.configure({ - androidClientId: 'YOUR_ANDROID_CLIENT_ID', - // androidClientId automatically detected from google-services.json - }); - ``` +- Expo SDK 49+ (recommended) +- Expo Development Build configured +- EAS CLI installed: `npm install -g @expo/eas-cli` or local dev server - **Method B: Manual Configuration** - - Provide the client ID directly in your configuration: - ```typescript - await GoogleAuth.configure({ - androidClientId: 'YOUR_ANDROID_CLIENT_ID', - iosClientId: 'YOUR_IOS_CLIENT_ID' - }); - ``` +#### Step 1: Install the Package -### Expo Setup (Development Build) +```bash +npx expo install react-native-google-auth +``` -> **Note**: This library requires native code and is **not compatible with Expo Go**. You must use Expo Development Build or eject to a bare React Native project. +#### Step 2: Configure the Expo Plugin -#### Prerequisites +Add the plugin to your `app.json` or `app.config.js`: -- Expo SDK 49+ (recommended) -- Expo Development Build configured +```json +{ + "expo": { + "name": "Your App Name", + "plugins": [ + [ + "react-native-google-auth", + { + "iosClientId": "YOUR_IOS_CLIENT_ID.apps.googleusercontent.com", + "androidClientId": "YOUR_ANDROID_CLIENT_ID.apps.googleusercontent.com" + } + ] + ] + } +} +``` -#### Installation +**Alternative: Automatic Configuration (Recommended)** -1. **Install the package:** - ```bash - npx expo install react-native-google-auth - ``` +Place your Google configuration files in your project root and let the plugin auto-detect them: -2. **Configure the plugin in your `app.json` or `app.config.js`:** +1. Download `GoogleService-Info.plist` from Firebase Console (iOS) +2. Download `google-services.json` from Firebase Console (Android) +3. Place both files in your project root (same level as `app.json`) - The library automatically detects client IDs from your Google configuration files. You have several options: +```json +{ + "expo": { + "plugins": [ + "react-native-google-auth" + ] + } +} +``` - **Option 1: Automatic Detection (Recommended)** - Place your Google configuration files in your project root: - - `google-services.json` (Android) - - `GoogleService-Info.plist` (iOS) +#### Step 3: Configure Platform-Specific Settings - Add the plugin to your `app.json`: - ```json - { - "expo": { - "plugins": [ - "react-native-google-auth" - ] - } - } - ``` +**iOS Configuration in app.json:** - Then configure without specifying client IDs: - ```javascript - await GoogleAuth.configure({ - scopes: [GoogleAuthScopes.EMAIL, GoogleAuthScopes.PROFILE] - }); - ``` +```json +{ + "expo": { + "ios": { + "bundleIdentifier": "com.yourcompany.yourapp", + "infoPlist": { + "GIDClientID": "YOUR_IOS_CLIENT_ID.apps.googleusercontent.com" + } + } + } +} +``` - **Option 2: Manual Configuration** - ```javascript - await GoogleAuth.configure({ - iosClientId: 'YOUR_IOS_CLIENT_ID', - androidClientId: 'YOUR_ANDROID_CLIENT_ID', - scopes: [GoogleAuthScopes.EMAIL, GoogleAuthScopes.PROFILE] - }); - ``` +**Android Configuration in app.json:** - **Option 3: Environment Variables in app.config.js** - ```javascript - export default { - expo: { - plugins: [ - 'react-native-google-auth' - ] - } - }; - ``` +```json +{ + "expo": { + "android": { + "package": "com.yourcompany.yourapp" + } + } +} +``` - Then use environment variables in your code: - ```javascript - await GoogleAuth.configure({ - iosClientId: process.env.GOOGLE_IOS_CLIENT_ID, - androidClientId: process.env.GOOGLE_ANDROID_CLIENT_ID, - scopes: [GoogleAuthScopes.EMAIL, GoogleAuthScopes.PROFILE] - }); - ``` +#### Step 4: Create Development Build -#### iOS Configuration - -1. **Add your iOS client ID to `app.json`:** - ```json - { - "expo": { - "ios": { - "infoPlist": { - "GIDClientID": "YOUR_IOS_CLIENT_ID" - }, - "bundleIdentifier": "com.yourcompany.yourapp" - } - } - } - ``` +```bash +# Login to EAS (if not already logged in) +eas login -2. **Configure URL schemes in `app.json`:** - ```json - { - "expo": { - "ios": { - "scheme": "com.googleusercontent.apps.YOUR_IOS_CLIENT_ID" - } - } - } - ``` - - **Note:** Remove `.apps.googleusercontent.com` from your iOS client ID when adding it as a scheme. - -#### Android Configuration - -1. **Add your `google-services.json` file:** - - Download `google-services.json` from Firebase Console - - Place it in your project root (same level as `app.json`) - - The config plugin will automatically copy it to the correct location - -2. **Configure package name in `app.json`:** - ```json - { - "expo": { - "android": { - "package": "com.yourcompany.yourapp" - } - } - } - ``` +# Configure your project (if first time) +eas build:configure -#### Building Development Build +# Create development build +eas build --profile development --platform all -1. **Create a development build:** - ```bash - # For iOS - eas build --profile development --platform ios - - # For Android - eas build --profile development --platform android - - # For both platforms - eas build --profile development --platform all - ``` +# Or for specific platform +eas build --profile development --platform ios +eas build --profile development --platform android +``` -2. **Install the development build on your device** +#### Step 5: Install and Test -3. **Start the development server:** +1. Install the development build on your device +2. Start the development server: ```bash npx expo start --dev-client ``` -#### Usage in Expo +#### Step 6: Use in Your App ```typescript import { GoogleAuth, GoogleAuthScopes } from 'react-native-google-auth'; @@ -358,9 +222,10 @@ export default function App() { const configureGoogleAuth = async () => { try { await GoogleAuth.configure({ - // Client IDs are automatically detected from app.json configuration + // Client IDs are automatically detected from plugin configuration scopes: [GoogleAuthScopes.EMAIL, GoogleAuthScopes.PROFILE] }); + console.log('Google Auth configured successfully'); } catch (error) { console.error('Google Auth configuration failed:', error); } @@ -372,8 +237,9 @@ export default function App() { if (response.type === 'success') { console.log('User signed in:', response.data.user); + // Handle successful sign-in } else if (response.type === 'cancelled') { - console.log('Sign in cancelled'); + console.log('Sign in cancelled by user'); } } catch (error) { console.error('Sign in failed:', error); @@ -384,79 +250,181 @@ export default function App() { } ``` -#### Expo Config Plugin Options +#### Expo Plugin Options The config plugin accepts the following options: ```typescript interface GoogleAuthPluginOptions { - iosClientId?: string; // iOS OAuth client ID - androidClientId?: string; // Android OAuth client ID - googleServicesFile?: string; // Path to google-services.json (default: './google-services.json') + iosClientId?: string; // iOS OAuth client ID + androidClientId?: string; // Android OAuth client ID + googleServicesFile?: string; // Path to google-services.json (default: './google-services.json') + iosGoogleServicesFile?: string; // Path to GoogleService-Info.plist (default: './GoogleService-Info.plist') } ``` -#### Troubleshooting Expo Issues +**Example with custom file paths:** -**Common Issues:** +```json +{ + "expo": { + "plugins": [ + [ + "react-native-google-auth", + { + "googleServicesFile": "./config/google-services.json", + "iosGoogleServicesFile": "./config/GoogleService-Info.plist" + } + ] + ] + } +} +``` -1. **"Google Auth not configured" in Expo:** - - Ensure you've added the config plugin to `app.json` - - Rebuild your development build after adding the plugin - - Verify client IDs are correctly set in the plugin configuration +### React Native CLI Setup -2. **"Invalid client ID" in Expo:** - - Check that your bundle identifier (iOS) and package name (Android) match your Google Cloud Console configuration - - Ensure you're using the correct client IDs for your platform +#### iOS Setup -3. **Build fails with Google Auth:** - - Make sure you're using Expo SDK 49+ - - Clear your build cache: `eas build --clear-cache` - - Verify `google-services.json` is in the project root +1. **Install pods:** + ```bash + cd ios && pod install + ``` -4. **Sign-in doesn't work in development build:** - - Ensure you're testing on a physical device (not Expo Go) - - Check that Google Play Services are installed and updated (Android) - - Verify your app's SHA-1 fingerprint is added to Google Cloud Console (Android) +2. **Configure Client ID (Choose one method):** -**Getting SHA-1 fingerprint for Expo:** -```bash -# For development builds -eas credentials -p android + **Method A: Automatic Detection from Info.plist (Recommended)** + + Add your iOS client ID to `ios/YourApp/Info.plist`: + ```xml + GIDClientID + YOUR_IOS_CLIENT_ID.apps.googleusercontent.com + ``` -# Or manually from keystore -keytool -list -v -keystore path/to/your/keystore.jks -alias your-key-alias -``` + **Method B: Manual Configuration** + + Provide the client ID directly in your configuration: + ```typescript + await GoogleAuth.configure({ + iosClientId: 'YOUR_IOS_CLIENT_ID.apps.googleusercontent.com', + androidClientId: 'YOUR_ANDROID_CLIENT_ID.apps.googleusercontent.com' + }); + ``` + +3. **Add URL schemes to `ios/YourApp/Info.plist`:** + ```xml + CFBundleURLTypes + + + CFBundleURLName + com.yourcompany.yourapp + CFBundleURLSchemes + + com.googleusercontent.apps.YOUR_IOS_CLIENT_ID + + + + ``` -#### Expo Limitations +4. **Configure URL handling in your `AppDelegate.swift`:** + ```swift + import GoogleSignIn + + // Add this method to handle URL schemes + func application( + _ app: UIApplication, + open url: URL, + options: [UIApplication.OpenURLOptionsKey: Any] = [:] + ) -> Bool { + return GIDSignIn.sharedInstance.handle(url) + } + ``` -- **Expo Go**: Not supported - requires development build -- **Web**: Not supported - mobile platforms only -- **Expo Router**: Fully compatible with file-based routing -- **Expo Updates**: Compatible with OTA updates +#### Android Setup -## Getting Client IDs +1. **Configure Client ID (Choose one method):** + + **Method A: Automatic Detection from google-services.json (Recommended)** + + Add your `google-services.json` file to `android/app/` directory. + + **Method B: Manual Configuration** + + Provide the client ID directly in your configuration: + ```typescript + await GoogleAuth.configure({ + androidClientId: 'YOUR_ANDROID_CLIENT_ID.apps.googleusercontent.com', + iosClientId: 'YOUR_IOS_CLIENT_ID.apps.googleusercontent.com' + }); + ``` + +## 🔧 Google Cloud Console Setup + +### Step 1: Create a Google Cloud Project 1. Go to [Google Cloud Console](https://console.cloud.google.com/) -2. Create a new project or select existing one -3. Enable **Google Sign-In API** -4. Go to **Credentials** → **Create Credentials** → **OAuth 2.0 Client IDs** - -### For iOS: -- Application type: **iOS** -- Bundle ID: Your iOS app's bundle identifier -- Copy the **Client ID** - -### For Android: -- Application type: **Android** -- Package name: Your Android app's package name -- SHA-1 certificate fingerprint: - ```bash - keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android - ``` -- Copy the **Client ID** - -## Usage +2. Create a new project or select an existing one +3. Enable the **Google Sign-In API** + +### Step 2: Configure OAuth Consent Screen + +1. Go to **APIs & Services** → **OAuth consent screen** +2. Choose **External** (for public apps) or **Internal** (for G Suite domains) +3. Fill in the required information: + - App name + - User support email + - Developer contact information +4. Add scopes (at minimum: `email`, `profile`, `openid`) +5. Save and continue + +### Step 3: Create OAuth 2.0 Client IDs + +#### For iOS: +1. Go to **Credentials** → **Create Credentials** → **OAuth 2.0 Client IDs** +2. Application type: **iOS** +3. Name: Your app name (iOS) +4. Bundle ID: Your iOS app's bundle identifier (e.g., `com.yourcompany.yourapp`) +5. Click **Create** +6. Copy the **Client ID** (ends with `.apps.googleusercontent.com`) + +#### For Android: +1. Go to **Credentials** → **Create Credentials** → **OAuth 2.0 Client IDs** +2. Application type: **Android** +3. Name: Your app name (Android) +4. Package name: Your Android app's package name (e.g., `com.yourcompany.yourapp`) +5. SHA-1 certificate fingerprint: + + **For development:** + ```bash + keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android + ``` + + **For Expo development builds:** + ```bash + eas credentials -p android + ``` + + **For production:** + ```bash + keytool -list -v -keystore path/to/your/release.keystore -alias your-key-alias + ``` + +6. Click **Create** +7. Copy the **Client ID** (ends with `.apps.googleusercontent.com`) + +### Step 4: Download Configuration Files (Optional but Recommended) + +#### For iOS: +1. In your OAuth 2.0 client, click **Download plist** +2. Save as `GoogleService-Info.plist` +3. Place in your project root (for Expo) or `ios/YourApp/` (for React Native CLI) + +#### For Android: +1. Go to **Firebase Console** → **Project Settings** → **General** +2. Add your Android app if not already added +3. Download `google-services.json` +4. Place in your project root (for Expo) or `android/app/` (for React Native CLI) + +## 💻 Usage ### Import @@ -470,11 +438,11 @@ import { GoogleAuth, GoogleAuthScopes } from 'react-native-google-auth'; const configure = async () => { try { await GoogleAuth.configure({ - iosClientId: 'YOUR_IOS_CLIENT_ID', // Optional on iOS - auto-detected from Info.plist - androidClientId: 'YOUR_ANDROID_CLIENT_ID', // Preferred for Android - webClientId: 'YOUR_WEB_CLIENT_ID', // Fallback for Android, required for server verification + iosClientId: 'YOUR_IOS_CLIENT_ID.apps.googleusercontent.com', // Optional on iOS - auto-detected from Info.plist + androidClientId: 'YOUR_ANDROID_CLIENT_ID.apps.googleusercontent.com', // Optional on Android - auto-detected from google-services.json + webClientId: 'YOUR_WEB_CLIENT_ID.apps.googleusercontent.com', // Optional - for server verification hostedDomain: 'yourdomain.com', // Optional - for G Suite domains - scopes: [ // Optional - additional OAuth scopes (use enum for common scopes) + scopes: [ // Optional - additional OAuth scopes GoogleAuthScopes.EMAIL, GoogleAuthScopes.PROFILE, 'https://www.googleapis.com/auth/drive.readonly', @@ -488,72 +456,6 @@ const configure = async () => { }; ``` -#### Configuration Options - -- **iosClientId** (iOS): Optional if configured in Info.plist. The library automatically detects from: - 1. `GIDClientID` key in Info.plist (recommended) - 2. `CLIENT_ID` key in GoogleService-Info.plist -- **androidClientId** (Android): Optional if google-services.json is configured. The library automatically detects from: - 1. `google-services.json` file in android/app/ directory (recommended) - 2. Falls back to webClientId if neither androidClientId nor google-services.json is available -- **webClientId**: Used for server-side verification and Android fallback -- **hostedDomain**: Restricts sign-in to users from a specific G Suite domain -- **scopes**: Array of OAuth 2.0 scopes to request. Supports: - - GoogleAuthScopes enum for common scopes (recommended for type safety) - - Google API scopes: `https://www.googleapis.com/auth/[service]` - - OpenID Connect scopes: `openid`, `email`, `profile` -- **offlineAccess**: Request refresh token for offline access - -#### OAuth Scopes - -The library provides a `GoogleAuthScopes` enum for type-safe scope definitions: - -```typescript -import { GoogleAuthScopes } from 'react-native-google-auth'; - -// Use predefined scopes for type safety -await GoogleAuth.configure({ - scopes: [ - GoogleAuthScopes.EMAIL, - GoogleAuthScopes.PROFILE, - GoogleAuthScopes.DRIVE_READONLY, - GoogleAuthScopes.CALENDAR, - ] -}); - -// Or mix with custom scopes -await GoogleAuth.configure({ - scopes: [ - GoogleAuthScopes.EMAIL, - 'https://www.googleapis.com/auth/custom.scope' - ] -}); -``` - -**Available Scopes:** -- **OpenID Connect**: `OPENID`, `EMAIL`, `PROFILE` -- **Google Drive**: `DRIVE`, `DRIVE_FILE`, `DRIVE_READONLY` -- **Gmail**: `GMAIL_READONLY`, `GMAIL_MODIFY`, `GMAIL_COMPOSE` -- **Calendar**: `CALENDAR`, `CALENDAR_READONLY` -- **Contacts**: `CONTACTS`, `CONTACTS_READONLY` -- **YouTube**: `YOUTUBE`, `YOUTUBE_READONLY` -- **Photos**: `PHOTOS`, `PHOTOS_READONLY` -- **Google Workspace**: `SPREADSHEETS`, `DOCUMENTS`, `PRESENTATIONS` (with readonly variants) -- **Cloud Platform**: `CLOUD_PLATFORM`, `CLOUD_PLATFORM_READONLY` -- **Analytics**: `ANALYTICS`, `ANALYTICS_READONLY` -- **Fitness**: `FITNESS_ACTIVITY_READ`, `FITNESS_BODY_READ`, `FITNESS_LOCATION_READ` -- **Other**: `USERINFO_EMAIL`, `USERINFO_PROFILE`, `PLUS_ME`, `ADWORDS`, `BLOGGER` - -#### Configuration Validation - -The library automatically validates: -- Client ID format (must match Google OAuth format) -- Domain format for hosted domains -- OAuth scope format and validity -- Required parameters based on platform -- Automatic detection fallbacks for both iOS and Android -``` - ### Sign In ```typescript @@ -562,14 +464,12 @@ const signIn = async () => { const response = await GoogleAuth.signIn(); if (response.type === 'success') { - console.log('User signed in:', response.data.user); - console.log('Access token:', response.data.accessToken); - console.log('ID token:', response.data.idToken); - // response.data.user contains: id, email, name, photo, etc. + const { user, idToken, accessToken } = response.data; + console.log('User:', user); + console.log('Access Token:', accessToken); // null on Android + console.log('ID Token:', idToken); } else if (response.type === 'cancelled') { - console.log('Sign in was cancelled by user'); - } else if (response.type === 'noSavedCredentialFound') { - console.log('No saved credential found'); + console.log('Sign in was cancelled'); } } catch (error) { console.error('Sign in failed:', error); @@ -583,7 +483,7 @@ const signIn = async () => { const signOut = async () => { try { await GoogleAuth.signOut(); - console.log('User signed out'); + console.log('User signed out successfully'); } catch (error) { console.error('Sign out failed:', error); } @@ -596,24 +496,44 @@ const signOut = async () => { const getTokens = async () => { try { const tokens = await GoogleAuth.getTokens(); - console.log('Tokens:', tokens); - // tokens contains: accessToken, idToken, etc. + console.log('ID Token:', tokens.idToken); + console.log('Access Token:', tokens.accessToken); // null on Android + console.log('User:', tokens.user); } catch (error) { console.error('Failed to get tokens:', error); } }; ``` +### Get Current User + +```typescript +const getCurrentUser = async () => { + try { + const user = await GoogleAuth.getCurrentUser(); + if (user) { + console.log('Current user:', user); + } else { + console.log('No user is currently signed in'); + } + } catch (error) { + console.error('Failed to get current user:', error); + } +}; +``` + ### Refresh Tokens ```typescript const refreshTokens = async () => { try { - const refreshedTokens = await GoogleAuth.refreshTokens(); - console.log('Refreshed tokens:', refreshedTokens); - // refreshedTokens contains: accessToken, idToken, user, expiresAt + const tokens = await GoogleAuth.refreshTokens(); + console.log('Refreshed ID Token:', tokens.idToken); + console.log('Refreshed Access Token:', tokens.accessToken); // null on Android + console.log('User:', tokens.user); + console.log('Expires At:', tokens.expiresAt); } catch (error) { - console.error('Failed to refresh tokens:', error); + console.error('Token refresh failed:', error); } }; ``` @@ -627,7 +547,7 @@ const checkTokenExpiration = async () => { console.log('Token expired:', isExpired); if (isExpired) { - // Automatically refresh tokens + // Refresh tokens if expired await refreshTokens(); } } catch (error) { @@ -636,115 +556,205 @@ const checkTokenExpiration = async () => { }; ``` -### Get Current User +### Check Play Services (Android) ```typescript -const getCurrentUser = async () => { +const checkPlayServices = async () => { try { - const currentUser = await GoogleAuth.getCurrentUser(); - if (currentUser) { - console.log('Current user:', currentUser); - // currentUser contains: id, name, email, photo, familyName, givenName - } else { - console.log('No user currently signed in'); - } + const playServicesInfo = await GoogleAuth.checkPlayServices(true); // Show error dialog if needed + console.log('Play Services available:', playServicesInfo.isAvailable); + console.log('Play Services status:', playServicesInfo.status); // Android only } catch (error) { - console.error('Failed to get current user:', error); + console.error('Failed to check Play Services:', error); } }; ``` +## 🔍 API Reference +### GoogleAuth -### Check Play Services +#### Methods + +- `configure(options: GoogleAuthConfig): Promise` +- `signIn(): Promise` +- `signOut(): Promise` +- `getCurrentUser(): Promise` +- `getTokens(): Promise` +- `refreshTokens(): Promise` +- `isTokenExpired(): Promise` +- `checkPlayServices(showErrorDialog?: boolean): Promise` + +#### Types ```typescript -const checkPlayServices = async () => { - try { - const playServicesInfo = await GoogleAuth.checkPlayServices(); - console.log('Play Services available:', playServicesInfo.isAvailable); - if (playServicesInfo.status) { - console.log('Play Services status:', playServicesInfo.status); - } - } catch (error) { - console.error('Play Services check failed:', error); - } -}; +interface GoogleAuthConfig { + iosClientId?: string; + androidClientId?: string; + webClientId?: string; + hostedDomain?: string; + scopes?: string[]; +} + +interface GoogleAuthResponse { + type: 'success' | 'cancelled'; + data?: { + user: GoogleUser; + idToken: string; + accessToken: string | null; // null on Android due to Credential Manager API limitations + }; +} + +interface GoogleUser { + id: string; + email: string; + name: string; + photo?: string; + familyName?: string; + givenName?: string; +} + +interface GoogleTokens { + idToken: string; + accessToken: string | null; // null on Android due to Credential Manager API limitations + user?: GoogleUser; + expiresAt?: number; +} + +interface PlayServicesInfo { + isAvailable: boolean; + status?: number; // Android only - Google Play Services status code +} ``` -*Note: This method is primarily useful on Android to check if Google Play Services are available. On iOS, it always returns `{ isAvailable: true }` since Play Services are not required.* +### GoogleAuthScopes -## Complete Example +Common OAuth scopes: + +```typescript +enum GoogleAuthScopes { + EMAIL = 'email', + PROFILE = 'profile', + OPENID = 'openid', + DRIVE = 'https://www.googleapis.com/auth/drive', + DRIVE_READONLY = 'https://www.googleapis.com/auth/drive.readonly', + CALENDAR = 'https://www.googleapis.com/auth/calendar', + CALENDAR_READONLY = 'https://www.googleapis.com/auth/calendar.readonly', +} +``` + +## 🐛 Troubleshooting + +### Common Issues + +#### Expo-Specific Issues + +**1. "Google Auth not configured" in Expo** +- Ensure you've added the config plugin to `app.json` +- Rebuild your development build after adding the plugin +- Verify client IDs are correctly set in the plugin configuration + +**2. "Invalid client ID" in Expo** +- Check that your bundle identifier (iOS) and package name (Android) match your Google Cloud Console configuration +- Ensure you're using the correct client IDs for your platform +- Verify the client IDs include the full `.apps.googleusercontent.com` suffix + +**3. Build fails with Google Auth** +- Make sure you're using Expo SDK 49+ +- Clear your build cache: `eas build --clear-cache` +- Verify `google-services.json` and `GoogleService-Info.plist` are in the project root + +**4. Sign-in doesn't work in development build** +- Ensure you're testing on a physical device (not Expo Go) +- Check that Google Play Services are installed and updated (Android) +- Verify your app's SHA-1 fingerprint is added to Google Cloud Console (Android) + +#### General Issues + +**1. "DEVELOPER_ERROR" on Android** +- Verify your package name matches Google Cloud Console +- Check SHA-1 fingerprint is correctly added +- Ensure `google-services.json` is in the correct location + +**2. "SIGN_IN_REQUIRED" error** +- User needs to sign in first +- Check if user session has expired + +**3. iOS URL scheme not working** +- Verify URL scheme is correctly added to Info.plist +- Check AppDelegate.swift has the URL handling code +- Ensure the scheme matches your client ID (reversed) + +**4. Network errors** +- Check internet connectivity +- Verify Google Play Services are available (Android) +- Check if your app is properly configured in Google Cloud Console + +#### Platform Differences + +**Important Note about Access Tokens:** +- **iOS**: Returns both `idToken` and `accessToken` +- **Android**: Returns `idToken` but `accessToken` is always `null` due to Android Credential Manager API limitations + +The Android Credential Manager API focuses on authentication (ID tokens) rather than authorization (access tokens). If you need access tokens on Android for API calls, consider implementing a separate OAuth2 flow or use the ID token for server-side token exchange. + +### Getting Help + +1. Check the [GitHub Issues](https://github.com/sbaiahmed1/react-native-google-auth/issues) +2. Review the [troubleshooting guide](./docs/troubleshooting.md) +3. Join our [Discord community](https://discord.gg/your-discord) + +## 📚 Examples + +### Complete Expo Example ```typescript import React, { useEffect, useState } from 'react'; -import { View, Button, Text, Alert } from 'react-native'; -import { GoogleAuth } from 'react-native-google-auth'; +import { View, Text, TouchableOpacity, StyleSheet, Alert } from 'react-native'; +import { GoogleAuth, GoogleAuthScopes, GoogleUser } from 'react-native-google-auth'; -const GoogleAuthExample = () => { - const [user, setUser] = useState(null); +export default function App() { + const [user, setUser] = useState(null); const [isConfigured, setIsConfigured] = useState(false); - const [tokens, setTokens] = useState(null); - const [tokenExpired, setTokenExpired] = useState(false); useEffect(() => { configureGoogleAuth(); - checkCurrentUser(); }, []); const configureGoogleAuth = async () => { try { await GoogleAuth.configure({ - iosClientId: 'YOUR_IOS_CLIENT_ID', - androidClientId: 'YOUR_ANDROID_CLIENT_ID', - webClientId: 'YOUR_WEB_CLIENT_ID' + scopes: [GoogleAuthScopes.EMAIL, GoogleAuthScopes.PROFILE] }); setIsConfigured(true); - } catch (error) { - Alert.alert('Configuration Error', error.message); - } - }; - - const checkCurrentUser = async () => { - try { + + // Check if user is already signed in const currentUser = await GoogleAuth.getCurrentUser(); - if (currentUser) { - setUser(currentUser); - await checkTokenExpiration(); - } + setUser(currentUser); } catch (error) { - console.log('No current user:', error.message); + console.error('Google Auth configuration failed:', error); + Alert.alert('Configuration Error', 'Failed to configure Google Auth'); } }; - const checkTokenExpiration = async () => { - try { - const isExpired = await GoogleAuth.isTokenExpired(); - setTokenExpired(isExpired); - - if (isExpired) { - Alert.alert('Token Expired', 'Your session has expired. Please refresh tokens.'); - } - } catch (error) { - console.error('Failed to check token expiration:', error); + const handleSignIn = async () => { + if (!isConfigured) { + Alert.alert('Error', 'Google Auth is not configured'); + return; } - }; - const handleSignIn = async () => { try { const response = await GoogleAuth.signIn(); if (response.type === 'success') { setUser(response.data.user); - setTokenExpired(false); - Alert.alert('Success', 'Signed in successfully!'); + Alert.alert('Success', `Welcome ${response.data.user.name}!`); } else if (response.type === 'cancelled') { Alert.alert('Cancelled', 'Sign in was cancelled'); - } else if (response.type === 'noSavedCredentialFound') { - Alert.alert('No Saved Credential', 'No saved credential found, please sign in manually'); } } catch (error) { - Alert.alert('Sign In Error', error.message); + console.error('Sign in failed:', error); + Alert.alert('Sign In Error', 'Failed to sign in with Google'); } }; @@ -752,486 +762,181 @@ const GoogleAuthExample = () => { try { await GoogleAuth.signOut(); setUser(null); - setTokens(null); - setTokenExpired(false); - } catch (error) { - Alert.alert('Sign Out Error', error.message); - } - }; - - const handleRefreshTokens = async () => { - try { - const refreshedTokens = await GoogleAuth.refreshTokens(); - setTokens(refreshedTokens); - setTokenExpired(false); - Alert.alert('Success', 'Tokens refreshed successfully!'); - } catch (error) { - Alert.alert('Refresh Error', error.message); - } - }; - - const handleGetTokens = async () => { - try { - const currentTokens = await GoogleAuth.getTokens(); - setTokens(currentTokens); - Alert.alert('Tokens Retrieved', 'Check console for token details'); - console.log('Current tokens:', currentTokens); + Alert.alert('Success', 'Signed out successfully'); } catch (error) { - Alert.alert('Token Error', error.message); + console.error('Sign out failed:', error); + Alert.alert('Sign Out Error', 'Failed to sign out'); } }; - if (!isConfigured) { - return Configuring Google Auth...; - } - return ( - + + Google Auth Example + {user ? ( - - Welcome, {user.name}! - Email: {user.email} - {tokenExpired && ( - ⚠️ Token Expired - )} - -