Para usar Better Auth en una aplicación Capacitor, instale el better-auth cliente en su web code, apunte a su servidor de Better Auth, agregue los Capacitor orígenes ("capacitor://localhost en iOS, https://localhost On Android) a trustedOrigins, y cambie de cookies a tokens de portador con el bearer plugin. Correos y contraseña funcionan a través del SDK tal cual. Para Google y Apple, obtén un token de ID con un plugin nativo como @capgo/capacitor-social-login y pase el authClient.signIn.social({ idToken }).
Este guía muestra la configuración del servidor y del cliente, el almacenamiento de tokens, el inicio de sesión social, los proveedores de empresas y los errores que las personas encuentran cuando mueven una aplicación web de autenticación mejorada a Capacitor 8.
¿Qué cambia cuando la autenticación mejorada se ejecuta dentro de Capacitor
Una aplicación Capacitor es una aplicación web servida desde un origen local dentro de un WebView nativo. En comparación con un sitio web normal, tres cosas son diferentes:
| Tema | En un sitio web | En una aplicación Capacitor |
|---|---|---|
| Origen | https://app.example.com |
capacitor://localhost (iOS) https://localhost (iOS) |
| Auth server | A menudo el mismo sitio, los cookies funcionan | Diferente dominio, los cookies de terceros se bloquean por WKWebView |
| Redirecciones OAuth | Una redirección de página completa está bien | La redirección deja tu aplicación empaquetada, Google bloquea WebViews |
Las soluciones son: confiar en los orígenes Capacitor, usar tokens de portador y usar plugins nativos para el inicio de sesión social.
Step 1: Configura el servidor de autenticación mejorado
Esta ejemplo utiliza Hono, pero la configuración de autenticación mejorada es la misma para Express, route handlers de Next.js o cualquier otro runtime.
bun add better-auth hono
// auth.ts
import { betterAuth } from 'better-auth';
import { bearer } from 'better-auth/plugins';
import { db } from './db'; // your Drizzle, Prisma, Kysely or pg instance
export const auth = betterAuth({
baseURL: process.env.BETTER_AUTH_URL, // https://auth.example.com
database: db,
emailAndPassword: {
enabled: true,
},
socialProviders: {
google: {
clientId: process.env.GOOGLE_WEB_CLIENT_ID as string,
clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
},
apple: {
clientId: process.env.APPLE_SERVICE_ID as string,
clientSecret: process.env.APPLE_CLIENT_SECRET as string,
appBundleIdentifier: process.env.APPLE_BUNDLE_ID as string, // com.example.app
},
},
trustedOrigins: [
'capacitor://localhost', // iOS WebView
'https://localhost', // Android WebView (androidScheme https, the default)
'https://app.example.com', // your web build
'https://appleid.apple.com',
],
plugins: [bearer()],
});
appBundleIdentifier Importa para Apple. Los tokens del cuadro de diálogo nativo de iOS llevan tu ID de paquete como audiencia, mientras que los tokens web y Android llevan el ID de Servicios. Autenticación mejorada utiliza este campo para aceptar ambos.
Monta el manejador y permite CORS desde los orígenes de la aplicación. Exponer el set-auth-token encabezado, de lo contrario el cliente nunca ve el token de portador:
// server.ts
import { Hono } from 'hono';
import { cors } from 'hono/cors';
import { auth } from './auth';
const app = new Hono();
app.use(
'/api/auth/*',
cors({
origin: ['capacitor://localhost', 'https://localhost', 'https://app.example.com'],
allowHeaders: ['Content-Type', 'Authorization'],
allowMethods: ['GET', 'POST', 'OPTIONS'],
exposeHeaders: ['set-auth-token'],
credentials: true,
}),
);
app.on(['GET', 'POST'], '/api/auth/*', (c) => auth.handler(c.req.raw));
export default app;
Correr bunx @better-auth/cli migrate (or generate para restaurar al usuario:
Si usas React, Vue, Svelte o Solid, importa
bun add better-auth @capgo/capacitor-native-biometric
bunx cap sync
Almacene el token de sesión en el Keychain o Keystore, y mantenga una copia en memoria para que el cliente pueda leerla de manera sincrónica.
// auth-client.ts
import { createAuthClient } from 'better-auth/client';
import { NativeBiometric } from '@capgo/capacitor-native-biometric';
const TOKEN_KEY = 'session.token';
let sessionToken = '';
export async function loadSessionToken() {
try {
const { value } = await NativeBiometric.getData({ key: TOKEN_KEY });
sessionToken = value;
} catch {
sessionToken = '';
}
}
async function saveSessionToken(token: string) {
sessionToken = token;
if (token) {
await NativeBiometric.setData({ key: TOKEN_KEY, value: token });
} else {
await NativeBiometric.deleteData({ key: TOKEN_KEY }).catch(() => {});
}
}
export const authClient = createAuthClient({
baseURL: 'https://auth.example.com',
fetchOptions: {
auth: {
type: 'Bearer',
token: () => sessionToken,
},
onSuccess: async (ctx) => {
const token = ctx.response.headers.get('set-auth-token');
if (token) await saveSessionToken(token);
},
},
});
export async function clearSession() {
await saveSessionToken('');
}
Call await loadSessionToken() antes de que tu aplicación se renderice authClient.getSession() para restaurar al usuario:
import { authClient, loadSessionToken } from './auth-client';
await loadSessionToken();
const { data: session } = await authClient.getSession();
if (session) {
console.log('Signed in as', session.user.email);
}
Si utiliza React, Vue, Svelte o Solid, importe createAuthClient desde la subruta correspondientebetter-auth/react, better-auth/vuepara restaurar al usuario: useSession hook. El fetchOptions stay the same. El mismo. setData y getData métodos son nativos solo. En web, caen en cookies o sessionStorage usando Capacitor.isNativePlatform().
Paso 3: Correo electrónico y contraseña
No se necesita ningún plugin. Estos son llamadas HTTP planas.
import { authClient, clearSession } from './auth-client';
export async function signUp(email: string, password: string, name: string) {
const { data, error } = await authClient.signUp.email({ email, password, name });
if (error) throw new Error(error.message);
return data;
}
export async function signIn(email: string, password: string) {
const { data, error } = await authClient.signIn.email({ email, password });
if (error) throw new Error(error.message);
return data;
}
export async function signOut() {
await authClient.signOut();
await clearSession();
}
Para la verificación de correo electrónico y el restablecimiento de contraseña, Better Auth envía enlaces que abren una URL. Punte callbackURL at a page on your domain that is also configured as an iOS Universal Link and Android App Link, so the link opens the app on phones and the website elsewhere.
Configura Google como se describe en
¿Cómo iniciar sesión con Google utilizando __CAPGO_KEEP_0__ Cómo iniciar sesión con Google utilizando CapacitorEntonces:
import { SocialLogin } from '@capgo/capacitor-social-login';
import { authClient } from './auth-client';
await SocialLogin.initialize({
google: {
webClientId: 'WEB_CLIENT_ID.apps.googleusercontent.com',
iOSClientId: 'IOS_CLIENT_ID.apps.googleusercontent.com',
mode: 'online', // Better Auth needs the idToken, offline mode only returns a code
},
});
export async function signInWithGoogle() {
const { result } = await SocialLogin.login({ provider: 'google', options: {} });
if (result.responseType !== 'online' || !result.idToken) {
throw new Error('Google did not return an ID token');
}
const { data, error } = await authClient.signIn.social({
provider: 'google',
idToken: {
token: result.idToken,
accessToken: result.accessToken?.token,
},
});
if (error) throw new Error(error.message);
return data;
}
Better Auth verifica el token contra las claves de Google y el ID de cliente configurado, crea o vincula al usuario y devuelve una sesión. El token de portador llega en el set-auth-token título y su onSuccess almacena su valor.
Step 5: Inicio de sesión nativo de Apple
Configura Apple según se describe en Cómo iniciar sesión con Apple utilizando Capacitor. Genera un valor nonce, pasa ese valor a Apple y envía el mismo valor a Better Auth:
import { SocialLogin } from '@capgo/capacitor-social-login';
import { authClient } from './auth-client';
export async function signInWithApple() {
const nonce = crypto.randomUUID();
const { result } = await SocialLogin.login({
provider: 'apple',
options: { scopes: ['email', 'name'], nonce },
});
if (!result.idToken) throw new Error('Apple did not return an ID token');
const { data, error } = await authClient.signIn.social({
provider: 'apple',
idToken: {
token: result.idToken,
nonce,
accessToken: result.accessToken?.token,
},
});
if (error) throw new Error(error.message);
return data;
}
Apple solo comparte el nombre del usuario en la primera autorización. Si deseas que esté en el registro del usuario, actualiza el perfil justo después del primer inicio de sesión con result.profile.givenName y familyName.
Facebook sigue el mismo patrón. En iOS con inicio de sesión limitado obtienes un idTokenen otros lugares pasas el token de acceso. El Integración de autenticación mejorada página tiene el ejemplo completo de Facebook.
Paso 6: Okta, Auth0, Entra ID y otros proveedores OIDC
Mejores autenticaciones genericOAuth El plugin envía ayudantes para Auth0, Okta, Keycloak y Microsoft Entra ID, además de una configuración manual para cualquier servidor OIDC. En el desarrollo web, authClient.signIn.oauth2({ providerId }) funciona como en cualquier sitio.
On iOS and Android the flow needs the system browser, because the provider login page must not load inside your WebView. The pattern that works:
- La aplicación abre tu URL de inicio de sesión de Better Auth en el navegador del sistema (por ejemplo con)
SocialLogin.openSecureWindowo un complemento de navegador para aplicaciones. - Mejor Autenticación ejecuta el baile de OAuth con el proveedor y crea la sesión en el servidor.
- Autenticación mejorada ejecuta el baile de OAuth con el proveedor y crea la sesión en el servidor.
oneTimeTokenplugin para este) y redirige a un enlace profundo comocom.example.app://auth?token=.... - La aplicación verifica el token de una sola vez con el cliente de autenticación y recibe una sesión de portador normal.
Eso es más partes móviles que la transferencia de token de ID. Si solo necesita SSO empresarial y no necesita la base de datos de usuarios de Better Auth, iniciar sesión directamente contra el proveedor es más simple, consulte los Okta, Auth0 y Entra ID guías.
Paso 7: Protege tu API con la sesión
Tu propio API puede leer la sesión desde el token de portador con el servidor API de Better Auth:
import { Hono } from 'hono';
import { auth } from './auth';
const api = new Hono();
api.get('/me/orders', async (c) => {
const session = await auth.api.getSession({ headers: c.req.raw.headers });
if (!session) return c.json({ error: 'unauthorized' }, 401);
return c.json(await listOrders(session.user.id));
});
El plugin de portador hace getSession aceptar Authorization: Bearer <token> de la misma manera que acepta el cookie.
Troubleshooting
| Problema | Causa | Solución |
|---|---|---|
Invalid origin o 403 al iniciar sesión |
Capacitor no se confía en el origen | Agregar capacitor://localhost y https://localhost a trustedOrigins |
| Error de CORS en la consola del WebView | El servidor CORS no incluye las orígenes de la aplicación | Agregarlas a su middleware CORS, permitir Authorization |
El inicio de sesión es exitoso pero getSession devuelve null |
Depende de las cookies en dominios diferentes | Utilice el plugin de portador y envíe el token |
set-auth-token es siempre null |
No se expone la cabecera | Agregar exposeHeaders: ['set-auth-token'] Usar CORS |
Google: disallowed_useragent |
Página de OAuth cargada en el WebView | Usar inicio de sesión nativo y el idToken transbordo |
| Google ID token rechazado | La audiencia no coincide con el ID de cliente configurado | Decodifica el token con SocialLogin.decodeIdToken y compara aud con clientId |
| Apple ID token rechazado en iOS | La audiencia es el ID de paquete | Establece appBundleIdentifier en el proveedor de Apple |
| Error de nonce de Apple | Nonce diferente enviado a Apple y Better Auth | Genera una vez, reutiliza el mismo valor |
| Sesión perdida después de reiniciar la aplicación | Se almacena el token solo en memoria | Persistir con setData y cargarlo antes de la primera solicitud |
Desplegar el lado móvil
Cambios de autenticación son raramente nativos. Los orígenes y proveedores confiables son configuración del servidor, y el cliente code es TypeScript, por lo que la mayoría de las correcciones pueden enviar a través Capgo actualizaciones en vivo en lugar de una versión de almacenamiento. Solo necesita un nuevo binario cuando agregue un plugin nativo o cambie los esquemas de URL.
Guías relacionadas
- How to add authentication to a Capacitor app compara Better Auth con Firebase, Supabase y proveedores de identidad hospedados.
- Biometric authentication in Capacitor apps para desbloquear una sesión almacenada con Face ID o huella dactilar.
- plugin de inicio de sesión social para los flujos nativos de Google, Apple y Facebook.