メインコンテンツにジャンプ

How to Sign In with Apple Using Capacitor (2026)

Add Sign in with Apple to a Capacitor 8 app on iOS, Android, and web: Apple Developer setup, plugin code, backend token checks, and common errors.

Article Credits

マーティン・ドナディュー

ライター

ヴァレリア

レビュアー

ジョーダン

エディター

How to Sign In with Apple Using Capacitor (2026)

To add Sign in with Apple to a Capacitor app, install @capgo/capacitor-social-login、Appleのサインイン機能を有効にするには、App IDに SocialLogin.initialize({ apple: {} }), すると呼び出しを行います。 SocialLogin.login({ provider: 'apple' }). iOSでは、Appleのネイティブシートを開き、IDトークンを返します。 AndroidとWebでは、AppleのWeb OAuthフローを使用し、サービスIDとHTTPSリダイレクトURLが必要です。

This guide covers Apple Developer setup, the client code for iOS, Android and web, verifying the token on your backend, and the errors you are most likely to hit. It targets Capacitor 8 and Xcode 26, which App Store Connect has required for uploads since April 2026.

始める前に必要なもの

要件 なぜ
Apple Developerプログラムの有料会員 App ID、サービスID、Sign in with Appleキーは会員のみに利用可能です。
Xcode 26を搭載したMac 2026年にiOSアプリをビルドアップロードするために必要
Capacitor 8プロジェクト プラグインv8はCapacitor 8をターゲットしています
HTTPSドメインのコントロール AndroidとWebのみ。Appleは拒否します。 localhost そして、プレーンHTTPの戻りURL
バックエンドエンドポイント 常にトークンを検証する。Androidでは、Appleのコールバックも受け取ります。

署名証明書がまだない場合は、 iOS証明書生成ツール Macキーチェーンのダンスなしで作成します。

各プラットフォームでSign in with Appleのしくみ

Appleは、自社のプラットフォーム用にネイティブSDKを提供するのみなので、フローは異なります。

プラットフォーム メカニズム 使用するクライアントID ログイン用のバックエンドが必要です
iOS ネイティブ ASAuthorizationAppleIDProvider シート アプリのバンドルID いいえ
Android AppleのWeb OAuthをChromeカスタムタブで実行し、Appleはサーバーにポストし、サーバーはアプリに戻ります サービスID はい
Web AppleのJSポップアップ サービスID いいえ (しかし、トークンを検証する)

アイデンティティトークンの対象者 (aud) はクライアントIDに従います。 iOS から発行されたトークンにはアプリケーションパッケージIDが含まれ、Android と Web から発行されたトークンにはサービスIDが含まれます。 バックエンドは両方を受け入れる必要があります。

ステップ 1: Apple Developer アカウントの設定

App ID に機能を有効にします。

  1. 機能を有効にする 開く 証明書、識別子、& プロファイル と選択.
  2. Open the App ID that matches your Capacitor appId アプリ ID を開く (例えば __CAPGO_KEEP_0__ com.example.app. が存在しない場合は作成してください。
  3. 下記の 機能, これを Appleサインイン を選択し、保存してください。主 App ID として残してください。複数のアプリをグループ化する場合にのみ変更してください。

既存の App ID の機能を変更した場合、使用するプロビジョニング プロファイルを再生成してください。古いプロファイルには新しい特権が含まれていません。

Services ID を作成します (Android と Web のみ)

  1. 「 」, + をクリックし、 サービスID.
  2. 「例:ウェブログイン」などの説明と「例:ウェブログイン」などのIDを使用してください。 com.example.app.signin。この値はAndroidとWebで使用されます。 clientId 新しいサービスIDを開き、チェックボックスにチェックを入れてください。
  3. Appleでサインイン 、をクリックしてください。設定 Configure.
  4. ドメインを追加してください。
  5. ドメインとサブドメイン (スキームなし、例 (スキームなし、例えば) auth.example.com).
  6. AppleサインインをCapacitorで使用する方法 戻りURL 完全なパスで https:// 例えば https://auth.example.com/apple/callback。ウェブログインの場合、ログインを呼び出すページのURLを、末尾のスラッシュがあるかどうかに関係なく、指定してください。

サインイン用のAppleキーを作成(アンドロイドとサーバー用トークン交換)

  1. 以下の手順に従ってください キーをクリック +を選択 Appleサインインを選択 設定 を選択するための主なApp IDを選択する
  2. キーを登録し、ファイルをダウンロードする .p8 Appleは、ファイルをダウンロードすることを一度だけ許可しています。
  3. 注意 キーID と チームID (開発者ポータルの右上とメンバーシップの下に表示されます)。

サーバーは、Appleのトークンエンドポイントに送信するJWTを署名するためにキーを使用します。 client_secret メールソースを登録する場合は、メールを送信します

Appleのサインイン機能をCapacitorで使用する方法

ユーザーは選択 メールを隠す, これはあなたに @privaterelay.appleid.com アドレスを与えます。 Apple は、登録したドメインとアドレスからのみメールを転送します。 サービス > Apple でメールのサインイン. この設定をスキップすると、パスワードリセットと受領メールは静かに消えます。

ステップ 2: プラグインのインストール

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

プラグインは複数のプロバイダーを含みます。 Apple (および Google) のみを使用する場合は、他のプロバイダーを無効にして SDK の送信を停止してください: capacitor.config.ts Run

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 Services > Sign in with Apple for Email Communication AD_ID Google Play Console で SDK が発生する警告

Step 3: iOS の設定

  1. 実行 bunx cap open ios.
  2. セレクト App 実行 Capacitor、Cloudflare、GitHub、Capgo、API、SDK、CLI、npm、bun は、Capgo の製品およびブランド名を厳密に保存します。ステップ 3: iOS の設定 実行, です。 ステップ 3: iOS の設定.
  3. 実行

Capacitor、Cloudflare、GitHub、Capgo、API、SDK、CLI、npm、bun は、Capgo の製品およびブランド名を厳密に保存します。 com.apple.developer.applesignin に App.entitlements. CI または Capgo ビルドを実行した場合、 ASAuthorizationError code 1000.

No Info.plist URLスキームまたは AppDelegate AppleのiOSに変更は必要ありません。

Step 4: 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.

を追加します。 android/app/src/main/AndroidManifest.xml、内部の MainActivity <activity> 要素:

<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>

プラグインに意図を転送する

編集 MainActivity.java したいので、深いリンクはプラグインに到達する

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);
    }
}

ホストでフィルタリングすると、パスワードリセット、宣伝リンクなどの他の深いリンクが機能する

コールバックエンド

Appleが code, id_token, state、最初のログイン時には user JSON文字列を戻り先URLに送信する。エンドポイントはcodeを交換し、トークンを含むアプリにリダイレクトする必要がある。プラグインはこの契約を解釈する。Honoの最小限の例を使用 success=true プラグインは、深いリンクのクエリ文字列からトークンを読み取るので、そこに何らかの値を入れると、ログに記録されるか、同じカスタムスキームを登録した別のアプリが読み取る可能性がある。短期間のトークンだけを送信する 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;

items access_token そして id_token, そのリフレッシュトークンではありません、 id_token 署名と nonce サーバー上で署名を検証する前に、

署名と client_secret 独自のアプリスキーム (アプリの逆バンドルID) を使用してください。 .p8 JWTは最大6ヶ月間有効ですが、リクエストごとに短いJWTを生成することで、有効期限切れのトラブルを回避できます。

秘密管理者にファイルを保存し、アプリのバンドルに保存しないでください。

ステップ 5: Web 設定 localhostWeb ビルドでは Apple JS を使用し、サービス ID を使用します。サービス ID の戻り先 URL に、ログインをトリガーするページの URL を追加してください。Apple は

を許可していないため、Web ログインをテストするには、デプロイされたプレビュー ドメインまたは実際のホスト名を持つトンネルを使用してください。

ステップ 6: 初期化とサインイン redirectUrl AndroidおよびWebのみ。iOSでは非空 redirectUrl プラグインはそのURLに結果をPOSTします。これはネイティブフローでは望ましくありません。

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

次に、ボタンからログインをトリガーします。サーバーが再送されたトークンを拒否できるように、ランダムなnonceを使用してください:

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

レスポンスは次のようになります。

フィールド 内容
result.idToken Appleから取得した署名済みJWT、サーバーが検証するもの
result.profile.user Appleの安定したユーザーID(同等のclaim) sub claim)
result.profile.email 初回の認可のみ
result.profile.givenName / familyName デフォルトの設定では、この認可__CAPGO_KEEP_0__が保持されますが、これは実際のアクセストークンではありません
result.accessToken デフォルトの設定では、この値は認可 code を保持し、実際のアクセストークンではありません。
result.authorizationCode Capacitorを初期化するときに useProperTokenExchange: true

サーバーがAppleのリフレッシュトークンを必要とする場合(例えばユーザーがアカウントを削除したときに呼び出すリバースエンドポイント)、 useProperTokenExchange: true configと apple サーバーで交換します。 authorizationCode ステップ7: バックエンドでアイデンティティトークンを検証する

Step 7: バックエンドで ID トークンを検証する

クライアントから送信されるプロファイルフィールドを信頼しない。Appleの公開鍵でJWTの署名を検証し、クレームを確認する。

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

ユーザー記録を subに、メールアドレスではなくにします。メールアドレスはリレー先のアドレスになる可能性があり、ユーザーはそれを共有するのをやめられる可能性があります。検証後、独自のセッショントークンを発行します。SupabaseまたはFirebaseを使用している場合、トークンを検証してくれるので、 SupabaseとCapacitorのSocial Login.

App Storeのルールを考慮する

  • ガイドライン 4.8Google、Facebook、または他の第三者ログインを主なアカウントシステムとして提供する場合は、同等のプライバシーに焦点を当てたオプションも提供する必要があります。Sign in with Appleはそのようなオプションです。
  • ガイドライン 5.1.1(v)Sign in with Appleユーザーには、Appleのエンドポイントを呼び出すことなくアカウントを削除できるようにする必要があります。詳細は、 https://appleid.apple.com/auth/revoke アカウント削除ガイド ボタン設計.
  • : Appleの承認済みボタンスタイルを使用し、他のログインボタンと同じ程度の目立つボタンを使用する必要があります。トラブルシューティング

症状

原因 解決策 Fix
ASAuthorizationError code 1000 iOS iOSビルドターゲットに機能または特権が欠けている XcodeにSign in with Appleを追加し、コミット App.entitlementsプロファイルを再生成
Code 1001 iOS ユーザーがキャンセル プラグインが拒否 USER_CANCELLED, これをエラーとして扱わない
invalid_client AndroidまたはWeb上で 間違った clientId (bundle IDが代わりにServices IDが使用されている)または悪い client_secret Services IDを使用し、Team ID、Key ID、およびキー内容を確認
invalid_request: Invalid redirect_uri 正確に登録されていないReturn URL URLを完全に追加 https:// URLの末尾のスラッシュを含めてサービスIDのURLを追加
Androidログイン後にアプリが戻らない Androidログイン後のサーバーからリダイレクトされるURLのスキームまたはホストが意図したものと異なる URLを com.example.app://apple-login 文字列を文字列単位で比較
apple.android.redirectUrl is null or empty Android redirectUrl Android 名前とメールアドレスは
最初の認証ではない null データベースから読み取り、再度アプリをデバイスの設定で削除してテストする 比較
バックエンドからトークンが拒否されました: aud 一致しません サーバーは1つのアウディエンスのみを受け付ける 両方のバンドルIDとサービスIDを受け付ける

リリース後の修正

ほとんどの認証の問題はTypeScriptにあります:プラットフォームごとに間違ったクライアントID、nonceが欠けている、エラー処理が悪い。ユーザーに同じ日に配信できるのは Capgoのライブアップデート アプリレビューを待つ必要なく MainActivity 特権の変更、Androidマニフェスト、

ネイティブな変更は新しいストアビルドが必要です

リアルタイムのアップデートはCapacitorアプリに

ウェブ層のバグが生じた場合、Capgoを通じて修正を配信し、アプリストアの承認待ちの日数を待たずに

14日間の無制限の無料試用版。クレジットカードは必要ありません。

Capgo gives you the best insights you need to create a truly professional mobile app.