Pasar al contenido principal

Cómo usar una autenticación mejor en aplicaciones de Capacitor (Guía de 2026)

Usar una autenticación mejorada en una aplicación Capacitor de 8: orígenes confiables, tokens de portador en lugar de cookies, correo electrónico y contraseña, inicio de sesión nativo de Google y Apple, y correcciones.

Créditos del artículo

Martín Donadieu

Escritor

Valeria

Revisor

Jordan

Editor

Cómo usar una autenticación mejorada en aplicaciones Capacitor (Guía de 2026)

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:

  1. La aplicación abre tu URL de inicio de sesión de Better Auth en el navegador del sistema (por ejemplo con) SocialLogin.openSecureWindow o un complemento de navegador para aplicaciones.
  2. Mejor Autenticación ejecuta el baile de OAuth con el proveedor y crea la sesión en el servidor.
  3. Autenticación mejorada ejecuta el baile de OAuth con el proveedor y crea la sesión en el servidor. oneTimeToken plugin para este) y redirige a un enlace profundo como com.example.app://auth?token=....
  4. 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.

Actualizaciones en vivo para aplicaciones Capacitor

Cuando haya un error en la capa de la web, envíe la corrección a través de Capgo en lugar de esperar días para la aprobación de la tienda de aplicaciones. Los usuarios obtienen la actualización en segundo plano mientras que los cambios nativos siguen en el camino de revisión normal.

Últimas noticias de nuestro Blog

Capgo te da las mejores perspectivas que necesitas para crear una aplicación móvil verdaderamente profesional.