Untuk menambahkan Sign in dengan Apple ke sebuah Capacitor aplikasi, instal @capgo/capacitor-social-loginAktifkan kemampuan Sign in dengan Apple pada ID Aplikasi Anda dan di Xcode, panggil SocialLogin.initialize({ apple: {} }), kemudian panggil SocialLogin.login({ provider: 'apple' }). Pada iOS, ini membuka lembaran Apple native dan mengembalikan token identitas. Android dan web menggunakan aliran OAuth web Apple, yang memerlukan ID Layanan dan URL Redirect HTTPS.
Panduan ini mencakup pengaturan Pengembang Apple, klien code untuk iOS, Android, dan web, memverifikasi token pada backend, dan kesalahan yang paling mungkin Anda temui. Ini berfokus pada Capacitor 8 dan Xcode 26, yang App Store Connect telah memerlukan untuk unggahan sejak April 2026.
Apa yang Anda butuhkan sebelum Anda mulai
| Persyaratan | Mengapa |
|---|---|
| Anggota Program Pengembang Apple Berbayar | ID App, ID Layanan, dan Kunci Masuk dengan Apple hanya tersedia bagi anggota |
| Mac dengan Xcode 26 | Needed to build and upload iOS apps in 2026 |
| Capacitor 8 proyek | Plugin v8 bertarget Capacitor 8 |
| Domain HTTPS yang Anda kendalikan | Hanya untuk Android dan web. Apple menolak localhost dan URL HTTP sederhana |
| Endpoint backend | Selalu untuk memverifikasi token. Pada Android juga menerima panggilan balik Apple |
Jika Anda belum memiliki sertifikat tanda tangan, maka penghasil sertifikat iOS membuat mereka tanpa menari kunci Mac.
Bagaimana Sign in dengan Apple bekerja pada setiap platform
Apple hanya mengirimkan SDK asli untuk platformnya sendiri, sehingga aliran berbeda:
| Platform | Mechanisme | ID Klien yang digunakan | Backend yang diperlukan untuk login |
|---|---|---|---|
| iOS | Asli ASAuthorizationAppleIDProvider jendela |
ID Paket Aplikasi Anda | Tidak |
| Android | OAuth web Apple di Tab Custom Chrome, Apple mengirimkan ke server Anda, server mengarah kembali ke aplikasi | ID Layanan | Ya |
| Web | Pop-up JavaScript Apple | ID Layanan | Tidak (tetapi verifikasi token) |
The audience (aud) of the identity token follows the client ID. Tokens from iOS carry your bundle ID, tokens from Android and web carry the Services ID. Your backend has to accept both.
Langkah 1: Konfigurasi Akun Pengembang Apple
aktifkan kemampuan pada ID Aplikasi Anda
- Buka Sertifikat, Identifikasi & Profil dan pilih Identifikasi.
- Buka ID Aplikasi yang sesuai dengan Capacitor
appId(misalnyacom.example.app. Buatlah jika tidak ada. - Di bawah Kemampuan, centang Masuk dengan Apple dan simpan. Biarkan sebagai ID Aplikasi utama kecuali Anda mengelompokkan beberapa aplikasi.
Jika Anda mengubah kemampuan pada ID Aplikasi yang sudah ada, regenerasi profil pengaturan yang menggunakan itu. Profil lama tidak termasuk hak baru.
Buat ID Layanan (hanya Android dan web)
- Dalam Pengidentifikasiklik + dan pilih Pengidentifikasi Layanan.
- Pilih deskripsi seperti “Contoh Login Web” dan identifikasi seperti
com.example.app.signin. Nilai ini menjadiclientIddi Android dan web. - Terbuka layanan ID baru, centang Masuk dengan Apple, dan klik Konfigurasi.
- Pilih ID Aplikasi Anda sebagai ID utama.
- Tambahkan domain Anda di bawah Domain dan Subdomain (tanpa skema, misalnya
auth.example.com). - Tambahkan URL kembali di bawah URL Kembali dengan jalur penuh
https://dengan jalur penuh, misalnyahttps://auth.example.com/apple/callbackUntuk login web tambahkan URL halaman yang memanggil login, dengan dan tanpa slash akhir.
Buatlah kunci Sign in dengan Apple (Android dan pertukaran token server)
- Pergi ke Kunci, klik +, centang Sign in dengan Apple, dan klik Konfigurasi untuk memilih ID Aplikasi utama.
- Daftarkan kunci dan unduh
.p8file. Apple hanya memungkinkan Anda mengunduhnya sekali. - Perhatikan ID Kunci dan ID Tim Anda ID Tim di bagian kanan atas portal pengembang dan di bawah Anggota.
JWT yang dikirim ke endpoint token Apple. client_secret Daftarkan sumber email jika Anda mengirim email
Daftarkan sumber email jika Anda mengirim email
Sembunyikan Alamat Email , yang memberikan Andayang memberi Anda akses ke akun Apple Anda. @privaterelay.appleid.com alamat email. Apple hanya mengirimkan surel dari domain dan alamat email yang Anda daftarkan. Jasa > Masuk dengan Apple untuk Komunikasi Email. Lepaskan ini dan email reset kata sandi dan struk Anda akan hilang secara diam-diam.
Langkah 2: Pasang plugin
bun add @capgo/capacitor-social-login
bunx cap sync
Plugin ini menyertakan beberapa penyedia. Jika Anda hanya menggunakan Apple (dan mungkin Google), matikan yang lain di capacitor.config.ts SDK mereka tidak dikirimkan.
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 Jangan mengaktifkan kembali setelah mengubah blok ini. Provider yang dinonaktifkan tidak tersedia pada waktu runtime, jadi jangan menonaktifkan yang kamu gunakan. Menghapus Facebook juga menghapus AD_ID peringatan izin yang dihasilkan oleh SDK di Google Play Console.
Langkah 3: Pengaturan iOS
- Run
bunx cap open ios. - Jalankan App target, buka Tanda Tangan & Kemampuan, klik + Kemampuan, dan tambahkan Masuk dengan Apple.
- Pastikan identifikasi paket aplikasi sesuai dengan ID Aplikasi yang Anda konfigurasi.
Xcode menulis com.apple.developer.applesignin ke dalam App.entitlements. Jika Anda membangun di CI atau dengan Capgo Buildcommit file entitikasi tersebut, jika tidak maka build di cloud akan menghasilkan aplikasi yang gagal dengan ASAuthorizationError code 1000.
Tidak Info.plist URL scheme atau AppDelegate Langkah 4: Pengaturan Android
Langkah 4: Pengaturan Android
Apple tidak memiliki Android SDK. Plugin membuka halaman autorisasi Apple di Tab Custom dengan ID Layanan Anda. Apple kemudian mengirim hasilnya sebagai POST form ke URL HTTPS kembali Anda. Server Anda menukar code, kemudian mengarahkan ke tautan dalam yang membuka aplikasi kembali dengan token.
Tambahkan tautan dalam
Dalam android/app/src/main/AndroidManifest.xml, di dalam 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>
Kirimkan intent ke plugin
Ubah MainActivity.java jadi tautan dalam mendalam mencapai 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);
}
}
Filtering pada host menjaga tautan dalam mendalam lainnya (reset kata sandi, tautan pemasaran) tetap berfungsi.
Endpoint panggilan balik
Apple mengirimkan code, id_token, state, dan pada login pertama sebuah user String JSON ke URL Anda untuk dikembalikan. Endpoint harus menukar code dan mengarahkan ke aplikasi dengan success=true plus token, yang merupakan kontrak yang plugin ini memproses. Contoh Hono minimal menggunakan 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;
Plugin membaca token dari tautan dalam mendalam query string, jadi apa pun yang Anda masukkan dapat berakhir di log atau dibaca oleh aplikasi lain yang mendaftarkan scheme khusus yang sama. Kirimkan hanya token yang hidup singkat access_token dan id_token, tidak pernah token refresh, verifikasi id_token token, pastikan nonce dan pada server Anda sebelum Anda percayakan, dan gunakan scheme unik untuk aplikasi Anda (ID balik aplikasi Anda).
Bagaimana client_secret Token JWT dapat bertahan hingga enam bulan, tetapi menghasilkan token singkat per permintaan dapat menghindari kegagalan waktu kedaluwarsa. Simpan .p8 file di manajer rahasia Anda, tidak dalam bundle aplikasi.
Langkah 5: Pengaturan Web
Konfigurasi web menggunakan Apple JS dengan ID Layanan. Tambahkan URL halaman yang tepat yang memicu login ke ID Layanan URL kembali. Apple tidak menerima localhost, sehingga uji coba login web pada domain pratinjau yang di-deploy atau melalui terowongan dengan hostname yang sebenarnya.
Langkah 6: Inisialisasi dan login
inisialisasi sekali pada awal aplikasi. Kirimkan redirectUrl hanya pada Android dan web. Pada iOS, nilai redirectUrl tidak kosong membuat plugin mengirimkan hasil ke URL tersebut, yang Anda tidak inginkan dengan aliran asli.
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'
: '',
},
});
}
Kemudian, trigger login dari tombol. Gunakan nonce acak agar server Anda dapat menolak token ulang:
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;
}
}
Respons memberikan Anda:
| Bidang | Isi |
|---|---|
result.idToken |
JWT yang ditandatangani oleh Apple, hal yang diverifikasi oleh server Anda |
result.profile.user |
ID pengguna stabil Apple (sama dengan klaim) sub claim) |
result.profile.email |
Alamat email atau alamat relay pribadi, ketika diberikan |
result.profile.givenName / familyName |
Hanya pada autentikasi pertama |
result.accessToken |
Dengan pengaturan default ini, menyimpan otorisasi code, bukan token akses nyata |
result.authorizationCode |
Sedia ketika Anda menginisialisasi dengan useProperTokenExchange: true |
Jika server Anda memerlukan token pembaruan Apple (misalnya untuk memanggil endpoint revoke ketika pengguna menghapus akun mereka), atur useProperTokenExchange: true di dalam apple konfigurasi dan pertukaran authorizationCode di server.
Langkah 7: Verifikasi token identitas di backend Anda
Tidak pernah percaya pada bidang profil yang dikirim oleh klien. Verifikasi tanda tangan JWT terhadap kunci publik Apple dan periksa klaim:
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',
};
}
Simpan rekaman pengguna pada sub, bukan pada alamat email. Alamat email dapat menjadi alamat relay, dan pengguna dapat menghentikan berbaginya alamat tersebut. Setelah verifikasi, keluarkan token sesi Anda sendiri. Jika Anda menggunakan Supabase atau Firebase, mereka akan memverifikasi token untuk Anda, lihat Supabase dengan Capacitor Login Sosial.
Aturan App Store yang perlu diingat
- Pedoman 4.8: jika Anda menawarkan login Google, Facebook, atau login pihak ketiga lainnya sebagai sistem akun utama, Anda harus menawarkan pilihan privasi yang setara. Sign in dengan Apple memenuhi syarat.
- Pedoman 5.1.1(v)Aplikasi yang memungkinkan pengguna membuat akun harus memungkinkan mereka menghapus akun di aplikasi. Untuk pengguna Sign in dengan Apple, hubungi Apple's
https://appleid.apple.com/auth/revokeendpoint pada penghapusan. Lihat di Petunjuk Menghapus Akun. - Desain Tombol: Gunakan gaya tombol yang disetujui oleh Apple dan buat tombol ini setidaknya sebesar tombol login lainnya.
Penyelesaian Masalah
| Symptom | Penyebab yang Mungkin | Fix |
|---|---|---|
ASAuthorizationError code 1000 di iOS |
Keterampilan atau hak istimewa hilang dalam target yang dibangun | Kemampuan atau hak istimewa hilang di target yang dibangun App.entitlementsregenerasi profil |
| Code 1001 di iOS | Pengguna batal | Plugin menolak dengan USER_CANCELLED, dianggap sebagai tidak ada kesalahan |
invalid_client pada Android atau web |
Salah clientId Gunakan ID Layanan, periksa ID Tim, ID Kunci, dan isi kunci client_secret |
Gunakan ID Layanan, periksa ID Tim, ID Kunci, dan isi kunci |
invalid_request: Invalid redirect_uri |
URL Pengembalian tidak terdaftar dengan tepat | Tambahkan fullstack https:// Aplikasi tidak kembali setelah login Android |
| Skenario atau host filter intent berbeda dari redirect server | Diferensiasi scheme atau host filter intent dengan host redirect server | Bandingkan com.example.app://apple-login character by character |
apple.android.redirectUrl is null or empty |
Tidak redirectUrl Pada Android |
Masukkan URL panggilan HTTPS Anda saat menginisialisasi pada Android |
Nama dan email adalah null |
Bukan otorisasi pertama | Baca dari database Anda. Revoke aplikasi di pengaturan perangkat untuk menguji lagi |
Token ditolak oleh backend: aud Tidak cocok |
Hanya server yang menerima satu audiens | Menerima baik ID paket dan ID Layanan |
Perbaikan pengiriman setelah rilis
Masalah autentikasi paling banyak hidup di TypeScript: ID klien salah per platform, nonce yang hilang, penanganan kesalahan yang buruk. Mereka dapat dikirimkan kepada pengguna pada hari yang sama dengan Capgo pembaruan hidup tanpa harus menunggu Tinjauan Aplikasi. Perubahan akses, manifesto Android atau MainActivity perlu dibangun ulang di toko.
Dimana untuk pergi selanjutnya
- Tambahkan Google di samping Apple: Bagaimana untuk masuk dengan Google menggunakan Capacitor.
- Pilih campuran yang tepat dari metode untuk aplikasi Anda: Bagaimana untuk menambahkan autentikasi ke aplikasi Capacitor.
- Referensi plugin dan halaman platform: Social Login dokumen dan halaman pengaturan iOS Apple.