※Sign in with Appleはまだbeta版の為、仕様等に変更が入る可能性があります
WWDC 2019でSign in with Appleの機能が発表されました。
この機能はApple IDを利用して様々なアプリやサービスにログインが可能となる物です。
但し、ログインが必要なアプリやサービスはアプリ単体で完結する物は少なく、基本的にはサーバー上のデータと連携する必要があるかと思います。
ここではFirebase Authenticationを利用してアプリとサーバーのユーザー情報を連携させてみようと思います。
事前準備
Apple DeveloperページのIdentifiers
でSign in with Apple
を有効にします。
Xcode上でもプロジェクトのSigning & Capabilities
に Sign 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
}
}
上記で受け取ったcustomToken
をFirebaseAuth
に渡します。
レスポンスを連携
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でのログイン機能が必要だと言っているので、なるべく早く対応しておきたいですね。