サンプルコード

「Appleでサインイン」によるユーザー認証の実装

アプリのユーザーアカウントを設定してサービスの利用を開始するための方法を提供します。

ダウンロード(英語)

概要

このサンプルアプリ「Juice」は、AuthenticationServicesフレームワークを使用し、ユーザーが自分のApple IDを使ってアカウントを設定しサインインするためのインターフェイスを提供します。ユーザーがアプリ用のアカウントを作成・設定するためのフォームを表示し、「Appleでサインイン」によりユーザーのApple IDの認証を行って、ユーザーのアカウントデータを表示します。

iOS 12以前での「Appleでサインイン」の実装については、他のプラットフォームでの「Appleでサインイン」の利用(英語)を参照してください。

サンプルコードプロジェクトの設定

サンプルコードプロジェクトを設定するには、Xcodeで以下の手順を実行してください。

  1. 「Signing & Capabilities」パネルで、ユニークなバンドルIDの設定(英語)を行います(バンドルIDを変更しないと次に進めません)。

  2. Apple IDアカウントの追加(英語)と、チームへのターゲットの割り当て(英語)を行い、Xcodeがプロビジョニングプロファイルを使用して「Appleでサインイン」を有効化できるようにします。

  3. 2ファクタ認証が設定されたApple IDでサインインを行う実行先を、「Scheme」ポップアップメニューから選択します。

  4. 必要に応じて、「Signing & Capabilities」パネルで「Register Device」をクリックし、プロビジョニングプロファイルを作成します。

  5. ツールバーで「Run」をクリックするか、または「Product」>「Run(⌘R)」を選択します。

「Appleでサインイン」ボタンの追加

サンプルアプリでは、LoginViewControllerによって、ログインフォームと「Appleでサインイン」ボタン(ASAuthorizationAppleIDButton(英語))がビュー階層の中に表示されます。ビューコントローラ自身もボタンのターゲットとして追加され、ボタンがタッチアップのイベントを受け取ったときに起動するアクションを指定します。

func setupProviderLoginView() {
    let authorizationButton = ASAuthorizationAppleIDButton()
    authorizationButton.addTarget(self, action: #selector(handleAuthorizationAppleIDButtonPress), for: .touchUpInside)
    self.loginProviderStackView.addArrangedSubview(authorizationButton)
}

Apple IDによる認可のリクエスト

ユーザーが「Appleでサインイン」ボタンをタップしたら、ビューコントローラはhandleAuthorizationAppleIDButtonPress()関数を呼び出します。この関数は、ユーザーの氏名とEメールアドレスに対する認可リクエストを実行して認証フローを開始します。するとシステムは、ユーザーが自分のApple IDでデバイスにサインインしているかどうかをチェックします。ユーザーがシステムレベルでサインインしていない場合、アプリはユーザーが「設定」でApple IDによるサインインを行うよう、メッセージを表示します。

@objcfunc handleAuthorizationAppleIDButtonPress() {
    let appleIDProvider = ASAuthorizationAppleIDProvider()
    let request = appleIDProvider.createRequest()
    request.requestedScopes = [.fullName, .email]
    
    let authorizationController = ASAuthorizationController(authorizationRequests: [request])
    authorizationController.delegate = self
    authorizationController.presentationContextProvider = self
    authorizationController.performRequests()
}

認可コントローラはpresentationAnchor(for:)(英語)関数を呼び出し、「Appleでサインイン」に関する情報をモーダルシートでユーザーに提示するためのウインドウをアプリから取得します。

func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
    return self.view.window!
}

ユーザーが自分のApple IDを使用してシステムレベルでサインインしている場合、「Appleでサインイン」の機能を説明するシートが表示され、続いて、ユーザーが自分のアカウントの情報を編集できるシートが表示されます。ユーザーは、自分の姓名や、連絡先情報のEメールアドレスを変更したり、Eメールアドレスをアプリに対して非公開にすることができます。ユーザーが、Eメールアドレスをアプリに対して非公開にすることを選択した場合、Appleは代理のEメールアドレスを生成し、ユーザーの私的なEメールアドレスにEメールを転送します。最後に、ユーザーはApple IDのパスワードを入力し、「Continue」をクリックしてアカウントを作成します。

ユーザー認証情報の処理

認証に成功したら、認可コントローラはデリゲート関数authorizationController(controller:didCompleteWithAuthorization:)(英語)を呼び出します。アプリはこの関数を使ってユーザーのデータをキーチェーンに保存します。

func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
    switch authorization.credential {
    case let appleIDCredential as ASAuthorizationAppleIDCredential:
        
        // Create an account in your system.
        let userIdentifier = appleIDCredential.user
        let fullName = appleIDCredential.fullName
        let email = appleIDCredential.email
        
        // For the purpose of this demo app, store the `userIdentifier` in the keychain.
        self.saveUserInKeychain(userIdentifier)
        
        // For the purpose of this demo app, show the Apple ID credential information in the `ResultViewController`.
        self.showResultViewController(userIdentifier: userIdentifier, fullName: fullName, email: email)
    
    case let passwordCredential as ASPasswordCredential:
    
        // Sign in using an existing iCloud Keychain credential.
        let username = passwordCredential.user
        let password = passwordCredential.password
        
        // For the purpose of this demo app, show the password credential as an alert.
        DispatchQueue.main.async {
            self.showPasswordCredentialAlert(username: username, password: password)
        }
        
    default:
        break
    }
}

認証に失敗した場合、認可コントローラはデリゲート関数authorizationController(controller:didCompleteWithError:)(英語)を呼び出し、エラー処理を行います。

func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) {
    // Handle error.
}

システムがユーザーを認証したら、アプリはResultViewControllerを表示します。これはユーザーが入力した氏名やEメールアドレスなど、フレームワークが要求するユーザー情報を示すものです。また、このビューコントローラは「サインアウト」ボタンを表示し、ユーザーデータをキーチェーンに保存します。ユーザーが「サインアウト」ボタンをタップしたら、アプリはビューコントローラとキーチェーンからユーザー情報を削除し、LoginViewControllerをユーザーに提示します。

既存の認証情報のリクエスト

LoginViewController.performExistingAccountSetupFlows()関数は、Apple IDとiCloudキーチェーンパスワードの両方を要求することによって、ユーザーが既存のアカウントを持っているかどうかを確認します。handleAuthorizationAppleIDButtonPress()と同様、認可コントローラはプレゼンテーションコンテンツプロバイダを設定し、LoginViewControllerオブジェクトにデリゲートします。

func performExistingAccountSetupFlows() {
    // Prepare requests for both Apple ID and password providers.
    let requests = [ASAuthorizationAppleIDProvider().createRequest(),
                    ASAuthorizationPasswordProvider().createRequest()]
    
    // Create an authorization controller with the given requests.
    let authorizationController = ASAuthorizationController(authorizationRequests: requests)
    authorizationController.delegate = self
    authorizationController.presentationContextProvider = self
    authorizationController.performRequests()
}

デリゲート関数authorizationController(controller:didCompleteWithAuthorization:)は、認証情報がApple ID(ASAuthorizationAppleIDCredential(英語))とパスワード認証情報(ASPasswordCredential(英語))のどちらであるかを確認します。認証情報がパスワードだった場合、システムはユーザーが既存のアカウントで認証できるよう、メッセージを表示します。

起動時のユーザー認証情報のチェック

サンプルアプリは、「Appleでサインイン」のユーザーインターフェイスを必要に応じて表示しているだけです。起動直後、アプリのデリゲートは、AppDelegate.application(_:didFinishLaunchingWithOptions:)関数で、保存されたユーザー認証情報のステータスをチェックします。

getCredentialState(forUserID:completion:)(英語)関数は、キーチェーンに保存されたユーザーIDのステータスを取得します。ユーザーがそのアプリに対する認可を与えていれば(ユーザーがデバイスのApple IDでサインインしている場合など)、アプリは実行を継続します。ユーザーがそのアプリに対する認可を与えていない場合や、認証情報のステータスが見つからない場合は、アプリはshowLoginViewController()関数を呼び出してログインフォームを表示します。

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    let appleIDProvider = ASAuthorizationAppleIDProvider()
    appleIDProvider.getCredentialState(forUserID: KeychainItem.currentUserIdentifier) { (credentialState, error) in
        switch credentialState {
        case .authorized:
            break // The Apple ID credential is valid.
        case .revoked, .notFound:
            // The Apple ID credential is either revoked or was not found, so show the sign-in UI.
            DispatchQueue.main.async {
                self.window?.rootViewController?.showLoginViewController()
            }
        default:
            break
        }
    }
    return true
}

関連項目

Appleでサインイン

「Appleでサインイン」のエンタイトルメント(英語)

アプリが「Appleでサインイン」を利用できるようにするためのエンタイトルメント。

キー: com.apple.developer.applesignin
class ASAuthorizationAppleIDProvider(英語)

ユーザーをApple IDによって認証するリクエストを生成するためのメカニズム。

class ASAuthorizationController(英語)

プロバイダによって生成された認可リクエストを管理するコントローラ。

class ASAuthorizationAppleIDCredential(英語)

Apple IDによる認証に成功した結果得られる認証情報。