Better Auth Plugin for Capacitor
The `better-auth-capacitor` package provides an offline-first authentication client plugin specifically designed for Capacitor and Ionic mobile applications. It integrates with the broader `better-auth` ecosystem, offering features like persistent session storage via `@capacitor/preferences`, robust OAuth flow support using system browsers and deep link callbacks, and automatic session refreshing when the app regains focus or network connectivity. The current stable version is 0.3.6, with recent releases indicating an active development cadence focusing on features and bug fixes. Key differentiators include its focus on mobile-specific authentication challenges, seamless integration with Capacitor's native capabilities, and utilities for secure token extraction and custom authentication endpoint integration.
Common errors
-
OAuth flow fails to complete or redirects to a blank page on mobile.
cause The Capacitor app's deep linking scheme is misconfigured, or `disableDefaultFetchPlugins` is not correctly set, causing Better Auth's default web redirect plugin to interfere with the native OAuth flow.fixVerify that `capacitor.config.json` has `urlSchemes` defined for your app. Ensure the `scheme` option in `withCapacitor` or `capacitorClient` matches this configuration. If using manual setup, confirm `disableDefaultFetchPlugins: isNativePlatform()` is active. -
Authentication token is not persisted across app restarts or after closing the app.
cause The `better-auth-capacitor` client relies on `@capacitor/preferences` for persistent storage. This issue usually indicates that `storagePrefix` is misconfigured or `disableCache` is inadvertently set to `true`.fixCheck the `storagePrefix` in your `withCapacitor` or `capacitorClient` options and ensure it's consistent. Confirm that `disableCache` is either `false` or omitted if you intend to use caching. Verify `@capacitor/preferences` is installed and functioning correctly. -
Server-side errors related to origin override or authorization proxy when using Capacitor.
cause The `capacitor()` server plugin is not correctly integrated into your `better-auth` server configuration, or there's a conflict with custom CORS settings.fixAdd `capacitor()` to the `plugins` array of your `betterAuth` server instance. If encountering CORS issues, investigate the `disableOriginOverride` option in the `capacitor()` plugin and ensure your server's CORS settings are compatible with Capacitor's requests.
Warnings
- gotcha OAuth flows, particularly social logins, require proper deep linking configuration in your Capacitor app's `capacitor.config.json` and client-side `scheme` option to redirect back to the app after authentication. Without it, the flow may get stuck in the browser.
- breaking When integrating Better Auth with Capacitor for OAuth, it's critical to disable Better Auth's default fetch redirect plugins on native platforms. The `withCapacitor` wrapper automatically handles this by setting `disableDefaultFetchPlugins: true`. Manual setup requires this configuration to prevent unwanted browser redirects and allow the native auth sheet to take over.
- gotcha On native platforms (iOS/Android), the WebView does not share cookies directly with the system browser used for OAuth. `better-auth-capacitor` is designed to handle this by using deep links to pass session tokens back into the app, but rely on Capacitor's native storage (`@capacitor/preferences`) for persistent session management.
- gotcha The plugin has peer dependencies on several Capacitor plugins, specifically `@capacitor/app`, `@capacitor/core`, `@capacitor/network`, and `@capacitor/preferences`. Additionally, it depends on `@better-auth/core` and `better-auth` itself. Missing or incompatible versions of these dependencies can lead to runtime errors or unexpected behavior.
Install
-
npm install better-auth-capacitor -
yarn add better-auth-capacitor -
pnpm add better-auth-capacitor
Imports
- withCapacitor
const withCapacitor = require('better-auth-capacitor/client');import { withCapacitor } from 'better-auth-capacitor/client'; - capacitor
const capacitor = require('better-auth-capacitor');import { capacitor } from 'better-auth-capacitor'; - getCapacitorAuthToken
const getCapacitorAuthToken = require('better-auth-capacitor/client');import { getCapacitorAuthToken } from 'better-auth-capacitor/client'; - setCapacitorAuthToken
import { setCapacitorAuthToken } from 'better-auth-capacitor/client';
Quickstart
import { withCapacitor } from 'better-auth-capacitor/client';
import { createAuthClient } from 'better-auth/client';
import { isPlatform } from '@ionic/react'; // Assuming Ionic/React context for platform checks
// --- Server-side configuration (example, typically in your Node.js/Edge function backend) ---
// import { betterAuth } from 'better-auth';
// import { capacitor } from 'better-auth-capacitor';
//
// export const auth = betterAuth({
// // ... your existing Better Auth config
// plugins: [
// capacitor({ disableOriginOverride: false }), // Integrate Capacitor server plugin
// ],
// });
// --- Client-side configuration in your Capacitor/Ionic app ---
// Define your base URL and deep link scheme
const API_BASE_URL = process.env.VITE_API_BASE_URL ?? 'https://api.example.com';
const APP_SCHEME = process.env.VITE_APP_SCHEME ?? 'myapp'; // Your app's custom URL scheme for deep links
// Create the Better Auth client using the withCapacitor wrapper
const authClient = createAuthClient(
withCapacitor({
baseURL: API_BASE_URL,
},
{
scheme: APP_SCHEME,
storagePrefix: 'better-auth',
// Optional: disableCache will prevent session caching in preferences if set to true
// disableCache: false,
})
);
// Example of usage: Sign in with a social provider
async function initiateSocialLogin(provider: string) {
try {
await authClient.signIn.social({
provider,
// The redirectTo URL should be handled by your deep linking setup
// and will trigger the callback logic in better-auth-capacitor.
redirectTo: `${APP_SCHEME}://callback/auth`,
});
console.log(`OAuth flow for ${provider} initiated.`);
} catch (error) {
console.error(`Error initiating social login for ${provider}:`, error);
}
}
// Example of getting the bearer token for API requests
import { getCapacitorAuthToken } from 'better-auth-capacitor/client';
async function fetchAuthenticatedData() {
const token = await getCapacitorAuthToken({
storagePrefix: 'better-auth',
cookiePrefix: 'better-auth', // Ensure this matches your server's cookie prefix
});
if (token) {
console.log('Retrieved auth token:', token.substring(0, 10) + '...');
// Example fetch (replace with your actual API call)
try {
const response = await fetch(`${API_BASE_URL}/protected-data`, {
headers: {
Authorization: `Bearer ${token}`,
},
});
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
const data = await response.json();
console.log('Protected data:', data);
} catch (error) {
console.error('Failed to fetch protected data:', error);
}
} else {
console.log('No authentication token found.');
}
}
// Simulate app launch and a login attempt
console.log('App starting, setting up auth client...');
// In a real app, you'd call initiateSocialLogin on a button click or similar user action
// For demonstration, let's log the client and token retrieval.
console.log('Auth client initialized:', authClient);
// You might want to automatically check session or refresh on app load
// authClient.getSession().then(session => console.log('Current session:', session));
// To run these in a live app:
// setTimeout(() => initiateSocialLogin('google'), 2000);
// setTimeout(fetchAuthenticatedData, 5000);