Para iniciar sesión con Auth0 en una app de Capacitor, crea una Nativo aplicación en Auth0, registra una URL de callback con esquema personalizado como com.example.app://oauth/auth0, y ejecuta el flujo de autorización OpenID Connect code con PKCE a través de un navegador del sistema. Con @capgo/capacitor-social-login eso es una sola initialize llamada con tus endpoints de Auth0 y una SocialLogin.login({ provider: 'oauth2', options: { providerId: 'auth0' } }).
Esta guía cubre la configuración del dashboard de Auth0, el cliente code para iOS, Android y web, tokens de acceso API, tokens de refresco, cierre de sesión y los errores con los que te encontrarás. Está escrita para Capacitor 8 a octubre de 2026.
Cómo funciona el flujo de Auth0 en Capacitor
Auth0 aloja la página de inicio de sesión (Universal Login). Tu app nunca ve la contraseña. El flujo es:
- La app genera un verificador y un desafío PKCE, y luego abre
https://YOUR_TENANT/authorizeen un navegador seguro. - El usuario inicia sesión (contraseña, social, passkey, MFA, lo que hayas configurado en Auth0).
- Auth0 redirige a tu URL de callback con un
code. - La app intercambia el code y el verificador PKCE en
/oauth/tokenpor un token de ID, un token de acceso y, opcionalmente, un token de refresco.
El navegador utilizado depende de la plataforma:
| Plataforma | Navegador | Manejo de callbacks |
|---|---|---|
| iOS | ASWebAuthenticationSession |
Manejado por el sistema, sin Info.plist cambiar |
| Android | WebView integrada por defecto, o Chrome Custom Tabs con androidUseCustomTabs: true |
Custom Tabs requieren un intent filter |
| Web | Popup (por defecto) o redirección a página completa | handleRedirectCallback() en la página de retorno |
En Android, usa Custom Tabs. Comparten cookies con Chrome, soportan passkeys y gestores de contraseñas, y siguen el estándar RFC 8252 (OAuth para apps nativas), que es lo que espera Auth0.
Paso 1: Configura la aplicación de Auth0
- En el Dashboard de Auth0, ve a Aplicaciones > Aplicaciones > Crear aplicación.
- Elige Nativo and create it.
- En el Configuración pestaña, toma nota de la Dominio (por ejemplo
example.eu.auth0.com) y la ID de cliente. - En Credenciales, comprueba que el método de autenticación es Ninguno. Esa es la configuración de cliente público utilizada con PKCE.
URLs de callback y logout
Completa estos campos Configuración > URIs de la aplicación:
| Campo | Valor para móvil | Valor para web |
|---|---|---|
| URLs de callback permitidas | com.example.app://oauth/auth0 |
https://app.example.com/auth/callback |
| URLs de logout permitidas | com.example.app://oauth/logout |
https://app.example.com/ |
| Orígenes web permitidos | No es necesario | https://app.example.com |
Separa varios valores con comas. Usa tu Capacitor appId como scheme. Un scheme de dominio inverso evita colisiones con otras apps. Si también publicas un build web, muchos equipos crean una Aplicación de página única en Auth0 para ello, de modo que la configuración web y móvil no se mezclen.
Refrescar tokens
Si los usuarios deben permanecer iniciados en la sesión:
- En Settings > Refresh Token Rotation, activa la rotación. Cada refresh devuelve un nuevo refresh token e invalida el anterior.
- Configura una duración absoluta y de inactividad adecuada a tu producto.
- Si llamas a tu propio API, abre Aplicaciones > APIs > tu API > Configuración y habilita Permitir acceso sin conexión.
- Solicita el
offline_accessscope en la app.
audiencia de API
Si tu backend valida los access tokens de Auth0, crea un API en Aplicaciones > APIs con un identificador como https://api.example.com. Pasa ese identificador como audience. Sin él, el access token solo servirá para el /userinfo endpoint.
Paso 2: Instalar y configurar el plugin
bun add @capgo/capacitor-social-login
bunx cap sync
Si Auth0 es tu único proveedor, desactiva los SDKs sociales incluidos para mantener el binario pequeño:
// capacitor.config.ts
plugins: {
SocialLogin: {
providers: { google: false, facebook: false, apple: false, twitter: false },
},
},
El motor OAuth2 genérico no depende de esos SDKs.
Filtro de intent de Android para Custom Tabs
Añade el esquema de callback a MainActivity en android/app/src/main/AndroidManifest.xml:
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="com.example.app" android:host="oauth" />
</intent-filter>
La plantilla de Capacitor ya configura android:launchMode="singleTask" en MainActivity, que necesita la redirección. Consérvalo.
Paso 3: Inicializa el proveedor de Auth0
import { Capacitor } from '@capacitor/core';
import { SocialLogin } from '@capgo/capacitor-social-login';
const AUTH0_DOMAIN = 'https://example.eu.auth0.com';
const isWeb = Capacitor.getPlatform() === 'web';
export async function initAuth() {
await SocialLogin.initialize({
oauth2: {
auth0: {
appId: 'YOUR_AUTH0_CLIENT_ID',
authorizationBaseUrl: `${AUTH0_DOMAIN}/authorize`,
accessTokenEndpoint: `${AUTH0_DOMAIN}/oauth/token`,
resourceUrl: `${AUTH0_DOMAIN}/userinfo`,
redirectUrl: isWeb
? 'https://app.example.com/auth/callback'
: 'com.example.app://oauth/auth0',
scope: 'openid profile email offline_access',
pkceEnabled: true,
additionalParameters: {
audience: 'https://api.example.com',
},
logoutUrl: `${AUTH0_DOMAIN}/oidc/logout`,
postLogoutRedirectUrl: isWeb
? 'https://app.example.com/'
: 'com.example.app://oauth/logout',
androidUseCustomTabs: true,
},
},
});
}
Puedes reemplazar los endpoints explícitos con issuerUrl: AUTH0_DOMAIN. El plugin luego lee /.well-known/openid-configuration. Los valores explícitos tienen prioridad sobre los detectados, lo cual ayuda cuando usas un dominio personalizado de Auth0.
Si estás migrando desde Ionic Auth Connect, el plugin también exporta SocialLoginAuthConnect con un auth0 preset que toma domain, clientId, redirectUrl y audience. Consulta la guía de migración de Auth Connect.
Paso 4: Iniciar sesión
import { SocialLogin } from '@capgo/capacitor-social-login';
export async function signInWithAuth0() {
try {
const { result } = await SocialLogin.login({
provider: 'oauth2',
options: { providerId: 'auth0' },
});
return {
idToken: result.idToken,
accessToken: result.accessToken?.token,
expiresAt: result.accessToken?.expires,
refreshToken: result.refreshToken,
user: result.resourceData, // JSON from /userinfo
};
} catch (error: any) {
if (error?.code === 'USER_CANCELLED') return null;
throw error;
}
}
Opciones de inicio de sesión útiles:
prompt: 'login'fuerza la página de inicio de sesión aunque Auth0 aún tenga una cookie de sesión.loginHint: 'user@example.com'precarga el correo electrónico.additionalParameters: { connection: 'google-oauth2' }omite Universal Login y va directamente a una conexión.additionalParameters: { screen_hint: 'signup' }abre la pantalla de registro.
Callback de redirección web
En web, el flujo de popup predeterminado no requiere nada extra. Si usas flow: 'redirect', lee el resultado en la página de callback:
const result = await SocialLogin.handleRedirectCallback();
if (result?.provider === 'oauth2') {
console.log(result.result.idToken);
}
handleRedirectCallback es solo para web y se rechaza en iOS y Android.
Paso 5: Lee el ID token y llama a tu API
Decodifica el ID token en el cliente solo para mostrarlo:
const { claims } = await SocialLogin.decodeIdToken({ idToken: result.idToken! });
console.log(claims.email, claims.name, claims.sub);
decodeIdToken no verifica la firma. Para cualquier cosa relacionada con la seguridad, envía el access token a tu API y validadlo allí:
import { createRemoteJWKSet, jwtVerify } from 'jose';
const jwks = createRemoteJWKSet(new URL('https://example.eu.auth0.com/.well-known/jwks.json'));
export async function verifyAuth0AccessToken(token: string) {
const { payload } = await jwtVerify(token, jwks, {
issuer: 'https://example.eu.auth0.com/', // Auth0 issuers end with a slash
audience: 'https://api.example.com',
});
return payload; // payload.sub is the Auth0 user ID
}
Paso 6: Actualiza el access token
import { SocialLogin } from '@capgo/capacitor-social-login';
export async function getValidAccessToken() {
const { isLoggedIn } = await SocialLogin.isLoggedIn({ provider: 'oauth2', providerId: 'auth0' });
if (!isLoggedIn) {
// access token expired: try the stored refresh token
try {
await SocialLogin.refresh({ provider: 'oauth2', options: { providerId: 'auth0' } });
} catch {
// refresh token expired, revoked or already rotated: sign in again
const loginResult = await signInWithAuth0();
if (!loginResult) throw new Error('Sign-in cancelled');
}
}
const { accessToken } = await SocialLogin.getAuthorizationCode({
provider: 'oauth2',
providerId: 'auth0',
});
return accessToken;
}
refresh se rechaza cuando el refresh token está caducado o revocado. Con la rotación activada, un refresh token que ya se usó también falla, así que maneja siempre el rechazo enviando al usuario de nuevo a iniciar sesión.
refresh usa el refresh token que el plugin guardó al iniciar sesión. Si guardas el refresh token tú mismo, llama a SocialLogin.refreshToken({ provider: 'oauth2', providerId: 'auth0', refreshToken }), que devuelve la respuesta completa del nuevo token. Con la rotación activada, guarda el nuevo refresh token cada vez.
El plugin guarda su copia del token en almacenamiento privado de la app. Si tu revisión de seguridad exige Keychain o Keystore, guarda el refresh token con NativeBiometric.setData({ key, value }) de los @capgo/capacitor-native-biometric y pásalo a refreshToken tú mismo. Nuestra guía de almacenamiento seguro de tokens compara las opciones.
Paso 7: Cerrar sesión
await SocialLogin.logout({ provider: 'oauth2', providerId: 'auth0' });
El plugin elimina los tokens almacenados y luego abre logoutUrl con id_token_hint y post_logout_redirect_uri para que Auth0 borre su cookie de sesión. El /oidc/logout endpoint entiende esos parámetros, y la URL de redirección debe estar en la lista de URLs de logout permitidas.
En iOS y Android, la página de finalización de sesión se abre en el navegador del sistema, por lo que el usuario sale brevemente de la app. Si solo quieres un cierre de sesión local, omite logoutUrl y pasar prompt: 'login' en el próximo inicio de sesión para que una sesión de Auth0 recordada no vuelva a iniciar al usuario automáticamente. Configuración iosPrefersEphemeralSession: true también evita las cookies compartidas en iOS, a costa de no tener SSO entre apps.
Solución de problemas
| Error | Causa | Solución |
|---|---|---|
Callback URL mismatch |
URL de redirección no está en las URLs de callback permitidas | Copia el valor exacto de code al dashboard, verifica el scheme y el host |
Unauthorized o invalid_client en /oauth/token |
El tipo de app no es Native o el método de autenticación no es None | Recrea como Native, configura la autenticación del token endpoint en None |
No refreshToken en el resultado |
offline_access falta o API no permite acceso offline |
Añade el scope, activa Allow Offline Access, activa la rotación |
| El access token no puede ser verificado por API | No audience enviado |
Añadir audience en additionalParameters |
| La app no se reabre tras iniciar sesión en Android | El scheme o host del intent filter no coincide redirectUrl |
Coincidir com.example.app y oauth exactamente |
OAuth2 provider "auth0" not configured |
login se llama antes de initialize o el key no coincide |
Await initialize al inicio, usa el mismo providerId |
| La página de logout sigue abierta | post_logout_redirect_uri no está en Allowed Logout URLs |
Añádela o usa solo logout local |
| Error CORS en el intercambio de token | El origen no está en Allowed Web Origins | Añade el origen web o usa una aplicación SPA para web |
Configurar logsEnabled: true en la configuración del provider para imprimir las URLs generadas y los detalles del intercambio de tokens mientras debugueas.
Lanza correcciones de auth sin pasar por la tienda
Cambiar los scopes, la audiencia o el manejo de errores solo requiere TypeScript. Con actualizaciones Live Update de Capgo puedes lanzar esos fixes a los usuarios el mismo día. Los cambios nativos, como un nuevo intent filter, aún requieren un nuevo binario, que Build de Capgo puedes generar en la nube.