Unity

【Unity】iOSでSign in with Appleの実装を解説

UnityでiOS向けのSing in with Appleを実装し、FirebaseAuthと連携していく方法を解説します。
とても簡単です。

前提として、「Firebaseにアプリのプロジェクトを作成」し、かつ、「iOSの開発ライセンスも取得済み」とします。

 

Android向けのSign in with Appleはコチラの記事を参照してみてください。

【Unity】AndroidでSign in with Appleの実装を解説UnityでAndroid用にSign in with Appleを実装し、Firebase Authと連携していく手順を解説します。 ...

 

XCodeでSign in With Appleの有効化

UnityでiOSビルドをし、iOSプロジェクトをXCodeで開きます。

TARGETSからアプリを選択して、Signing & Capabilitiesタブを選択して、+ Capabilityボタンを押す。

 

リストからSign in with Appleを選択。

 

これでプロジェクトにSign in with Appleが有効になります。

 

FirebaseでSign In With Appleの有効化

Firebaseで該当プロジェクトを開き、Authenticationメニューで、Sign-in methodタブを選び、新しいプロバイダを追加ボタンを押し、Apple認証を追加します。

iOSの場合、有効化するだけで、各種オプションの入力は不要です。

 

実装

FirebaseAuthパッケージの導入

以下のリンクから最新版のFirebase Unity SDKをダウンロードします。
今回はバージョン11.2.0を導入しています。

既に他のFirebaseパッケージを導入済の場合は同じバージョンのFirebaseAuthパッケージを入れるか、最新のパッケージを全て入れ直してください。
そうしないとエラーで動きません。

ダウンロードしたら解凍して、FirebaseAuth.unitypackage をimportします。

 

Sign in with Appleパッケージの導入

次に非公式Sign in with Appleパッケージをimportします。

 

C#の実装

後は実装するだけです。
詳細はソースのコメントを参照してください。

using UnityEngine;
using Firebase.Auth;
using Firebase.Extensions;
using System.Runtime.InteropServices;
using System;
using System.Collections.Generic;
using AppleAuth.Interfaces;
using AppleAuth;
using AppleAuth.Native;
using AppleAuth.Enums;
using System.Threading.Tasks;
using System.Security.Cryptography;
using System.Text;


public class TestController : MonoBehaviour
{
    private FirebaseAuth auth;
    Firebase.DependencyStatus dependencyStatus = Firebase.DependencyStatus.UnavailableOther;
    private AppleAuthManager appleAuthManager;

    private void Start()
    {
        // FirebaseAuthの初期化
        Firebase.FirebaseApp.CheckAndFixDependenciesAsync().ContinueWithOnMainThread(task =>
        {
            dependencyStatus = task.Result;
            if (dependencyStatus == Firebase.DependencyStatus.Available)
            {
                auth = FirebaseAuth.DefaultInstance;
            }
            else
            {
                Debug.Log($"error: {dependencyStatus}");
            }
        });
    }

    private void Update()
    {
        // これやらないとSign In後の処理が進まない
        if (this.appleAuthManager != null)
        {
            this.appleAuthManager.Update();
        }
    }


    public void SignInWithAppleForiOS()
    {
        var deserializer = new PayloadDeserializer();
        this.appleAuthManager = new AppleAuthManager(deserializer);

        // 暗号化した文字列(nonce)を渡して認証し、認証結果に暗号化前の文字列を渡して改ざんチェックをする
        var rawNonce = GenerateRandomString(32);
        var nonce = GenerateSHA256NonceFromRawNonce(rawNonce);
        var loginArgs = new AppleAuthLoginArgs(LoginOptions.IncludeEmail | LoginOptions.IncludeFullName, nonce);
        appleAuthManager.LoginWithAppleId(
            loginArgs,
            credential =>
            {
                var appleIdCredential = credential as IAppleIDCredential;
                if (appleIdCredential != null)
                {
                    // Sign In成功!

                    var identityToken = Encoding.UTF8.GetString(appleIdCredential.IdentityToken);
                    var authorizationCode = Encoding.UTF8.GetString(appleIdCredential.AuthorizationCode);
                    Credential credencial = OAuthProvider.GetCredential("apple.com", identityToken, rawNonce, authorizationCode);
                    var signInCompleted = new TaskCompletionSource<FirebaseUser>();

                    // AppleアカウントをFirebaseAuthに紐付ける
                    auth.SignInWithCredentialAsync(credencial).ContinueWith(task =>
                    {
                        if (task.IsCanceled)
                        {
                            Debug.Log($"canceled");
                            signInCompleted.SetCanceled();
                        }
                        else if (task.IsFaulted)
                        {
                            Debug.Log($"fault {task.Exception}");
                            signInCompleted.SetException(task.Exception);
                        }
                        else
                        {
                            Debug.Log($"complete!");
                            signInCompleted.SetResult(task.Result);
                        }
                    });
                } else
                {
                    Debug.Log($"no credential!");
                }
            },
            error =>
            {
                Debug.Log($"sign in error");
            });
    }



    private static string GenerateRandomString(int length)
    {
        if (length <= 0)
        {
            throw new Exception("Expected nonce to have positive length");
        }

        const string charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._";
        var cryptographicallySecureRandomNumberGenerator = new RNGCryptoServiceProvider();
        var result = string.Empty;
        var remainingLength = length;

        var randomNumberHolder = new byte;
        while (remainingLength > 0)
        {
            var randomNumbers = new List<int>(16);
            for (var randomNumberCount = 0; randomNumberCount < 16; randomNumberCount++)
            {
                cryptographicallySecureRandomNumberGenerator.GetBytes(randomNumberHolder);
                randomNumbers.Add(randomNumberHolder[0]);
            }

            for (var randomNumberIndex = 0; randomNumberIndex < randomNumbers.Count; randomNumberIndex++)
            {
                if (remainingLength == 0)
                {
                    break;
                }

                var randomNumber = randomNumbers[randomNumberIndex];
                if (randomNumber < charset.Length)
                {
                    result += charset[randomNumber];
                    remainingLength--;
                }
            }
        }

        return result;
    }

    private static string GenerateSHA256NonceFromRawNonce(string rawNonce)
    {
        var sha = new SHA256Managed();
        var utf8RawNonce = Encoding.UTF8.GetBytes(rawNonce);
        var hash = sha.ComputeHash(utf8RawNonce);

        var result = string.Empty;
        for (var i = 0; i < hash.Length; i++)
        {
            result += hash[i].ToString("x2");
        }
        return result;
    }
}

 

Sign in with Appleが成功し、FirebaseAuthとの連携が成功すると、FirebaseにAppleと連携したアカウントが作成されます。