mcpField
A
axiom-passkeys

axiom-passkeys

github.com/charleswiltgenセキュリティ1.2k35 回インストール今日更新
リポジトリ

axiom-passkeys について

iOS 開発者向けのパスキー認証ガイドです。登録、サインイン、AutoFill、パスワードからの移行を扱い、関連ドメインの設定やパスキー・パスワード・Sign in with Apple の併用フローも説明します。

主な機能

  1. 01パスキー登録とアサーションの実装
  2. 02AutoFill によるサインインの設定
  3. 03iOS 18+ の自動アップグレード要求
  4. 04パスキー、パスワード、Apple サインインの併用
  5. 05関連ドメインと AASA ファイルの設定
  6. 06クロスデバイスのパスキーサインイン

活用シーン

  1. 01iOS アプリへのパスキーサインイン追加
  2. 02QuickType バーにパスキーが出ない問題の調査
  3. 03パスワード利用者の段階的な移行
  4. 04複数の認証情報を扱うサインインフローの構築

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/charleswiltgen/axiom --skill axiom-passkeys

インストール後、Claude Code で/axiom-passkeysを入力して読み込まれたか確認します。

詳細

パスキー

iOS アプリ向けのパスキー認証 — 登録、アサーション、AutoFill を活用したリクエスト、自動アップグレード、複合クレデンシャルフロー、パスワードベース認証からの移行に対応します。

このスキルを使う場面

次のような場合に使用します。

  • ☑ iOS アプリにパスキーサインインを追加する
  • ☑ パスワードベースの認証をパスキーに置き換える
  • ☑ パスキーの登録またはアサーション用に ASAuthorizationController を設定する
  • ☑ AutoFill を活用したパスキーリクエスト(QuickType バー)を設定する
  • ☑ 既存のパスワードユーザー向けにパスキーの自動アップグレードを追加する(iOS 18 以降)
  • ☑ 複合クレデンシャルリクエスト(パスキー + パスワード + Sign in with Apple)に対応する
  • ☑ webauthn/webcredentials 用に関連付けられたドメインを設定する
  • ☑ パスキーのアサーション失敗や QuickType の候補が表示されない問題をデバッグする

プロンプト例

「アプリにパスキーサインインを追加するにはどうすればよいですか?」 「QuickType バーにパスキーが表示されません」 「既存のパスワードユーザーをパスキーに移行するにはどうすればよいですか?」 「ASAuthorizationError.canceled — 何が問題なのでしょうか?」 「移行期間中にパスキーとパスワードの両方に対応するにはどうすればよいですか?」 「パスキー用の関連付けられたドメインを設定するにはどうすればよいですか?」 「performRequests と performAutoFillAssistedRequests の違いは何ですか?」 「iOS 18 でパスキーの自動アップグレードを追加するにはどうすればよいですか?」

注意すべき兆候

次のような場合は、誤った方向に進んでいる可能性があります。

  • ❌ パスキーを利用できるのに、引き続きパスワードを主要な認証手段として使っている — パスキーは「追加のセキュリティ」ではありません。パスワードに取って代わるものです。パスワードだけでサインインできる状態は、フィッシングの機会を残したままにしています。
  • ❌ ユーザー名フィールドに .username の textContentType を指定していない — これを設定しないと、システムはフィールドをパスキーのクレデンシャルに関連付けられません。ラベルのないフィールドでは、AutoFill はパスキーを候補として表示しません。
  • ❌ 主要なサインインフローに performRequests() を使用している — これでは QuickType バーにパスキーを表示する代わりに、モーダルシートが表示されます。主要なフローには performAutoFillAssistedRequests() を使用してください。performRequests() は登録と、ユーザーが明示的に「サインイン」ボタンをタップした場合に限って使用します。
  • ❌ サーバー側で userVerification を "required" に設定している — 生体認証に対応していないデバイスではサインインできなくなります。プラットフォームはデバイスごとに適切な検証を処理します。"preferred"(デフォルト)を使用してください。
  • ❌ 拡張機能やメインアプリ以外のコンテキストでパスキーを作成している — パスキーの作成にはメインアプリのターゲットが必要です。拡張機能ではアサーションを実行できますが、登録はできません。
  • ❌ 複合クレデンシャルリクエストに対応していない — 移行期間中、ユーザーはパスキーを持っている場合も、パスワードを持っている場合も、どちらも持っていない場合もあります。1 つの ASAuthorizationController ですべてに対応できます。パスキーだけを提供すると、まだアップグレードしていないユーザーが締め出されます。

パスキーを使う理由

パスキーは、パスワードを少し改良したものではありません。認証の仕組みそのものを置き換えます。

フィッシングに強い:各パスキーは、特定のドメインに暗号学的に紐付けられています。secure-myapp.com の偽ログインページから、myapp.com 用に作成されたパスキーを使うことはできません。誤ったサイトに入力してしまうクレデンシャルも存在しません。

漏えいするクレデンシャルデータベースが不要:サーバーに保存されるのは公開鍵だけです。情報漏えいが発生しても、悪用できるものはありません。解読されるパスワードハッシュも、再利用される共有シークレットもありません。

ワンタップでサインイン:入力の代わりに Face ID または Touch ID を使用します。登録もアサーションも、どちらもワンタップで完了します。

FIDO Alliance 標準:WebAuthn/CTAP2 プロトコルを使用します。Apple、Google、Microsoft のプラットフォーム間で動作します。iPhone で作成したパスキーは iCloud Keychain で同期され、Mac、iPad、ウェブでも使用できます。

普及状況:Apple はパスキーをシステムの主要機能として提供しています。iCloud Keychain がパスキーを同期し、Passwords アプリで管理できます。サードパーティのクレデンシャルマネージャー(1Password、Dashlane)も iOS 17 以降、ネイティブで対応しています。

関連付けられたドメインの設定

パスキーを使うには、アプリとサーバーを結び付ける関連付けられたドメインが必要です。これがないと、システムはアプリでパスキーを提示しません。

1. Entitlement を追加する

Xcode で、Target > Signing & Capabilities > + Associated Domains を選択します。

次を追加します。

webcredentials:example.com
2. AASA ファイルをホストする

HTTPS 経由で、ドメインの /.well-known/apple-app-site-association を Content-Type: application/json として配信します。

{
  "webcredentials": {
    "apps": [
      "TEAMID.com.example.myapp"
    ]
  }
}

要件:

  • 有効な証明書を使用した HTTPS(自己署名証明書は不可)
  • リダイレクトなし — ファイルを指定されたパスで直接配信する必要があります
  • TEAMID は Apple Developer Team ID です。バンドル ID のプレフィックスではありません
  • ファイルはサブディレクトリではなく、ルートドメインに配置する必要があります
3. 検証
curl -s "https://example.com/.well-known/apple-app-site-association" | python3 -m json.tool

Apple の CDN は AASA ファイルをキャッシュします。変更が反映されるまで、最大 24 時間かかる場合があります。開発中は、デバイスの「設定」の「デベロッパ」から「Associated Domains Development」を有効にし、?mode=developer クエリパラメーターを使用してください。

登録フロー

登録すると、新しいパスキーが作成され、ユーザーの認証情報マネージャーに保存されます。

import AuthenticationServices

func registerPasskey(challenge: Data, userName: String, userID: Data) {
    let provider = ASAuthorizationPlatformPublicKeyCredentialProvider(
        relyingPartyIdentifier: "example.com"
    )

    let request = provider.createCredentialRegistrationRequest(
        challenge: challenge,
        name: userName,
        userID: userID
    )

    let controller = ASAuthorizationController(authorizationRequests: [request])
    controller.delegate = self
    controller.presentationContextProvider = self
    controller.performRequests()
}

主なパラメーター:

  • relyingPartyIdentifier — 関連付けられたドメインと一致する必要があります(https:// プレフィックスは不要です)
  • challenge — サーバーが生成する暗号学的チャレンジです(少なくとも 16 バイトのランダムデータを使用してください)。チャレンジを再利用しないでください。
  • name — パスキープロンプトおよび「パスワード」アプリでユーザーに表示される名前です
  • userID — ユーザーアカウントの不透明な識別子です。メールアドレスやユーザー名は使用せず、ランダムな UUID またはサーバー側のユーザー ID を使用してください
登録レスポンスの処理
func authorizationController(controller: ASAuthorizationController,
                             didCompleteWithAuthorization authorization: ASAuthorization) {
    guard let credential = authorization.credential
        as? ASAuthorizationPlatformPublicKeyCredentialRegistration else { return }

    let attestationObject = credential.rawAttestationObject
    let clientDataJSON = credential.rawClientDataJSON
    let credentialID = credential.credentialID

    // Send attestationObject, clientDataJSON, credentialID to your server
    // Server validates and stores the public key
}

ユーザーが明示的にパスキーの作成を選択したため、登録では performRequests()(モーダル)を使用します。モーダル表示が適切なのは、この場合です。

アサーションフロー(サインイン)

サインインには、UX の状況に応じた 2 つの方法があります。

AutoFill アシスト(主要な方法)

推奨されるサインインフローです。ユーザーが .username コンテンツタイプのテキストフィールドをタップすると、パスキーが QuickType バーに表示されます。モーダルによる中断なしで、1 回のタップでサインインできます。

func signInWithAutoFill() {
    let provider = ASAuthorizationPlatformPublicKeyCredentialProvider(
        relyingPartyIdentifier: "example.com"
    )

    let request = provider.createCredentialAssertionRequest(
        challenge: serverChallenge
    )

    let controller = ASAuthorizationController(authorizationRequests: [request])
    controller.delegate = self
    controller.presentationContextProvider = self
    controller.performAutoFillAssistedRequests()
}

重要な詳細:

  • performAutoFillAssistedRequests() は早めに呼び出してください。ユーザーがユーザー名フィールドにフォーカスする前に、viewDidAppear またはサインイン画面が表示されたときに呼び出します。
  • ユーザー名の UITextField には .textContentType = .username を設定する必要があります。設定しないと、QuickType バーにパスキー候補が表示されません。
  • リクエストに allowedCredentials を設定しないでください。AutoFill では、ドメインで利用可能なすべてのパスキーを表示する必要があります。
  • リクエストは、ユーザーが認証情報を選択するか、画面から移動するか、キャンセルするまで有効です。
モーダル(代替方法)

ユーザーが「サインイン」ボタンを明示的にタップした場合や、ユーザー名がわかっていて特定の認証情報をリクエストする場合に使用します。

func signInWithModal(allowedCredentials: [ASAuthorizationPlatformPublicKeyCredentialDescriptor]? = nil) {
    let provider = ASAuthorizationPlatformPublicKeyCredentialProvider(
        relyingPartyIdentifier: "example.com"
    )

    let request = provider.createCredentialAssertionRequest(
        challenge: serverChallenge
    )

    if let allowedCredentials {
        request.allowedCredentials = allowedCredentials
    }

    let controller = ASAuthorizationController(authorizationRequests: [request])
    controller.delegate = self
    controller.presentationContextProvider = self
    controller.performRequests()
}

ユーザーの認証情報 ID がわかっている場合は、allowedCredentials を使用します(たとえば、ユーザーがユーザー名を入力し、サーバーが登録済みの認証情報 ID を返した場合)。これにより、パスキーの選択対象をそのアカウントに絞り込めます。

アサーションレスポンスの処理
func authorizationController(controller: ASAuthorizationController,
                             didCompleteWithAuthorization authorization: ASAuthorization) {
    guard let credential = authorization.credential
        as? ASAuthorizationPlatformPublicKeyCredentialAssertion else { return }

    let signature = credential.signature
    let clientDataJSON = credential.rawClientDataJSON
    let authenticatorData = credential.rawAuthenticatorData
    let credentialID = credential.credentialID
    let userID = credential.userID

    // Send to server for verification
}

パスキーの自動アップグレード(iOS 18 以降)

ユーザーのフローを中断せずに、パスワードを使用するユーザーをパスキーにサイレントにアップグレードします。システムはアップグレードを確認する短い通知を表示します。モーダル表示や追加のタップは不要です。

仕組み

ユーザーがパスワードでサインインすると、システムは同じアカウントのパスキーを自動的に作成できます。これは、次の条件を満たす場合に行われます。

  1. 認証情報マネージャーが自動アップグレードに対応している
  2. ユーザーが同じアカウントでパスワード認証に成功した直後である
  3. アプリが条件付き登録をリクエストしている
実装
func requestAutomaticUpgrade(challenge: Data, userName: String, userID: Data) {
    let provider = ASAuthorizationPlatformPublicKeyCredentialProvider(
        relyingPartyIdentifier: "example.com"
    )

    let request = provider.createCredentialRegistrationRequest(
        challenge: challenge,
        name: userName,
        userID: userID
    )
    request.requestStyle = .conditional

    let controller = ASAuthorizationController(authorizationRequests: [request])
    controller.delegate = self
    controller.presentationContextProvider = self
    controller.performAutoFillAssistedRequests()
}

重要なポイント:.requestStyle = .conditional により、登録は機会に応じて実行されます。条件が整っていればサイレントに成功し、整っていなければサイレントに失敗します。失敗コールバックをエラーとして扱わないでください。これは、今回条件が満たされなかったことを意味します。

呼び出すタイミング:ユーザーがパスワード認証に成功した後です。まず、そのユーザーがこのアカウントのパスキーをすでに持っているか確認してください。持っている場合は、アップグレードをリクエストしないでください。

サーバーの要件

サーバーは、パスワードサインインの直後に非同期の登録リクエストが届く可能性に備えておく必要があります。userID と challenge は有効であり、セッションに関連付けられている必要があります。

複合的な認証情報のリクエスト

移行期間中、ユーザーはパスキー、パスワード、または「Sign in with Apple」の認証情報を持っている場合があります。1 つの ASAuthorizationController で、これら 3 種類すべてを処理できます。

func signInWithCombinedRequest() {
    var requests: [ASAuthorizationRequest] = []

    // Passkey assertion
    let passkeyProvider = ASAuthorizationPlatformPublicKeyCredentialProvider(
        relyingPartyIdentifier: "example.com"
    )
    requests.append(
        passkeyProvider.createCredentialAssertionRequest(challenge: serverChallenge)
    )

    // Password
    let passwordProvider = ASAuthorizationPasswordProvider()
    requests.append(passwordProvider.createRequest())

    // Sign in with Apple
    let appleIDProvider = ASAuthorizationAppleIDProvider()
    requests.append(appleIDProvider.createRequest())

    let controller = ASAuthorizationController(authorizationRequests: requests)
    controller.delegate = self
    controller.presentationContextProvider = self
    controller.performAutoFillAssistedRequests()
}
複数の認証情報タイプの処理
func authorizationController(controller: ASAuthorizationController,
                             didCompleteWithAuthorization authorization: ASAuthorization) {
    switch authorization.credential {
    case let credential as ASAuthorizationPlatformPublicKeyCredentialAssertion:
        // Passkey sign-in — verify with server
        handlePasskeyAssertion(credential)

    case let credential as ASPasswordCredential:
        // Password sign-in — verify, then offer passkey upgrade
        handlePasswordSignIn(credential)

    case let credential as ASAuthorizationAppleIDCredential:
        // Apple ID sign-in
        handleAppleIDSignIn(credential)

    default:
        break
    }
}

パスワードでのサインインに成功したら、自動アップグレードフローを呼び出して、ユーザーを段階的にパスキーへ移行します。

デバイス間のサインイン

パスキーが保存されていないデバイスでは、ユーザーはスマートフォンを認証器として使ってサインインできます。

仕組み:

  1. アプリでパスキーのアサーションリクエストを提示します
  2. サインインを要求しているデバイスに、システムが QR コードを表示します
  3. ユーザーがパスキーを保存したスマートフォンで QR コードをスキャンします
  4. Bluetooth による近接確認で、スマートフォンが物理的に近くにあることを確認します
  5. ユーザーがスマートフォンで Face ID/Touch ID を使って認証します
  6. 元のデバイスでアサーションが完了します

アプリの変更は不要です。これはシステムレベルの機能です。パスキーに対応するデバイスなら、どれでもデバイス間認証器として使用できます。通信は Apple のリレーサーバーを介してエンドツーエンドで暗号化されます。

Bluetooth が必要です:両方のデバイスで Bluetooth を有効にする必要があります。この近接確認によってリモートフィッシングを防止します。つまり、認証するデバイスは、リクエスト元のデバイスの近くに物理的にある必要があります。

移行戦略

フェーズ 1 — パスワードと併せてパスキーのサポートを追加する

既存のパスワード認証を維持します。パスキーの登録とアサーションを追加します。複合的な認証情報のリクエストを使用して、どちらの方法でも動作するようにします。

サーバーの変更:登録とアサーション用の WebAuthn エンドポイントを追加します。パスワードのハッシュと併せて公開鍵を保存します。どちらの認証方法でも、同じユーザーセッションとして認証されるようにします。

アプリの変更:登録フロー(パスワードでのサインイン後に提示)、アサーションフロー(AutoFill による補助)、複合リクエストを実装します。

フェーズ 2 — 自動アップグレード(iOS 18 以降)

パスワードでのサインイン後に、条件付き登録リクエストを追加します。ユーザーは時間の経過とともに、通知なくパスキーへ移行します。アップグレードの指標を追跡して、利用状況を測定します。

ユーザーの操作は不要です。システムがアップグレードを透過的に処理します。

フェーズ 3 — フィッシングされやすい認証要素を減らす

パスキーを利用しているアカウントでは、次の対応を検討してください。

  • パスキーでは不要なため、パスワードのリセットフローを削除する
  • パスキーは本質的に「デバイスの所持+生体認証」の 2 要素認証であるため、SMS による 2 要素認証を廃止する
  • メール/SMS の代わりに、別のデバイス上のパスキーによるアカウント復旧を提供する

パスワードを強制的に削除しないでください。パスワードレスにするかどうかは、ユーザーが選べるようにします。パスキーに対応していないデバイスからパスワードでのアクセスが必要なユーザーもいます。

パスワードアプリとの連携(iOS 18 以降)

パスワードアプリは、関連付けられたドメインの OpenGraph メタデータを使って、アプリ名とアイコンを表示します。ウェブサイトの <head> に次の内容を追加してください。

<meta property="og:title" content="MyApp" />
<meta property="og:image" content="https://example.com/icon.png" />

これにより、ユーザーの認証情報マネージャーにアプリが表示されます。これがない場合、パスワードアプリにはドメイン名のみが表示されます。

非合理化

非合理化実態所要時間
「今はパスワードで問題ありません」パスワードでのサインインは、すべてフィッシングの侵入口になります。認証情報を使ったリスト型攻撃は実際に金銭的損害をもたらします。情報漏えいによる被害額は平均で450万ドルです。パスキーを使えば、攻撃対象領域全体をなくせます。継続的なリスク、または実装に2〜3日
「パスキーは後で追加します」AutoFill に対応したパスキーリクエストの統合に必要な作業量は、AutoFill に対応したカスタムのパスワード入力フィールドと同じです。先送りしても時間の節約にはなりません。どちらを選んでも実装工数は同じ
「ユーザーはパスキーを理解できません」ユーザーが公開鍵暗号を理解する必要はありません。「Face ID でサインイン」と表示され、タップするだけです。Apple、Google、Microsoft は、すべてのプラットフォームでパスキーをデフォルトとして提供しています。ユーザーへの追加説明は不要
「サーバーが WebAuthn に対応していません」主要なバックエンド(Python、Node、Go、Ruby、Java、.NET)には、サーバー側の WebAuthn ライブラリがあります。そのほとんどは十分にテストされ、活発にメンテナンスされています。サーバー側の統合に1〜2日
「生体認証を利用できないユーザーはどうしますか?」デバイスのパスコードも、有効なユーザー検証方法です。サポート対象のデバイスには、すべて少なくともパスコードがあります。userVerification を "preferred" に設定すれば、プラットフォームが適切に処理します。追加作業なし。プラットフォームが処理します
「パスワードをフォールバックとして永久に残す必要があります」認証情報を組み合わせたリクエストでは、パスワードとパスキーの両方を同時にサポートできます。自動アップグレードを利用して、段階的に移行してください。パスキーを主な方法にしながら、パスワードを無期限に残すこともできます。どちらかを選ぶ必要はありません。両方を併用できます

プレッシャーがかかる場面

シナリオ1:「ユーザーはパスキーを使う準備ができていません」

背景:プロダクトマネージャーが、ユーザーが混乱するリスクを理由に、パスキーの導入に反対しています。

懸念:「ユーザーは技術に詳しくありません。パスキーが何なのか理解できないでしょう。パスワードを使い続けて、パスキーは来年追加しましょう。」

実態:Apple は、iPhone、iPad、Mac、Apple Watch、クロスデバイス認証を使った Windows など、すべてのプラットフォームに組み込まれたシステム機能としてパスキーを提供しています。ユーザーには QuickType バーに「Face ID でサインイン」と表示されます。「WebAuthn CTAP2 公開鍵認証情報」と表示されるわけではありません。Passwords アプリは、パスキーをパスワードと一緒に透過的に管理します。Apple のアカウントシステム、Google アカウント、Microsoft アカウントはいずれもパスキーを使用しています。ユーザーはすでに、ほかのサービスでパスキーを使っています。

適切な対応:認証情報を組み合わせたリクエストを実装します。既存のパスワードユーザーは、引き続きパスワードでサインインできます。認証情報マネージャーが対応しているユーザーには、パスキーが自動的に表示されます。自動アップグレード(iOS 18 以降)を追加すれば、ユーザーの操作なしで段階的に移行できます。

反論への回答例:「ユーザーがパスキーを理解する必要はありません。『Face ID でサインイン』と表示され、タップするだけで完了します。Apple、Google、Amazon は、すでに自社のサインインにパスキーを使っています。パスワードと併用するので、誰の利用手順も変わりません。パスキーを利用できるユーザーには、より便利な体験が自動的に提供され、それ以外のユーザーはこれまでどおり利用できます。」

シナリオ 2:「まずはパスワード認証をリリースし、パスキーは後から追加する」

背景:新しいアプリのリリース期限が迫っています。開発者はパスキーのサポートをリリース後のアップデートに先送りしたいと考えています。

プレッシャー:「金曜日までにリリースする必要があります。パスワード認証は動作します。パスキーは次のスプリントで追加します。」

実情:AutoFill を利用したパスキーリクエストの実装にかかる労力は、AutoFill サポート、セキュアなストレージ、「パスワードを忘れた場合」のフローを備えた、使いやすいパスワード入力フィールドの構築と同程度です。どちらにしても、ASAuthorizationController との連携を実装することになります。違いは、1 つのプロバイダー(パスワード)を接続するか、3 つ(パスキー+パスワード+Apple ID)を接続するかです。リクエストを組み合わせても、デリゲートに追加されるのは約 30 行です。

適切な対応:最初から複数の認証情報を組み合わせたリクエストを実装します。サーバーには WebAuthn エンドポイントが必要ですが、クライアント側の作業はほぼ同じです。初日からパスキーをサポートしてリリースすれば、後から組み込む必要はなく、「次のスプリント」が「次の四半期」になる事態も避けられます。

反論への返答例:「AutoFill を利用したパスキーでは、パスワードの AutoFill に使うのと同じ ASAuthorizationController を使います。パスキーのサポートを追加する作業は、デリゲートに約 30 行を加えるだけで、1 スプリント分の作業ではありません。サポートなしでリリースすると、今パスワードのフローを構築し、後からパスキーを追加するために認証フローを作り直すことになります。一度で済ませましょう。」

チェックリスト

パスキー認証をリリースする前に確認してください。

Associated Domains:

  • Associated Domains 機能に webcredentials:yourdomain.com を追加している
  • AASA ファイルを HTTPS 経由で /.well-known/apple-app-site-association に配信している
  • AASA ファイルに正しい Team ID とバンドル識別子が含まれている
  • AASA ファイルのパスでリダイレクトが発生しない
  • AASA の変更が反映されている(またはテスト用に開発者モードを有効にしている)

登録:

  • サーバーが登録の試行ごとに一意のチャレンジを生成する
  • userID が不透明な値である(メールアドレスやユーザー名ではない)
  • relyingPartyIdentifier が関連付けられたドメインと完全に一致する
  • 登録で performRequests() を使用する(モーダル — 明示的な作成に適切)
  • 登録成功後、サーバーが credentialID と公開鍵を保存する

アサーション(サインイン):

  • ユーザー名のテキストフィールドに .textContentType = .username を設定している
  • AutoFill を利用したリクエストを早い段階で(フィールドにフォーカスする前に)呼び出す
  • AutoFill の経路で performAutoFillAssistedRequests() を使用する
  • 明示的なサインインボタン用に performRequests() によるモーダルのフォールバックを用意している
  • AutoFill を利用したリクエストに allowedCredentials を設定しない
  • サーバーが署名、authenticatorData、clientDataJSON を検証する

複合リクエスト(複数の認証方式をサポートする場合):

  • パスキー、パスワード、Apple ID のプロバイダーをすべて含めている
  • デリゲートの switch 文ですべての認証情報の種類を処理する
  • パスワードでのサインイン時に、自動的にパスキーへのアップグレードフローを開始する

自動アップグレード(iOS 18 以降):

  • 登録リクエストで .requestStyle = .conditional を使用する
  • アップグレードリクエストで performAutoFillAssistedRequests() を使用する
  • 失敗コールバックを「条件が満たされていない」として扱い、エラーとは見なさない
  • アップグレードをリクエストする前に、既存のパスキーを確認する

エラー処理:

  • ASAuthorizationError.canceled を適切に処理する(ユーザーによる閉じる操作 — エラーではない)
  • ASAuthorizationError.failed を、デバッグ用のコンテキスト情報とともにログに記録する
  • サーバー検証中のネットワーク障害によって、認証状態が不整合にならない

リソース

WWDC:2022-10092、2024-10125

ドキュメント:/authenticationservices、/authenticationservices/public-private-key-authentication/supporting-passkeys

スキル:axiom-keychain

コメント