Um eine Capacitor-App mit Sign in with Apple zu versehen, installieren Sie @capgo/capacitor-social-loginEnable the Sign in with Apple capability on your App ID und in Xcode aufrufen SocialLogin.initialize({ apple: {} })Dann aufrufen SocialLogin.login({ provider: 'apple' }). Bei iOS öffnet dies das native Apple Blatt und gibt einen Identitäts-Token zurück. Android und Web verwenden Apple’s Web OAuth-Flow, der eine Services-ID und einen HTTPS-Redirect-URL benötigt.
Diese Anleitung behandelt die Apple-Entwickler-Einrichtung, den Client code für iOS, Android und Web, die Verifizierung des Tokens auf Ihrem Backend und die Fehler, die Sie am häufigsten treffen werden. Sie richtet sich an Capacitor 8 und Xcode 26, die App Store Connect seit April 2026 für Uploads vorausgesetzt hat.
Was Sie vorher benötigen
| Anforderung | Warum |
|---|---|
| Bezahlte Mitgliedschaft im Apple-Entwickler-Programm | App-IDs, Services-IDs und Sign in with Apple-Schlüssel sind nur für Mitglieder verfügbar |
| Ein Mac mit Xcode 26 | Notwendig, um iOS-Apps in 2026 zu erstellen und hochzuladen |
| Capacitor 8-Projekt | Plugin v8 zielt auf Capacitor 8 ab |
| Ein HTTPS-Domain, die Sie kontrollieren | Nur für Android und Web. Apple lehnt ab localhost und einfache HTTP-Rückruf-URLs |
| Ein Backend-Endpunkt | Immer zum Verifizieren von Token. Auf Android erhält es auch Apples Callback |
Wenn Sie noch keine Signierzertifikate besitzen, erstellt der iOS-Zertifikatsgenerator erstellt sie ohne eine Mac-Schlüsselkette.
Wie Sign in with Apple auf jeder Plattform funktioniert
Apple liefert nur eine native SDK für seine eigenen Plattformen, daher unterscheidet sich der Ablauf:
| Plattform | Methode | Kunden-ID, die verwendet wird | Backend erforderlich für die Anmeldung |
|---|---|---|---|
| iOS | Native ASAuthorizationAppleIDProvider sheet |
Ihr App-Bundle-Id | No |
| Nein | Apple OAuth in einem Chrome Custom Tab, Apple sendet Daten an Ihren Server, der leitet die Benutzer dann zurück in die App. | Dienst-ID | Services-ID |
| Ja | Apple JS Popup | Dienst-ID | Nein (aber überprüfen Sie den Token) |
Die Zielgruppe (aud) des Identitäts-Tokens folgt dem Client-ID. Token von iOS tragen Ihren Bundle-ID, Token von Android und Web tragen die Services ID. Ihr Backend muss beide akzeptieren.
Schritt 1: Konfigurieren Sie das Apple-Entwicklerkonto
Konfigurieren Sie die Fähigkeit auf Ihrem App-ID
- Öffnen Zertifikate, Identifikatoren und Profile und wählen Sie Identifikatoren.
- Öffnen Sie das App-ID, das Ihrem Capacitor entspricht
appId(zum Beispiel)com.example.app. Erstelle es, wenn es nicht existiert. - Unter Fähigkeiten, bestätige Anmeldung mit Apple und speichere. Lasse es als Haupt-App-ID, es sei denn, du gruppiert mehrere Apps.
Wenn Sie die Fähigkeiten eines bestehenden App-IDs ändern, müssen Sie die entsprechenden Provisionierungsprofile neu generieren. Die alten Profile enthalten die neuen Berechtigungen nicht.
Erstelle eine Services-ID (nur für Android und Web)
- In Identifikatoren, klicke + und wähle Dienstleistungs-IDs.
- Verwenden Sie eine Beschreibung wie „Beispiel Web-Login“ und einen Identifier wie
com.example.app.signinDieser Wert wird zumclientIdauf Android und Web. - Öffnen Sie das neue Services-ID, setzen Sie das Häkchen Anmeldung mit Apple, und klicken Sie Konfigurieren.
- Wählen Sie Ihre App-ID als Haupt-App-ID.
- Hinzufügen Sie Ihren Domain unter Domains und Subdomains (kein Scheme, zum Beispiel
auth.example.com). - Fügen Sie die Rückruf-URLs unter Rückruf-URLs mit dem vollständigen
https://Weg, zum Beispielhttps://auth.example.com/apple/callback. Für Web-Anmeldungen fügen Sie die Seite-URL hinzu, die die Anmeldung aufruft, mit und ohne Schlusspunkt.
Erstellen Sie einen Sign in with Apple-Schlüssel (Android und Server-Token-Übertragung)
- Gehe zu Schlüssel, klicke +, markiere Anmelden mit Apple, und klicke Einstellen um die primäre App-ID auszuwählen.
- Registrieren Sie die Schlüssel und laden Sie das
.p8Datei herunter. Apple lässt Sie es nur einmal herunterladen. - Hinweis auf die Schlüssel-ID und Ihre Team-ID (gezeigt im oberen rechten Bereich des Entwicklerportals und unter Mitgliedschaft).
Ihr Server verwendet die Schlüssel, um das client_secret JWT zu signieren, das er an Apples Token-Endpunkt sendet.
Registrieren Sie E-Mail-Quellen, wenn Sie E-Mail senden
Benutzer können wählen Meine E-Mail verbergen, die Ihnen ermöglicht, sich anzumelden. @privaterelay.appleid.com . Wenn Sie dies überspringen, verschwinden Ihre Passwort-Restitutions- und Rechnungs-E-Mails stillschweigend. Dienste > Apple-Anmeldung für E-Mail-KommunikationDeaktivieren Sie diese Option und Ihre Passwörterrset- und Rechnungsemails verschwinden still und leise.
so ab, dass ihre SDKs nicht geliefert werden:
bun add @capgo/capacitor-social-login
bunx cap sync
Die Plugin verpackt mehrere Anbieter. Wenn Sie nur Apple (und vielleicht Google) verwenden, schalten Sie die anderen aus in capacitor.config.ts So ihre SDKs werden nicht geliefert:
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;
Run bunx cap sync Ändern Sie diesen Block nicht erneut. Ein deaktivierter Anbieter ist bei der Ausführung nicht verfügbar, daher sollten Sie einen nicht deaktivieren, den Sie verwenden. AD_ID Warnungen über die Berechtigungen, die SDK in Google Play Console auslöst.
Schritt 3: iOS-Einrichtung
- Lauf
bunx cap open ios. - Wählen Sie App Schritt 3: iOS-Einrichtung Anmeldung & Funktionen, click Xcode schreibt, und fügen Sie Anmelden mit Apple.
- Stellen Sie sicher, dass der Bundle-Identifier dem von Ihnen konfigurierten App-ID entspricht.
Xcode schreibt com.apple.developer.applesignin auf App.entitlements. Wenn Sie in CI oder mit Capgo Buildcommitte das Entitlements-Datei, andernfalls produziert die Cloud-Build eine App, die mit einem Fehler fehlschlägt ASAuthorizationError code 1000.
No Info.plist URL-Schema oder AppDelegate Eine Änderung ist erforderlich für Apple auf iOS.
Schritt 4: Android-Einrichtung
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.
Fügen Sie die tiefe Verknüpfung hinzu
In android/app/src/main/AndroidManifest.xml, innerhalb des MainActivity <activity> element:
<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>
Richten Sie die Absicht an das Plugin weiter
Bearbeiten MainActivity.java so dass der tiefere Link das Plugin erreicht:
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);
}
}
Filtern Sie auf dem Host, um sicherzustellen, dass Ihre anderen tiefen Links (Passwort-Restart, Marketing-Links) weiterhin funktionieren.
Der Callback-Endpunkt
Apple sendet code, id_token, state, und bei der ersten Anmeldung einen user eine JSON-Zeichenfolge an Ihre Rückruf-URL. Der Endpunkt muss die code austauschen und zu der App umleiten, die success=true zusammen mit den Token, die der Plugin-Vertrag parsen muss. Ein minimaler Hono-Beispiel, der 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;
Das Plugin liest die Token aus der tiefen-Link-Abfragezeichenfolge, sodass alles, was Sie dort einfügen, in Protokollen landen kann oder von einem anderen App, das den gleichen benutzerdefinierten Scheme registriert, gelesen werden kann. Senden Sie nur die kurzlebigen access_token und id_token, nie das Refresh-Token, überprüfen Sie die id_token Signatur und nonce auf Ihrem Server, bevor Sie sie vertrauen, und verwenden Sie einen einzigartigen Scheme für Ihre App (Ihre umgekehrte Bundle-ID).
Der client_secret JWT kann bis zu sechs Monate lang gültig sein, aber die Erzeugung eines kurzen pro Anfrage vermeidet einen Ablaufdatum-Ausfall. Halten Sie die .p8 Datei in Ihrem geheimen Manager, nie in der App-Bundle.
Schritt 5: Web-Einrichtung
Die Web-Build verwendet Apple JS mit der Services-ID. Fügen Sie die genaue Seite-URL hinzu, die den Login auslöst, zu den Rückgabewerten der Services-ID. Apple akzeptiert localhost, also testen Sie den Web-Login auf einem bereitgestellten Vorabdomänen- oder durch einen Tunnel mit einem realen Hostnamen.
Schritt 6: Initialisierung und Anmeldung
Initialisiere einmalig bei App-Start. Übergebe die redirectUrl nur auf Android und Web. Auf iOS wird ein nicht leerer redirectUrl macht das Plugin den Ergebnis an die URL POSTen, was du bei der nativen Fluss nicht haben möchtest.
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'
: '',
},
});
}
Dann löst die Anmeldung von einem Button aus. Verwende einen zufälligen Nonce, damit dein Server wiederholte Tokens ablehnen kann:
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;
}
}
Die Antwort gibt dir:
| Feld | Inhalt |
|---|---|
result.idToken |
Signierter JWT von Apple, das Ding, das dein Server überprüft |
result.profile.user |
Stabiles Benutzerkonto von Apple (gleich dem sub Behauptung) |
result.profile.email |
E-Mail-Adresse oder privater Relay-Adresse, wenn gewährt |
result.profile.givenName / familyName |
Nur bei der ersten Autorisierung |
result.accessToken |
Mit den Standardeinstellungen hält diese die Autorisierung code, kein echter Zugriffstoken |
result.authorizationCode |
Präsentiert, wenn Sie mit} useProperTokenExchange: true |
Wenn Ihr Server Apple-Refresh-Tokens benötigt (z.B. um den Revoke-Endpunkt aufzurufen, wenn ein Benutzer sein Konto löscht), setzen Sie useProperTokenExchange: true im apple Konfiguration und austauschen Sie authorizationCode auf dem Server.
Schritt 7: Überprüfen Sie die Identitäts-Token auf Ihrem Backend
Kontext: Über Capgo-Seite. Rolle: Benutzerschnittstelle-Label. Gesehen in: Seite über about.astro. Nachrichtenschlüssel `about_how_step_label` (Über How Schritt Label).
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',
};
}
Key the user record on 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 mit Capacitor Social Login.
Supabase mit __CAPGO_KEEP_0__ Social Login
- Richtlinie 4.8: Wenn Sie Google, Facebook oder einen anderen Drittanbieter-Login als Hauptsystem für Benutzerkonten anbieten, müssen Sie auch eine gleichwertige, datenschutzorientierte Option anbieten. Sign in with Apple qualifiziert sich dafür.
- Richtlinie 5.1.1(v): Apps, die Benutzern ermöglichen, Konten zu erstellen, müssen ihnen ermöglichen, das Konto im App zu löschen. Für Benutzer von Sign in with Apple rufen Sie Apple’s
https://appleid.apple.com/auth/revokeEndpunkt bei der Löschung auf. Siehe unsere Anleitung zur Konto-Löschung. - Knopfdesign: Verwenden Sie die von Apple genehmigten Knopfstile und machen Sie den Knopf mindestens so prominent wie andere Login-Knöpfe.
Problembehandlung
| Symptom | Wahrscheinliche Ursache | Lösung |
|---|---|---|
ASAuthorizationError code 1000 auf iOS |
Fähigkeit oder Berechtigung fehlt im Zielobjekt | Fügen Sie Sign in with Apple in Xcode hinzu, committen Sie App.entitlements, regenerieren Sie Profile |
| Code 1001 auf iOS | Benutzer stornierte | Der Plugin lehnt mit USER_CANCELLED, behandeln Sie es als Nichtfehler |
invalid_client auf Android oder Web |
Falsch clientId (Bundle-ID wird anstelle von Services-ID verwendet oder schlecht) client_secret |
Verwenden Sie die Services-ID, überprüfen Sie Team-ID, Key-ID und Schlüsselinhalt |
invalid_request: Invalid redirect_uri |
Rückruf-URL ist nicht genau registriert | Fügen Sie die vollständige https:// URL zur Services-ID hinzu, passen Sie die Schlusspunkte an |
| Die App kehrt nicht zurück nach Android-Anmeldung | Die Intent-Filter-Scheme oder -Host unterscheidet sich von der Server-Redirect-URL | Vergleichen com.example.app://apple-login Zeichen für Zeichen |
apple.android.redirectUrl is null or empty |
Nein redirectUrl auf Android |
Geben Sie Ihre HTTPS-Rückruf-URL mit der Initialisierung auf Android an |
Name und E-Mail sind null |
Nicht die erste Autorisierung | Lese sie aus Ihrer Datenbank. Entziehe der App die Berechtigung in den Geräte-Einstellungen, um erneut zu testen. |
Token wurde vom Backend abgelehnt: aud mismatch |
Der Server akzeptiert nur eine Zielgruppe | Accept both bundle ID and Services ID |
Lieferung von Korrekturen nach der Veröffentlichung
Die meisten Auth-Bugs leben in TypeScript: eine falsche Client-ID pro Plattform, ein fehlender Nonce, schlechte Fehlerbehandlung. Diese können den Benutzern am gleichen Tag mit Capgo Live-Updates ohne Warten auf die App-Überprüfung. Änderungen an den Berechtigungen, dem Android-Manifest oder MainActivity sind native und erfordern eine neue App-Veröffentlichung.
Wo geht es als Nächstes?
- Fügen Sie Google neben Apple hinzu: How to sign in with Google using Capacitor.
- Wählen Sie die richtige Mischung von Methoden für Ihre App: Authentifizierung hinzufügen zu einer Capacitor App.
- Plugin-Referenz und Plattformseiten: Social Login-Dokumentation und die Apple iOS-Einrichtung.