Saltar al contenido principal
Tutorial iOS Capacitor

Cómo iniciar sesión con Apple utilizando Capacitor (2026)

Agregue Sign in with Apple a una Capacitor 8 aplicación en iOS, Android y web: configuración del desarrollador de Apple, plugin code, comprobaciones de tokens de servidor y errores comunes.

Cómo iniciar sesión con Apple utilizando Capacitor (2026)

Para agregar Sign in with Apple a una Capacitor aplicación, instale @capgo/capacitor-social-loginHabilite la capacidad de inicio de sesión con Apple en su ID de aplicación y en Xcode, llame SocialLogin.initialize({ apple: {} })Luego llame SocialLogin.login({ provider: 'apple' })On iOS abre la hoja nativa de Apple y devuelve un token de identidad. Android y web utilizan el flujo OAuth web de Apple, que requiere un ID de Servicio y una URL de redirección HTTPS.

Esta guía cubre la configuración del desarrollador de Apple, el cliente code para iOS, Android y web, la verificación del token en su servidor de backend, y los errores que es probable que encuentre. Se dirige a Capacitor 8 y Xcode 26, que App Store Connect ha requerido para subir desde abril de 2026.

¿Qué necesitas antes de empezar

Requisito ¿Por qué
Membresía del programa de desarrolladores de Apple pagado App IDs, IDs de Servicios y claves de inicio de sesión con Apple solo están disponibles para miembros
Un Mac con Xcode 26 Se necesita para crear y subir aplicaciones de iOS en 2026
Capacitor 8 proyecto El plugin v8 apunta a Capacitor 8
Un dominio HTTPS que controlas Sólo para Android y web. Apple se niega localhost y URLs de retorno HTTP planos
Punto de conexión de servidor Siempre para verificar tokens. En Android también recibe la llamada de Apple

Si no tiene certificados de firma aún, el generador de certificado de iOS los crea sin bailar con la llave de Mac.

Sobre cómo funciona Sign in with Apple en cada plataforma

Apple solo envía un nativo SDK para sus propias plataformas, por lo que el flujo difiere:

Plataforma Mecanismo ID de cliente utilizado Requerimiento de backend para inicio de sesión
iOS Nativo ASAuthorizationAppleIDProvider sheet ventana emergente No
No OAuth de Apple en una pestaña personalizada de Chrome, Apple envía a tu servidor, tu servidor redirige de vuelta a la aplicación. ID de Servicios Yes
Sí Apple popup de JS Servicios ID No (pero verifica el token)

La audiencia (aud) del token de identidad sigue el ID de cliente. Los tokens de iOS llevan tu ID de paquete, los tokens de Android y web llevan el Servicios ID. Tu backend tiene que aceptar ambos.

Configura el paso 1: Cuenta del desarrollador de Apple

Configura el paso 1: Cuenta del desarrollador de Apple

  1. Open Certificados, Identificadores y Perfiles y seleccionar y selecciona.
  2. Abra el ID de la aplicación que coincide con su Capacitor. appId (por ejemplo) com.example.app). Create it if it does not exist.
  3. Bajo Capacidades, marca Iniciar sesión con Apple y guarda. Deja que sea un ID de App principal a menos que agrupes varias aplicaciones.

Si cambias las capacidades en un ID de App existente, regenera los perfiles de configuración que lo utilizan. Los perfiles antiguos no incluyen la nueva autorización.

Crea un ID de Servicios (solo para Android y web)

  1. En Identificadores, haz clic + y selecciona IDs de Servicios.
  2. Utilice una descripción como “Iniciar sesión en la web de ejemplo” y un identificador como com.example.app.signinEste valor se convierte en el clientId en Android y web.
  3. Abra el nuevo ID de Servicio, marque Iniciar sesión con Apple, y haga clic en Configurar.
  4. Seleccione su ID de App como ID de App principal.
  5. Agregue su dominio bajo Dominios y Subdominios (sin esquema, por ejemplo auth.example.com).
  6. Agregar las URLs de retorno debajo de URLs de retorno con la ruta completa, por ejemplo https:// Para inicio de sesión web, agregue la URL de la página que llama al inicio de sesión, con y sin una barra diagonal de cierre. https://auth.example.com/apple/callbackCrear una clave de inicio de sesión con Apple (token de Android y servidor de intercambio)

Crear una clave de inicio de sesión con Apple (intercambio de tokens Android y servidor)

  1. Claves , hacer clic, seleccionar +, tick , y hacer clicy haz clic Configura para elegir el ID de App principal.
  2. Registra la clave y descarga el .p8 archivo. Apple solo te permite descargarlo una vez.
  3. Toma nota del ID de clave y tu ID de equipo (mostrado en la parte superior derecha de la consola de desarrolladores y bajo Miembros).

Tu servidor utiliza la clave para firmar el client_secret JWT que envía a la API de tokens de Apple.

Register email sources if you send email

Los usuarios pueden elegir Ocultar mi correo electrónico, lo que te da una @privaterelay.appleid.com dirección. Solo Apple envía correo electrónico de dominios y direcciones que has registrado. Servicios > Iniciar sesión con Apple para la comunicación por correo electrónicoSilenciosamente desaparecen tus correos electrónicos de restablecimiento de contraseña y facturas.

Step 2: Instala el plugin

bun add @capgo/capacitor-social-login
bunx cap sync

El plugin agrupa varios proveedores. Si solo usas Apple (y tal vez Google), desactiva los demás en capacitor.config.ts para que sus SDKs no se envíen:

import type { CapacitorConfig } from '@capacitor/cli';

const config: CapacitorConfig = {
  appId: 'com.example.app',
  appName: 'Example',
  webDir: 'dist',
  plugins: {
    SocialLogin: {
      providers: {
        apple: true,
        google: false,
        facebook: false,
        twitter: false,
      },
    },
  },
};

export default config;

Corre bunx cap sync No vuelvas a cambiar esta bloque. Un proveedor deshabilitado no está disponible en tiempo de ejecución, así que no lo deshabiles. Eliminar Facebook también elimina el AD_ID advertencias de permiso que su SDK desencadena en el Console de Google Play.

Step 3: configuración de iOS

  1. Correr bunx cap open ios.
  2. Selecciona el App Aplicación Iniciar sesión & Capabilities, click haz clic en+ Capacidad Iniciar sesión con Apple.
  3. Asegúrese de que el identificador de la cesta coincida con el ID de la App que configuró.

Xcode escribe com.apple.developer.applesignin en App.entitlementsSi construyes en CI o con Capgo Construcción, comite el archivo de entitlements, de lo contrario, la compilación en la nube produce una aplicación que falla con ASAuthorizationError code 1000.

en Info.plist URL scheme o AppDelegate Se necesita cambio para Apple en iOS.

Paso 4: configuración de Android

Apple has no Android SDK. The plugin opens Apple’s authorize page in a Custom Tab with your Services ID. Apple then sends the result as a form POST to your HTTPS return URL. Your server exchanges the code, then redirects to a deep link that reopens the app with the tokens.

In android/app/src/main/AndroidManifest.xml, dentro de MainActivity <activity> elemento:

<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="apple-login" />
</intent-filter>

Reenvía la intención al plugin

Editar MainActivity.java para que el enlace profundo llegue al plugin:

package com.example.app;

import android.content.Intent;
import android.net.Uri;
import com.getcapacitor.BridgeActivity;
import com.getcapacitor.Plugin;
import com.getcapacitor.PluginHandle;
import ee.forgr.capacitor.social.login.SocialLoginPlugin;

public class MainActivity extends BridgeActivity {

    @Override
    protected void onNewIntent(Intent intent) {
        Uri data = intent.getData();
        if (Intent.ACTION_VIEW.equals(intent.getAction()) && data != null
                && "apple-login".equals(data.getHost())) {
            PluginHandle handle = getBridge().getPlugin("SocialLogin");
            if (handle != null) {
                Plugin plugin = handle.getInstance();
                if (plugin instanceof SocialLoginPlugin) {
                    ((SocialLoginPlugin) plugin).handleAppleLoginIntent(intent);
                    return;
                }
            }
        }
        super.onNewIntent(intent);
    }
}

El filtrado en el host mantiene funcionando tus otros enlaces profundos (restablecimiento de contraseña, enlaces de marketing).

El punto final de llamada

Apple publica code, id_token, state, y en el primer inicio un user cadena JSON a tu URL de retorno. El punto final tiene que intercambiar el code y redirigir al app con success=true más los tokens, que es el contrato que el plugin parsea. Un ejemplo mínimo de Hono utilizando jose:

import { Hono } from 'hono';
import { SignJWT, importPKCS8, createRemoteJWKSet, jwtVerify } from 'jose';
import { saveAppleRefreshToken } from './db'; // your own persistence layer

const app = new Hono();

const TEAM_ID = process.env.APPLE_TEAM_ID!;
const KEY_ID = process.env.APPLE_KEY_ID!;
const SERVICE_ID = process.env.APPLE_SERVICE_ID!; // com.example.app.signin
const PRIVATE_KEY = process.env.APPLE_PRIVATE_KEY!; // contents of the .p8 file
const APP_REDIRECT = 'com.example.app://apple-login';
const APPLE_JWKS = createRemoteJWKSet(new URL('https://appleid.apple.com/auth/keys'));

async function appleClientSecret() {
  const key = await importPKCS8(PRIVATE_KEY, 'ES256');
  return new SignJWT({})
    .setProtectedHeader({ alg: 'ES256', kid: KEY_ID })
    .setIssuer(TEAM_ID)
    .setSubject(SERVICE_ID)
    .setAudience('https://appleid.apple.com')
    .setIssuedAt()
    .setExpirationTime('5m')
    .sign(key);
}

app.post('/apple/callback', async (c) => {
  const form = await c.req.parseBody();
  const code = String(form.code ?? '');
  if (!code) {
    return c.redirect(`${APP_REDIRECT}?success=false`);
  }

  // `form.user` is only present on the first authorization. Store it now.

  const res = await fetch('https://appleid.apple.com/auth/token', {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: new URLSearchParams({
      client_id: SERVICE_ID,
      client_secret: await appleClientSecret(),
      code,
      grant_type: 'authorization_code',
      redirect_uri: 'https://auth.example.com/apple/callback',
    }),
  });
  if (!res.ok) {
    return c.redirect(`${APP_REDIRECT}?success=false`);
  }
  const tokens = await res.json();

  // Verify the identity token, then keep the long-lived refresh token on the server.
  // Only the short-lived tokens go back to the app in the deep link.
  const { payload } = await jwtVerify(tokens.id_token, APPLE_JWKS, {
    issuer: 'https://appleid.apple.com',
    audience: SERVICE_ID,
  });
  if (tokens.refresh_token) {
    await saveAppleRefreshToken(String(payload.sub), tokens.refresh_token);
  }
  const params = new URLSearchParams({
    success: 'true',
    access_token: tokens.access_token,
    id_token: tokens.id_token,
  });
  return c.redirect(`${APP_REDIRECT}?${params}`);
});

export default app;

El plugin lee los tokens del enlace profundo de la cadena de consulta, por lo que cualquier cosa que pongas allí puede terminar en registros o ser leída por otra app que registre el mismo esquema personalizado. Envía solo los tokens de vida corta access_token y id_tokennunca el token de refresco, verifica la id_token signature and nonce en tu servidor antes de confiar en él, y utiliza un esquema único para tu aplicación (tu ID de paquete inverso).

La client_secret JWT puede vivir hasta seis meses, pero generar una breve por solicitud evita una interrupción de caducidad. Mantén el .p8 archivo en tu administrador de secretos, nunca en el paquete de la aplicación.

Paso 5: Configuración web

La construcción web utiliza Apple JS con el ID de Servicios. Agrega la URL de página exacta que desencadena la autenticación a las URL de retorno del ID de Servicios. Apple no acepta localhost, así que prueba el inicio de sesión web en un dominio de previsualización desplegado o a través de un túnel con un hostname real.

Paso 6: Inicializar y autenticar

Inicializa una vez al inicio de la aplicación. Pasa el redirectUrl solamente en Android y web. En iOS, un no vacío redirectUrl hace que el plugin envíe el resultado a esa URL, lo que no deseas con el flujo nativo.

import { Capacitor } from '@capacitor/core';
import { SocialLogin } from '@capgo/capacitor-social-login';

const platform = Capacitor.getPlatform();

export async function initAuth() {
  await SocialLogin.initialize({
    apple: {
      clientId: platform === 'ios' ? 'com.example.app' : 'com.example.app.signin',
      redirectUrl:
        platform === 'android'
          ? 'https://auth.example.com/apple/callback'
          : platform === 'web'
            ? 'https://app.example.com/login'
            : '',
    },
  });
}

Después, activa el inicio de sesión desde un botón. Utiliza un nonce aleatorio para que tu servidor pueda rechazar tokens repetidos:

import { SocialLogin } from '@capgo/capacitor-social-login';

export async function signInWithApple() {
  const nonce = crypto.randomUUID();

  try {
    const { result } = await SocialLogin.login({
      provider: 'apple',
      options: {
        scopes: ['email', 'name'],
        nonce,
      },
    });

    if (!result.idToken) {
      throw new Error('Apple did not return an identity token');
    }

    // Send to your backend. Name and email are only present on the first login.
    const response = await fetch('https://api.example.com/auth/apple', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        idToken: result.idToken,
        nonce,
        givenName: result.profile.givenName,
        familyName: result.profile.familyName,
      }),
    });
    return response.json();
  } catch (error: any) {
    if (error?.code === 'USER_CANCELLED') {
      return null; // user closed the sheet, not a failure
    }
    throw error;
  }
}

La respuesta te da:

Campo Contenido
result.idToken JWT firmado de Apple, la cosa que tu servidor verifica
result.profile.user ID de usuario estable de Apple (igual que el sub reclamo)
result.profile.email Correo electrónico o dirección de relay privada, cuando se concede
result.profile.givenName / familyName Sólo en la primera autorización
result.accessToken Con los ajustes por defecto, esto contiene la autorización code, no un token de acceso real
result.authorizationCode Presente cuando inicies con useProperTokenExchange: true

Si su servidor necesita tokens de actualización de Apple (por ejemplo, para llamar al endpoint de revocación cuando un usuario elimina su cuenta), establezca useProperTokenExchange: true en la apple configuración y cancele authorizationCode en el servidor.

Step 7: Verifique el token de identidad en su backend

Nunca confíes en los campos de perfil que el cliente envía. Verifica la firma del JWT contra las llaves públicas de Apple y revisa las declaraciones.

import { createRemoteJWKSet, jwtVerify } from 'jose';

const appleKeys = createRemoteJWKSet(new URL('https://appleid.apple.com/auth/keys'));

export async function verifyAppleToken(idToken: string, expectedNonce: string) {
  const { payload } = await jwtVerify(idToken, appleKeys, {
    issuer: 'https://appleid.apple.com',
    audience: ['com.example.app', 'com.example.app.signin'], // bundle ID + Services ID
  });

  if (payload.nonce !== expectedNonce) {
    throw new Error('Nonce mismatch');
  }

  return {
    appleUserId: payload.sub as string,
    email: payload.email as string | undefined,
    emailVerified: payload.email_verified === true || payload.email_verified === 'true',
  };
}

Registre el registro del usuario sub, not on email. The email can be a relay address, and users can stop sharing it. After verification, issue your own session token. If you use Supabase or Firebase, they verify the token for you, see Supabase con Capacitor Inicio de sesión social.

Supabase con __CAPGO_KEEP_0__ Inicio de sesión social

  • Directiva 4.8: si ofrece Google, Facebook o otra opción de inicio de sesión de terceros como el sistema de cuenta principal, debe ofrecer también una opción equivalente enfocada en la privacidad. Iniciar sesión con Apple cumple con esto.
  • Directiva 5.1.1(v)Las aplicaciones que permiten a los usuarios crear cuentas deben permitirles eliminar la cuenta en la aplicación. Para los usuarios de Sign in with Apple, llame a Apple's https://appleid.apple.com/auth/revoke Guía de eliminación de cuentas Guía de eliminación de cuenta.
  • Diseño de botónUtilice los estilos de botón aprobados por Apple y haga que el botón sea tan destacado como otros botones de inicio de sesión.

Troubleshooting

Síntoma Causa probable Solución
ASAuthorizationError code 1000 en iOS Falta de capacidad o permiso en el objetivo compilado Agregar Sign in with Apple en Xcode, hacer commit App.entitlementsregenerar perfiles
Code 1001 en iOS El usuario canceló El plugin rechaza con USER_CANCELLED, tratar como un error no
invalid_client en Android o web Incorrecto clientId (ID de paquete utilizado en lugar de ID de Servicios) o mal client_secret Usar el ID de Servicios, verificar ID de equipo, ID de clave y contenido de la clave
invalid_request: Invalid redirect_uri La URL de retorno no está registrada exactamente Agregar el identificador de Apple https:// La aplicación no regresa después de iniciar sesión en Android
La aplicación no regresa después de iniciar sesión con Android Intent filter scheme or host differs from the server redirect Compare com.example.app://apple-login carácter por carácter
apple.android.redirectUrl is null or empty en Android redirectUrl en Android Pase su URL de llamada HTTPS cuando inicie en Android
No es la primera autorización null No es la primera autorización Lee las desde tu base de datos. Revoca la aplicación en ajustes de dispositivo para probar de nuevo.
Token rechazado por el servidor: aud incompatibilidad El servidor solo acepta un audiencia Aceptar ambos ID de paquete y ID de Servicios

Arreglos de envío después de la liberación

La mayoría de los problemas de autenticación viven en TypeScript: una ID de cliente equivocada por plataforma, un nonce faltante, una mala gestión de errores. Pueden ser enviados a los usuarios el mismo día con actualizaciones en vivo de Capgo sin esperar la revisión de la App. Los cambios en las entidades, el manifiesto de Android o MainActivity son nativos y requieren una nueva compilación de tienda.

¿Dónde ir a continuación?

Actualizaciones en vivo para aplicaciones Capacitor

¿Cuándo un bug en la capa web está vivo, envíe la solució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 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.