Saltare al contenuto principale

Come accedere con Apple utilizzando Capacitor (2026)

Aggiungi l'accesso con Apple a un Capacitor 8 app su iOS, Android e web: configurazione sviluppatore Apple, plugin code, controlli token backend e errori comuni.

Come utilizzare l'accesso con Apple con Capacitor (2026)

Per aggiungere l'accesso con Apple a un Capacitor app, installa @capgo/capacitor-social-loginAbilita la capacità di accesso con Apple sul tuo ID App e in Xcode, chiama SocialLogin.initialize({ apple: {} })Chiamare SocialLogin.login({ provider: 'apple' })On iOS questo apre la schermata nativa di Apple e restituisce un token di identità. Android e web utilizzano il flusso OAuth web di Apple, che richiede un ID Servizi e un URL di reindirizzamento HTTPS.

Questa guida copre la configurazione dello sviluppatore Apple, il client code per iOS, Android e web, la verifica del token sul tuo backend e gli errori che sei più probabile di incontrare. Si concentra su Capacitor 8 e Xcode 26, che App Store Connect richiede per le pubblicazioni dal 2026.

Cosa serve prima di iniziare

Requisito Perché
Iscrizione al programma di sviluppo Apple a pagamento App IDs, Services IDs e chiavi Sign in with Apple sono disponibili solo per i membri
Un Mac con Xcode 26 È necessario per creare e caricare app iOS nel 2026
Capacitor 8 progetto Plugin v8 che mira a Capacitor 8
Un dominio HTTPS che controlli Solo per Android e web. Apple rifiuta localhost e ritorna URL HTTP semplice
Punto finale backend Sempre per verificare i token. Su Android riceve anche il callback di Apple

Se non hai ancora i certificati di firma, il generatore di certificato iOS crea senza una danza di keychain Mac.

Come funziona l'accesso con Apple su ogni piattaforma

Apple invia solo un nativo SDK per le proprie piattaforme, quindi il flusso differisce:

Piattaforma Meccanismo ID client utilizzato Richiesta di backend per l'accesso
IOS Nativo ASAuthorizationAppleIDProvider sheet Finestra di dialogo No
No Apple OAuth web in una finestra personalizzata di Chrome, Apple invia i dati al tuo server, il server reindirizza nuovamente all'app. ID dei Servizi Yes
Sì Apple popup JS Servizi ID No (ma verificare il token)

Il pubblico ('aud) del token di identità segue l'ID del client. I token da iOS portano il tuo ID bundle, i token da Android e web portano il Servizi ID. Il tuo backend deve accettare entrambi.

Passo 1: Configura l'account sviluppatore Apple

Abilita la capacità sul tuo ID App

  1. Apre Certificati, identificatori e profili e seleziona Identificatori.
  2. Apre l'ID App che corrisponde al tuo Capacitor appId (ad esempio com.example.app). Create it if it does not exist.
  3. Sotto Funzionalità, segna Accedi con Apple e salvalo. Lascialo come ID App principale a meno che non si tratta di un gruppo di app.

Se modifichi le funzionalità di un ID App esistente, rigenera i profili di provisioning che lo utilizzano. I vecchi profili non includono l'entità nuova.

Crea un ID Servizi (solo per Android e web)

  1. In Identificatori, clicca + e scegli ID dei Servizi.
  2. Usa una descrizione come “Esempio di accesso web” e un identificatore come com.example.app.signin. Questo valore diventa il clientId su Android e web.
  3. Apre il nuovo ID dei Servizi, seleziona Accedi con Apple, e clicca su Configura.
  4. Scegli il tuo ID App come ID App principale.
  5. Aggiungi il tuo dominio sotto Domini e sottodomini (senza schema, ad esempio auth.example.com).
  6. Aggiungi gli URL di ritorno sotto URL di ritorno con la strada completa, ad esempio https:// percorso, ad esempio https://auth.example.com/apple/callbackCreare una chiave di accesso Sign in with Apple (token di scambio Android e server)

Crea una chiave Sign in with Apple (scambio token Android e server)

  1. Chiavi , clicca, seleziona +, tick Accedi con Apple__CAPGO_KEEP_0__ Configura per scegliere l'ID App principale.
  2. Registra la chiave e scarica il .p8 file. Apple ti consente di scaricarlo solo una volta.
  3. Nota il ID Chiave e il tuo ID Team (mostrato nella parte superiore destra della porta di accesso per sviluppatori e sotto Abbonamenti).

Il tuo server utilizza la chiave per firmare il client_secret JWT che invia a Apple endpoint del token.

Registrare fonti email se invii email

Gli utenti possono scegliere Nascondi il mio indirizzo email, che ti fornisce un @privaterelay.appleid.com indirizzo. Apple invia solo posta da domini e indirizzi che hai registrato sotto Servizi > Accedi con Apple per la comunicazione via email. Saltare questo e i tuoi email di reimpostazione della password e ricevuta scompaiono silenziosamente.

Passo 2: Installa il plugin

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

Il plugin raggruppa diversi provider. Se utilizzi solo Apple (e forse Google), disabilita gli altri in capacitor.config.ts così che i loro SDK non vengano spediti:

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;

Esegui bunx cap sync ancora dopo aver modificato questo blocco. Un provider disabilitato non è disponibile in esecuzione, quindi non disabilitare uno che utilizzi. Eliminare Facebook elimina anche le AD_ID avvertimenti di autorizzazione che il suo SDK attiva nel Google Play Console.

Passo 3: configurazione iOS

  1. Avvia bunx cap open ios.
  2. Scegli il App Xcode scrive Autenticazione & FunzionalitàXcode scrive Xcode scriveXcode scrive Accedi con Apple.
  3. Assicurati che l'identificatore del bundle corrisponda all'ID App che hai configurato.

Xcode scrive com.apple.developer.applesignin nel App.entitlements. Se costruisci in CI o con Capgo Costruisci, commetta il file di entitlements, altrimenti la costruzione cloud produce un'app che fallisce con ASAuthorizationError code 1000.

No Info.plist URL scheme o AppDelegate change is needed for Apple on iOS.

Passo 4: configurazione 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, all'interno dell' 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>

Invia l'intento al plugin

Modifica MainActivity.java così che il collegamento profondo raggiunge il 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);
    }
}

Filtra sul host per mantenere funzionanti i tuoi altri collegamenti profondi (reset della password, link di marketing).

Il punto di fine callback

Apple invia code, id_token, state, e al primo accesso un user stringa JSON al tuo URL di ritorno. Il punto di fine ha scambiare il code e reindirizzare all'app con success=true insieme ai token, che è il contratto che il plugin elabora. Un esempio minimale di Hono utilizzando 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;

Il plugin legge i token dal parametro di query string del collegamento profondo, quindi qualsiasi cosa metti lì può finire nei log o essere letta da un altro app che registra lo stesso schema personalizzato. Invia solo i token di breve durata access_token Ecco id_tokenmai il token di refresh, verificare il id_token signature and nonce prima di fidarti di esso, utilizza uno schema univoco per la tua app (il tuo ID inverso del bundle).

Il client_secret JWT può vivere fino a sei mesi, ma generare uno breve per ogni richiesta evita un'interruzione di scadenza. Conserva il .p8 file nel tuo manager segreto, mai nel bundle dell'app.

Passo 5: Configurazione web

The web build uses Apple JS with the Services ID. Add the exact page URL that triggers the login to the Services ID return URLs. Apple does not accept localhost, quindi testa l'accesso web su un dominio di anteprima distribuito o attraverso un tunnel con un vero hostname.

Passo 6: Inizializza e accedi

Passo 6: Inizializzazione e login redirectUrl esclusivamente su Android e web. Su iOS, un non-empty redirectUrl rende il plugin POST il risultato a quella URL, che non desiderate con il flusso 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'
            : '',
    },
  });
}

Poi attiva il login da un pulsante. Utilizza un nonce casuale affinché il tuo server possa rifiutare i token riprodotti:

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 risposta ti dà:

Campo Contenuto
result.idToken JWT firmato da Apple, la cosa che il tuo server verifica
result.profile.user ID utente stabile di Apple (lo stesso del sub claim)
result.profile.email Indirizzo e-mail o indirizzo di relay privato, quando concesso
result.profile.givenName / familyName Solo alla prima autorizzazione
result.accessToken Con le impostazioni predefinite, questo contiene l'autorizzazione code, non un token di accesso reale
result.authorizationCode Presente quando inizializzi con useProperTokenExchange: true

Se il tuo server richiede token di rinnovo Apple (ad esempio per chiamare l'endpoint di revoca quando un utente cancella il proprio account), impostalo useProperTokenExchange: true in la apple config e scambiare authorizationCode su il server.

Passo 7: Verifica il token di identità sul tuo backend

Non fidarsi mai dei campi del profilo che il client invia. Verificare la firma JWT contro le chiavi pubbliche di Apple e controllare le dichiarazioni:

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',
  };
}

Chiave il record utente su sube non su email. L'email può essere un indirizzo di relay e gli utenti possono fermare di condividerla. Dopo la verifica, emetti il tuo proprio token di sessione. Se utilizzi Supabase o Firebase, essi verificano il token per te, vedi Supabase con Capacitor Social Login.

Regole dell'App Store da tenere a mente

  • Linea guida 4.8: se offri Google, Facebook o un altro sistema di login terzo, dovete offrire anche un'opzione focalizzata sulla privacy equivalente. Sign in with Apple qualifica.
  • Linea guida 5.1.1(v)Le app che consentono agli utenti di creare un account devono consentire loro di cancellare l'account all'interno dell'app. Per gli utenti Sign in with Apple, chiamare Apple. https://appleid.apple.com/auth/revoke Guida alla cancellazione dell'account Progettazione del pulsante.
  • Disegno del pulsanteUsa gli stili di bottone approvati da Apple e assicurati che il bottone sia almeno altrettanto prominente quanto gli altri pulsanti di accesso.

Troubleshooting

Simulazione Probabile causa Soluzione
ASAuthorizationError code 1000 su iOS Manca la capacità o l'entità nel target costruito Aggiungi Sign in with Apple in Xcode, commit App.entitlements, regenera i profili
Code 1001 su iOS L'utente ha annullato Il plugin rifiuta con USER_CANCELLED, trattalo come un errore non rilevante
invalid_client su Android o web Errato clientId (ID bundle utilizzato al posto di ID Servizi) o cattivo client_secret Utilizza l'ID Servizi, controlla l'ID Team, l'ID chiave e i contenuti della chiave
invalid_request: Invalid redirect_uri L'URL di ritorno non è registrato esattamente Aggiungi l'intero https:// URL dei Servizi ID, corrispondenza di virgole finali
L'app non torna dopo il login Android L'intent filter schema o host differisce dal redirect del server Confronta com.example.app://apple-login carattere per carattere
apple.android.redirectUrl is null or empty No redirectUrl su Android Passa l'URL di callback HTTPS quando si inizializza su Android
Nome e indirizzo e-mail sono null Non è la prima autorizzazione Leggile dal tuo database. Revoca l'app nei impostazioni del dispositivo per testare nuovamente
Token rifiutato dal backend: aud mancanza Server accetta solo un pubblico: Accetta sia il bundle ID che l'ID Services:

Correzioni di spedizione dopo la rilascio:

La maggior parte dei bug di autenticazione vive in TypeScript: un ID cliente sbagliato per piattaforma, un nonce mancante, una gestione degli errori cattiva. Quelle possono essere spedite agli utenti lo stesso giorno con aggiornamenti in tempo reale di Capgo senza attendere la revisione dell'app. Modifiche agli entità, la manifestazione Android o MainActivity senza dover attendere la revisione dell'app.

Cosa fare successivamente

Aggiornamenti in tempo reale per le app Capacitor

Dove un bug nel layer web è attivo, invia la correzione attraverso Capgo anziché aspettare giorni per l'approvazione della store. Gli utenti ricevono l'aggiornamento in background mentre le modifiche native rimangono nel normale percorso di revisione.

Ultimi articoli dal nostro Blog

Capgo ti offre le migliori informazioni che ti servono per creare un'app mobile veramente professionale.