Skip to main content

Capacitor Auth Manager: AI integration guide

Release: 3.0.0. Audience: Claude Code, Codex, and developers integrating Google sign-in. Use this guide from the installed package; the npm README and documentation describe the same release.

🧭 Table of Contents #​

🔧 Contract #​

DecisionContract
Enabled providerAuthProvider.GOOGLE only; other recognized providers throw PROVIDER_NOT_ENABLED
Application responsibilityGoogle OAuth registration, Firebase initialization, app sessions, authorization, translations
Package responsibilityPlatform dispatch, Google SDK invocation, normalized { user, credential }, observable local state
Firebase dependencyNone; install Firebase in the app
Node≥24 for installation/tooling; imports are server-safe, sign-in runs in a browser or native app
Capacitor@capacitor/core is required even on web; peers support 7.4.2 and 8.x
Native floorsAndroid API 24; iOS 15; native builds require JDK 21
Upgrade from 2.4.xNode floor rises, iOS floor rises, Angular peers are 22.x; web may return an access token instead of an ID token

Read the actual installed .d.ts declarations before inventing a method. Public imports are capacitor-auth-manager, /core, /react, /vue, /angular, /providers/web, and /package.json. The /providers/web entry exports GoogleAuthProviderWeb; construct it with Google options or an injected provider config. React exports useAuth, useAuthState, useUser, useIsAuthenticated, useAuthProvider. The root exports the auth singleton, AuthProvider, AuthErrorCode, AuthError, isAuthError, storage classes, logger, and shared types.

📦 Setup #​

yarn add capacitor-auth-manager@3.0.0 @capacitor/core firebase
# Native applications only, after the platform projects exist:
yarn cap sync

Use the app's existing Capacitor CLI and keep its core, CLI, Android, and iOS versions aligned. Configure once in the browser application bootstrap, before rendering enabled sign-in controls. OAuth client IDs are public identifiers; client secrets never belong in a frontend or Capacitor config.

import { auth, AuthProvider } from 'capacitor-auth-manager';

export async function prepareGoogle(clientId: string, iosClientId?: string) {
auth.configure({
providers: {
[AuthProvider.GOOGLE]: {
clientId,
serverClientId: clientId,
iosClientId,
webFlow: 'popup',
},
},
persistence: 'memory',
});
await auth.prepare(AuthProvider.GOOGLE);
}

prepare() loads Google Identity Services or initializes the native SDK without opening a sign-in prompt. Disable the app's sign-in button until preparation succeeds. Call sign-in from a direct click, never an effect, a timeout, or a background refresh. Keep configure() outside render loops; configure before prepare(). A provider instance keeps its initial options until disposed; do not rotate client IDs during a session.

🚀 Firebase handoff #​

The consuming app initializes Firebase and supplies its Auth instance. This function works with both web flows and native credentials:

import { auth, AuthProvider } from 'capacitor-auth-manager';
import { GoogleAuthProvider, signInWithCredential, signOut, type Auth } from 'firebase/auth';

export async function signInGoogle(firebaseAuth: Auth) {
const { credential } = await auth.signIn(AuthProvider.GOOGLE);
const { idToken, accessToken } = credential;
if (!idToken && !accessToken) throw new Error('Google returned no usable credential');
try {
return await signInWithCredential(
firebaseAuth,
GoogleAuthProvider.credential(idToken ?? null, accessToken ?? null),
);
} catch (error) {
await auth.signOut().catch(() => undefined);
throw error;
}
}

export async function signOutGoogle(firebaseAuth: Auth) {
// Attempt both sign-outs; failure in one must not skip the other.
const results = await Promise.allSettled([signOut(firebaseAuth), auth.signOut()]);
const failed = results.find((result) => result.status === 'rejected');
if (failed?.status === 'rejected') throw failed.reason;
}

Use Firebase's onAuthStateChanged to drive protected routes and your app's authenticated UI. The package's user and isAuthenticated indicate the local Google flow, not a verified Firebase session. For authenticated backend requests, get a Firebase ID token from firebaseAuth.currentUser.getIdToken(); auth.getIdToken() returns a Google ID token and is not interchangeable with it.

📱 Platform setup #​

PlatformRequired configuration
WebEnable Google in Firebase Auth. Register production and development origins on the Web OAuth client and app domains in Firebase Auth. Load GIS from https://accounts.google.com/gsi/client; allow the Google endpoints required by your CSP. Popup flows require a click and appropriate COOP settings.
AndroidRegister the exact application ID and debug, release, and Play App Signing SHA-1/SHA-256 fingerprints. Use the Web OAuth client ID as serverClientId. A Google account/Play Services must be available. google-services.json belongs to the consuming Firebase app configuration, not this package.
iOSRegister the exact bundle ID and iOS OAuth client. Set iosClientId or GIDClientID, and the reversed iOS client ID in CFBundleURLTypes. CocoaPods consumers use GoogleSignIn 10.x; set the deployment target to iOS 15 or later. With Capacitor 8 use yarn cap add ios --packagemanager CocoaPods; this package currently has no Swift Package Manager manifest.

Web, Android, and iOS cover the native steps.

🧩 Tokens and state #​

FlowCredential fields
Web webFlow: 'popup'accessToken; no idToken or refresh token
Web webFlow: 'one-tap'idToken; no access token
Web webFlow: 'auto' (default)One-Tap ID token or popup access token; suppression may require another click to open a popup
AndroididToken; no access token/server auth code from the sign-in call
iOSidToken, accessToken, optional serverAuthCode when configured

auth.getIdToken({forceRefresh: true}) can prompt again. A popup-only web session has no Google ID token. Use Firebase for background app-session refresh; avoid calling Google interactive refresh on a timer. auth.signOut() clears the package's local session; also sign out Firebase as shown above. Android revokeAccess() clears local, GoogleSignIn and Credential Manager state, not Google's server-side grant.

Android sign-in (androidFlow, since 3.1.0): 'auto' runs GoogleSignIn (the path @codetrix-studio/capacitor-google-auth used) and falls back to Credential Manager on any failure except a cancel. A configuration error names the installed package and signing SHA-1 — register that SHA-1 on the app's Android OAuth client. Never force play-services-auth 22 (it removed GoogleSignIn). Account linking, account deletion, and profile updates in Firebase belong to Firebase's API.

Default JS storage persists profile metadata, never token strings. Android credentials are kept in process memory; iOS relies on GoogleSignIn's SDK Keychain and no longer duplicates credentials in UserDefaults. The major upgrade removes the package's legacy Android credential store and iOS credential copies; users may need to sign in again. CapacitorPreferencesStorage is not encrypted secure storage. If your app persists secrets, it owns a reviewed Keychain/Keystore adapter. A cached profile is not an authorization decision. Firebase/server verification is required.

🚑 Errors and security #​

SignalApp action
USER_CANCELLED, POPUP_CLOSED_BY_USERReturn to the idle sign-in UI
POPUP_BLOCKEDRetry from a fresh click with webFlow: 'popup'; preload via prepare()
MISSING_CONFIGURATIONCorrect client IDs/native setup; do not retry in a loop
NETWORK_ERROR, SDK load failureShow a translated retry action after connectivity recovers
OPERATION_NOT_ALLOWED during sign-inAn operation is already pending; keep the button disabled
INVALID_TOKEN, INVALID_NONCE, TOKEN_EXPIREDReject the credential and reauthenticate
PROVIDER_NOT_ENABLEDUse Google; other providers are roadmap code

Use isAuthError(error) and AuthErrorCode to map errors to the app's i18n keys. Catch rejected event-handler promises; never log credentials, tokens, or client secrets. The browser validates issuer, audience, subject, expiry, a configured nonce, and a configured Workspace domain, but does not verify JWT signatures. Google/Firebase/server verification establishes trust. Never send an OAuth access token to an ID-token-only backend.

For a custom ID-token backend, choose the One-Tap/native ID-token path, require credential.idToken, verify signature/issuer/audience/expiry/nonce on the server, and issue your own session. Do not decode a token and trust its claims.

🧪 App acceptance checks #​

Verify a fresh sign-in, returning sign-in, cancel/retry, blocked popup, offline/retry, cold restart, token expiry, and sign-out on each deployed platform. Verify release signing fingerprints and production origins, not only debug setup. Confirm Firebase receives the expected user, protected routes follow Firebase state, and storage/logs contain no secret tokens. Native compile checks and mocked Google tests cannot certify your OAuth configuration or a real-device sign-in.