AkkeyLab

Re-Authenticate with Sign in with Apple

Sign in with Apple は WWDC2019 で発表された Apple ID による認証を実現する仕組みです。今回は、その Sign in with Apple で再認証処理を行う方法をご紹介します。 環境としては、アカウントの管理は FirebaseAuth を利用し、 RxSwift を導入しているものとします。

完成形

まずは完成形をみていただきます。呼び出し部分はたったコレだけです。全てを RxSwift のストリームで処理しても良いですが、様々な再認証画面に対応させるために、クロージャを一部用いております。 この実装方法に興味を持っていただけたのであれば、記事を読み進めることをお勧めします。
この再認証処理を行うことによって credential が有効であるかを確認します。すなわち、 Apple ID が有効であるかを確認することができます。

final class viewController: UIViewController, Reauthable {
    func reauth() {
        appleReauth { [weak self] in
            self?.success()
        }
    }
}

前提条件

下記の記事の実装が行われていることが前提となります。この記事から読む方は、各種変数名・関数名を以下の記事で検索していただくことでその内容を理解することができます。
従って、共通する処理に関してはこの記事で省略していきます。
Sign in with Apple thinking in the stream by RxSwift

事前準備:RxSwift 対応

まず、 FirebaseAuth の処理を Observable として扱えるように拡張します。各種エラーは独自で定義、ハンドリングする必要があります。ここでは省略して表示しています。

extension Reactive where Base: FirebaseAuth.User {
    func reauth(with credential: AuthCredential) -> Single<AuthDataResult> {
        return Single.create { observer in
            self.base.reauthenticate(with: credential) { result, error in
                guard let result = result, error == nil else {
                    observer(.error(AuthError.unAuthorized))
                    return
                }
                observer(.success(result))
            }
            return Disposables.create()
        }
    }
}

再認証に関する処理の実装

先程実装した再認証処理を呼び出す部分になります。ここで登場する各種関数・変数は Sign-up や Sign-in の処理でも使用しているものがほとんどであることに気がつくと思います。ここで注意しなければならない点としては、メモリリークの発生です。1つの画面内で、その画面が生成されて破棄されるまでの間に複数回認証画面が出る可能性がある場合、以下の処理では購読が解除されない可能性が発生します。
では、この問題を解決する方法を考えていきましょう。

protocol Reauthable {}
extension Reauthable where Self: UIViewController {
    @available(iOS 13.0, *)
    private func appleReauth(success: @escaping () -> Void) {
        let authorizationProvider = ASAuthorizationProvider()
        _ = authorizationProvider.authResult
            .takeUntil(self.rx.deallocated)
            .reauth()
            .subscribe(onNext: { _ in
                success()
            })
        authorizationProvider.show(vc: self)
    }
}

設計の見直し

認証部分が元々コンパクトに書けるようになっているので、認証処理を行う ViewController に直接処理を書く方法が1つ、適応箇所が複数ある場合は struct でまとめて宣言する方法が2つ目に考えられます。
実際の実装方法は、アプリ全体の実装方法に合わせて検討してください。

直接処理を記述する方法:

@available(iOS 13.0, *)
final class ViewController: UIViewController {
    private let bag = DisposeBag()
    override func viewDidLoad() {
        let authorizationProvider = ASAuthorizationProvider()
        authorizationProvider.authResult
            .takeUntil(self.rx.deallocated)
            .reauth()
            .subscribe(onNext: { _ in
                debugPrint("Sign in OK")
            })
            .disposed(by: bag)
        authorizationProvider.show(vc: self)
    }
}

処理をまとめて宣言する方法:

@available(iOS 13.0, *)
struct ReauthHelper {
    public var reauthSuccess: Observable<Void> {
        authorizationProvider.authResult
            .reauth()
            .map { _ in }
    }
    private let authorizationProvider = ASAuthorizationProvider()

    public func reauth(from: UIViewController) {
        authorizationProvider.show(vc: from)
    }
}

@available(iOS 13.0, *)
final class ViewController: UIViewController {
    private let bag = DisposeBag()
    override func viewDidLoad() {
        let reauthHelper = ReauthHelper()
        reauthHelper.reauthSuccess
            .subscribe(onNext: {
                debugPrint("Sign in OK")
            })
            .disposed(by: bag)
        reauthHelper.reauth(from: self)
    }
}

実装完了

以上の実装で以下のような再認証フローを実現できるようになります。アイデア、ご指摘等ございましたら、このホームページの連絡フォームより気軽に送っていただけると嬉しいです。
最後までお読みいただき、ありがとうございます。

final class viewController: UIViewController, Reauthable {
    func reauth() {
        appleReauth { [weak self] in
            self?.success()
        }
    }
}

Author

Next post

Authorization Status thinking in the stream by RxSwift

iOS ではカメラやマイクにアクセスする場合にはユーザに対して許可を貰う必要があります。
まず最初に「アクセスしても良いか」をアラートで訪ねます。そこで拒否されてしまった場合には有効にしてもらうようにユーザに提案し、設定画面に移動させてあげると親切です。今回は、この処理をストリーム思考な設計で実装する事例をご紹介します。

Read More →