capacitor-google-auth-zeattacker
v1.0.2
Published
Google Auth plugin for capacitor.
Maintainers
Readme
✨ Key Features
- 🔐 Persistent Sessions - Sign in once, stay authenticated across app restarts
- ⚡ Automatic Token Refresh - Tokens refresh transparently without user interaction
- 📊 Perfect for Data Sync - Optimized for RxDB → Google Sheets/Drive sync scenarios
- 🌐 Cross-Platform - Works seamlessly on Web, iOS, and Android
- 📅 Token Expiration Tracking - Know exactly when tokens expire
- 🛡️ Secure Storage - Platform-native secure session persistence
- 🎯 Type-Safe - Full TypeScript support with comprehensive type definitions
💡 Perfect For
- Offline-First Apps with cloud sync (RxDB, PouchDB, etc.)
- Google Drive/Sheets Integration without OAuth verification hassle
- Apps requiring persistent authentication without constant re-login
- Background data synchronization with automatic token management
📦 Installation
npm install @zeattacker/capacitor-google-auth
# or with pnpm
pnpm add @zeattacker/capacitor-google-auth
# or with yarn
yarn add @zeattacker/capacitor-google-authThen sync your Capacitor project:
npx cap sync🚀 Quick Start
Basic Setup with Persistent Sessions
import { GoogleAuth, GoogleAuthScopes } from '@zeattacker/capacitor-google-auth';
// Initialize with auto-restore for persistent sessions
await GoogleAuth.initialize({
clientId: 'YOUR_CLIENT_ID.apps.googleusercontent.com',
scopes: [GoogleAuthScopes.DRIVE_FILE], // Pre-defined scope constants
grantOfflineAccess: true, // Enable refresh tokens
autoRestoreSession: true, // Auto-restore on app launch
refreshThreshold: 300, // Auto-refresh if expires in 5 min
});
// Try to restore existing session
let user = await GoogleAuth.restoreSession();
if (!user) {
// No saved session, prompt user to sign in
user = await GoogleAuth.signIn();
await GoogleAuth.saveSession(); // Save for next time
}
// User is now authenticated!
console.log('Signed in as:', user.email);
console.log('Access token:', user.authentication.accessToken);Using with Google Sheets Sync (RxDB Example)
import { GoogleAuth, GoogleAuthScopes } from '@zeattacker/capacitor-google-auth';
class SheetsSync {
private accessToken: string;
async initialize() {
// Initialize auth
await GoogleAuth.initialize({
clientId: 'YOUR_CLIENT_ID.apps.googleusercontent.com',
scopes: [GoogleAuthScopes.DRIVE_FILE], // Only app-created files
grantOfflineAccess: true,
autoRestoreSession: true,
});
// Restore or sign in
let user = await GoogleAuth.restoreSession();
if (!user) {
user = await GoogleAuth.signIn();
await GoogleAuth.saveSession();
}
this.accessToken = user.authentication.accessToken;
}
async syncRxDBData(documents: any[]) {
// Check token validity before API call
const validation = await GoogleAuth.isTokenValid();
if (!validation.valid) {
// Auto-refresh if expired
const auth = await GoogleAuth.refresh();
this.accessToken = auth.accessToken;
await GoogleAuth.saveSession();
}
// Now safely make Google Sheets API call
await this.updateSpreadsheet(documents);
}
private async updateSpreadsheet(data: any[]) {
const response = await fetch(
'https://sheets.googleapis.com/v4/spreadsheets/YOUR_SPREADSHEET_ID/values/Sheet1!A1:append?valueInputOption=RAW',
{
method: 'POST',
headers: {
'Authorization': `Bearer ${this.accessToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ values: data }),
}
);
if (!response.ok) {
throw new Error('Failed to sync to Google Sheets');
}
}
}📖 For complete RxDB + Google Sheets integration examples, see GOOGLE_SHEETS_SYNC_GUIDE.md
🔧 Platform Setup
Web
The web platform uses Google Identity Services (GIS). Initialize after the platform is ready:
import { GoogleAuth } from '@zeattacker/capacitor-google-auth';
// In your app initialization (e.g., Angular AppComponent, React App.tsx)
GoogleAuth.initialize({
clientId: 'YOUR_WEB_CLIENT_ID.apps.googleusercontent.com',
scopes: ['profile', 'email'],
grantOfflineAccess: true,
});Optional: Use meta tags for client ID:
<meta name="google-signin-client_id" content="YOUR_CLIENT_ID.apps.googleusercontent.com" />iOS
Create iOS Client ID in Google Cloud Console
- Get your Client ID and iOS URL scheme
Add URL Scheme to
Info.plist- Open Xcode → Select App Target → Info → URL Types
- Add
REVERSED_CLIENT_IDfrom your iOS URL scheme - Example:
com.googleusercontent.apps.YOUR_CLIENT_ID
Configure Client ID (priority order):
clientIdininitialize()methodiosClientIdincapacitor.config.jsonclientIdincapacitor.config.jsonCLIENT_IDinGoogleService-Info.plist
Example capacitor.config.json:
{
"plugins": {
"GoogleAuth": {
"iosClientId": "YOUR_IOS_CLIENT_ID.apps.googleusercontent.com",
"scopes": ["profile", "email"],
"forceCodeForRefreshToken": true
}
}
}Android
- Configure Client ID (priority order):
clientIdininitialize()methodandroidClientIdincapacitor.config.jsonclientIdincapacitor.config.jsonserver_client_idinstrings.xml
Option 1: Using capacitor.config.json (Recommended)
{
"plugins": {
"GoogleAuth": {
"androidClientId": "YOUR_ANDROID_CLIENT_ID.apps.googleusercontent.com",
"scopes": ["profile", "email"],
"forceCodeForRefreshToken": true
}
}
}Option 2: Using strings.xml
<!-- android/app/src/main/res/values/strings.xml -->
<resources>
<string name="server_client_id">YOUR_WEB_CLIENT_ID.apps.googleusercontent.com</string>
</resources>Optional: Override Play Services Auth version in variables.gradle:
ext {
gmsPlayServicesAuthVersion = '21.2.0'
}📚 Core API
Initialize
await GoogleAuth.initialize(options?: InitOptions);Options:
| Option | Type | Description | Default |
|--------|------|-------------|---------|
| clientId | string | Client ID from Google Console | - |
| scopes | string[] | OAuth scopes to request | ['email', 'profile', 'openid'] |
| grantOfflineAccess | boolean | Request refresh tokens | false |
| autoRestoreSession | boolean | Auto-restore saved session on init | false |
| refreshThreshold | number | Seconds before expiry to trigger refresh | 300 |
Sign In
const user = await GoogleAuth.signIn();Returns user information with authentication tokens:
{
id: string;
email: string;
name: string;
familyName: string;
givenName: string;
imageUrl: string;
serverAuthCode: string;
authentication: {
accessToken: string;
idToken: string;
refreshToken?: string;
expiresIn: number; // ⭐ New: seconds until expiration
issuedAt: number; // ⭐ New: timestamp when issued
expiresAt: number; // ⭐ New: expiration timestamp
}
}Refresh Token
const auth = await GoogleAuth.refresh(options?: RefreshOptions);Options:
| Option | Type | Description | Default |
|--------|------|-------------|---------|
| forceRefresh | boolean | Force refresh even if token is valid | false |
Smart refresh logic:
- If
forceRefresh: falseand token valid for > 5 min → returns current token - If
forceRefresh: true→ always gets new token - On mobile: uses refresh token (no user prompt)
- On web: may require user interaction
Sign Out
await GoogleAuth.signOut();Signs out the user and clears saved session.
Token Validation (⭐ New)
const validation = await GoogleAuth.isTokenValid();Returns:
{
valid: boolean; // Is token currently valid?
expiresIn: number; // Seconds remaining until expiration
needsRefresh: boolean; // Should refresh soon? (< threshold)
hasRefreshToken: boolean; // Can refresh without user prompt?
}Example:
const validation = await GoogleAuth.isTokenValid();
if (!validation.valid) {
console.log('Token expired, refreshing...');
await GoogleAuth.refresh();
} else if (validation.needsRefresh) {
console.log('Token expiring soon, refresh in background');
GoogleAuth.refresh().catch(console.error);
}Session Management (⭐ New)
Save Session
await GoogleAuth.saveSession();Saves current authentication state to secure storage. Call after sign-in or refresh for persistence.
Restore Session
const user = await GoogleAuth.restoreSession();Restores previously saved session:
- Returns
nullif no saved session exists - Auto-refreshes token if expired
- Returns user object if successful
Check Saved Session
const { exists } = await GoogleAuth.hasSavedSession();Returns true if a saved session exists.
Clear Session
await GoogleAuth.clearSession();Clears saved session from storage (doesn't sign out from Google).
🎯 Predefined Scopes (⭐ New)
Use GoogleAuthScopes constants for common scopes:
import { GoogleAuthScopes } from '@zeattacker/capacitor-google-auth';
await GoogleAuth.initialize({
scopes: [
GoogleAuthScopes.DRIVE_FILE, // App-created files only ✅ No verification needed!
GoogleAuthScopes.DRIVE_APPDATA, // App-specific data folder
GoogleAuthScopes.SPREADSHEETS, // Full spreadsheet access (requires verification)
GoogleAuthScopes.SPREADSHEETS_READONLY, // Read-only spreadsheets
GoogleAuthScopes.EMAIL, // Email address
GoogleAuthScopes.PROFILE, // Basic profile
GoogleAuthScopes.OPENID, // OpenID Connect
]
});💎 Recommended: drive.file Scope
For RxDB/database sync apps, use GoogleAuthScopes.DRIVE_FILE:
✅ Benefits:
- Access only to files your app creates
- No OAuth verification required from Google
- Perfect for sync spreadsheets
- Better user privacy
- Faster to production
scopes: [GoogleAuthScopes.DRIVE_FILE] // 👈 Best choice for sync apps🔄 Common Patterns
Pattern 1: App Initialization with Auto-Restore
// app.component.ts (Angular) or App.tsx (React)
async function initializeApp() {
await GoogleAuth.initialize({
clientId: 'YOUR_CLIENT_ID.apps.googleusercontent.com',
scopes: [GoogleAuthScopes.DRIVE_FILE],
grantOfflineAccess: true,
autoRestoreSession: true, // ← Auto-restore on launch
});
// Session will be restored automatically if available
// No need to call restoreSession() manually
}Pattern 2: Manual Session Restore with Fallback
async function ensureAuthenticated() {
// Try to restore saved session
let user = await GoogleAuth.restoreSession();
if (!user) {
// No saved session, prompt user to sign in
user = await GoogleAuth.signIn();
await GoogleAuth.saveSession();
}
return user;
}Pattern 3: Token Validation Before API Calls
async function makeAuthenticatedRequest(url: string) {
// Always check token validity first
const validation = await GoogleAuth.isTokenValid();
if (!validation.valid) {
// Refresh if expired
await GoogleAuth.refresh();
await GoogleAuth.saveSession();
}
// Now make the request
const response = await fetch(url, {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
return response;
}Pattern 4: Background Token Refresh
// Refresh tokens periodically in background
setInterval(async () => {
const validation = await GoogleAuth.isTokenValid();
if (validation.needsRefresh && validation.hasRefreshToken) {
try {
await GoogleAuth.refresh();
await GoogleAuth.saveSession();
console.log('Token refreshed in background');
} catch (error) {
console.error('Background refresh failed:', error);
}
}
}, 5 * 60 * 1000); // Every 5 minutes🎓 Framework Examples
Angular
// app.component.ts
import { Component } from '@angular/core';
import { Platform } from '@ionic/angular';
import { GoogleAuth } from '@zeattacker/capacitor-google-auth';
@Component({
selector: 'app-root',
templateUrl: 'app.component.html',
})
export class AppComponent {
constructor(private platform: Platform) {
this.initializeApp();
}
async initializeApp() {
await this.platform.ready();
await GoogleAuth.initialize({
scopes: ['profile', 'email'],
grantOfflineAccess: true,
autoRestoreSession: true,
});
}
async signIn() {
const user = await GoogleAuth.signIn();
await GoogleAuth.saveSession();
console.log('Signed in:', user.email);
}
async signOut() {
await GoogleAuth.signOut();
console.log('Signed out');
}
}React
// App.tsx
import { useEffect, useState } from 'react';
import { GoogleAuth, User } from '@zeattacker/capacitor-google-auth';
function App() {
const [user, setUser] = useState<User | null>(null);
useEffect(() => {
initAuth();
}, []);
const initAuth = async () => {
await GoogleAuth.initialize({
clientId: 'YOUR_CLIENT_ID.apps.googleusercontent.com',
scopes: ['profile', 'email'],
grantOfflineAccess: true,
autoRestoreSession: true,
});
// Try to restore session
const restoredUser = await GoogleAuth.restoreSession();
if (restoredUser) {
setUser(restoredUser);
}
};
const signIn = async () => {
const user = await GoogleAuth.signIn();
await GoogleAuth.saveSession();
setUser(user);
};
const signOut = async () => {
await GoogleAuth.signOut();
setUser(null);
};
return (
<div>
{user ? (
<div>
<p>Welcome, {user.name}!</p>
<button onClick={signOut}>Sign Out</button>
</div>
) : (
<button onClick={signIn}>Sign In with Google</button>
)}
</div>
);
}
export default App;Vue 3
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { GoogleAuth } from '@zeattacker/capacitor-google-auth';
const user = ref(null);
onMounted(async () => {
await GoogleAuth.initialize({
scopes: ['profile', 'email'],
grantOfflineAccess: true,
autoRestoreSession: true,
});
// Try to restore session
user.value = await GoogleAuth.restoreSession();
});
const signIn = async () => {
user.value = await GoogleAuth.signIn();
await GoogleAuth.saveSession();
};
const signOut = async () => {
await GoogleAuth.signOut();
user.value = null;
};
</script>
<template>
<div>
<div v-if="user">
<p>Welcome, {{ user.name }}!</p>
<button @click="signOut">Sign Out</button>
</div>
<button v-else @click="signIn">Sign In with Google</button>
</div>
</template>🔍 Troubleshooting
Sessions not persisting across app restarts
Solution: Ensure you're calling saveSession() after sign-in and token refresh:
const user = await GoogleAuth.signIn();
await GoogleAuth.saveSession(); // ← Don't forget this!Token expired errors when making API calls
Solution: Always validate tokens before API calls:
const validation = await GoogleAuth.isTokenValid();
if (!validation.valid) {
await GoogleAuth.refresh();
}
// Now make your API call"Refresh token not available" on web
Explanation: Web platform doesn't support automatic refresh without user interaction. Users must re-authenticate.
Solution: Handle web platform differently or accept re-authentication:
import { Capacitor } from '@capacitor/core';
if (Capacitor.getPlatform() === 'web') {
// Web: re-authenticate
await GoogleAuth.signIn();
} else {
// Mobile: can refresh silently
await GoogleAuth.refresh();
}iOS build errors about GoogleSignIn
Solution: Ensure you've added the URL scheme to Info.plist:
- Open Xcode
- Select your app target
- Go to Info → URL Types
- Add
REVERSED_CLIENT_IDfrom Google Console
📖 Additional Resources
- Google Sheets Sync Guide - Complete guide for RxDB + Google Sheets integration
- Google OAuth Scopes - Full list of available scopes
- Google Cloud Console - Create and manage OAuth credentials
- Capacitor Documentation - Learn more about Capacitor
🤝 Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Guidelines:
- Follow existing code style
- Add tests for new features
- Update documentation
- Keep features aligned with official Google Auth library
