69
39

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Sign in with Apple with Firebase Authentication

Last updated at Posted at 2019-07-21

※Sign in with Appleはまだbeta版の為、仕様等に変更が入る可能性があります

WWDC 2019でSign in with Appleの機能が発表されました。
この機能はApple IDを利用して様々なアプリやサービスにログインが可能となる物です。
但し、ログインが必要なアプリやサービスはアプリ単体で完結する物は少なく、基本的にはサーバー上のデータと連携する必要があるかと思います。
ここではFirebase Authenticationを利用してアプリとサーバーのユーザー情報を連携させてみようと思います。

事前準備

Apple DeveloperページのIdentifiersSign in with Appleを有効にします。

image.png

Xcode上でもプロジェクトのSigning & CapabilitiesSign in with Appleを追加します。

ログインボタンの設置

基本的にはOSが用意したボタンを用意するのが良いかと思いますが、カスタマイズしたい場合はHIGを確認してガイドラインに沿うか確認した方が良いでしょう。

import AuthenticationServices

let appleIdButton = ASAuthorizationAppleIDButton(authorizationButtonType: .signUp, authorizationButtonStyle: .whiteOutline)
appleIdButton.addTarget(self, action: #selector(handleTap(_:)), for: .touchUpInside)
view.addSubview(appleIdButton)

ログインボタンのイベントハンドリング

@objc
func handleTap(_ sender: ASAuthorizationAppleIDButton) {
    let request = ASAuthorizationAppleIDProvider().createRequest()
    request.requestedScopes = [.fullName, .email]
    let controller = ASAuthorizationController(authorizationRequests: [request])
    controller.delegate = self
    controller.presentationContextProvider = self
    controller.performRequests()
}

ログインのdelegateを実装

extension ViewController: ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding {
    func authorizationController(controller _: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
        switch authorization.credential {
        case let appleIdCredential as ASAuthorizationAppleIDCredential:
            // TODO: use appleIdCredential.user, email: appleIdCredential.email
        default:
            break
        }
    }

    func authorizationController(controller _: ASAuthorizationController, didCompleteWithError error: Error) {
        DispatchQueue.main.async { [weak self] in
           // TODO: Error handling
        }
    }

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

Firebase

環境はNode 8系を利用します。
Firebase toolsがインストールされている前提です。

Firebase Cloud functionsの作成

firebase init functions でCloud functionsを初期化します。
JavaScriptかTypeScriptを聞かれるのでモダンなTyprScriptを選択しておきましょう。
functions/src/index.ts などが生成されているかと思いますので下記の様に修正します。

import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';

admin.initializeApp();

type AuthRequest = {
    email: string | null,
    userIdentifier: string
};

function isAuthRequest(data: any): data is AuthRequest {
    return typeof data.userIdentifier === 'string';
}

export const signin_with_apple = functions.https.onCall(async (data, context)  => {
    if (!isAuthRequest(data)) {
        throw new Error('invalid request');
    }
    // この書き方なんとかしたい
    // 追記: 良い書き方をコメントで教えてもらったので気になる方はコメントを参照ください
    const request = data as any as AuthRequest;
    const uid = request.userIdentifier;
    const email = request.email;

    const customToken = await admin.auth().createCustomToken(uid);
    if (!email) {
        return {
            custom_token: customToken
        };
    }
    await admin.auth().updateUser(uid, {
        email: email
    });
    return {
        custom_token: customToken
    };
});

開発が完了したら firebase deploy --only functions:signin_with_apple などでデプロイをしておきます。

アプリからCloud functionsの呼び出し

import FirebaseFunctions

Functions.functions().httpsCallable("signin_with_apple").call(["email": appleIdCredential.email, "userIdentifier": appleIdCredential.user]) { result, error in
    if let error = error {
        // TODO: Error handling
        return
    }

    if let customToken = (result?.data as? [String: Any])?["custom_token"] as? String {
        // Success!
        return
    } else {
        // TODO: Error handling
    }
}

上記で受け取ったcustomTokenFirebaseAuthに渡します。

レスポンスを連携

import FirebaseAuth

Auth.auth().signIn(withCustomToken: customToken) { [weak self] _, error in
    if error != nil {
        // TODO: Error handling
        return
    }
    // Success!
}

Apple IDのバリデーション

Apple IDは設定アプリやサイトから簡単に無効化出来るので、必要に応じてApple IDのハンドリングしましょう。

let provider = ASAuthorizationAppleIDProvider()
provider.getCredentialState(forUserID: appleId) { credentialState, _ in
    DispatchQueue.main.async {
        switch credentialState {
        case .authorized:
            // nothing todo
            break
        case .notFound, .revoked:
            // TODO: error handling
            break
        @unknown default:
            fatalError("unknown credentialState")
        }
    }
}

まとめ

Apple IDでのログイン機能をFirebase Authと連携する事が出来ました。
Appleとしては既存アプリが外部ログイン機能を提供している場合、Apple IDでのログイン機能が必要だと言っているので、なるべく早く対応しておきたいですね。

69
39
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
69
39

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?