dasukoの技術ブログ

現役エンジニアのブログです。

【Unity】Firebase Authenticationを使って認証を実装してみた

はじめに

まずはUnityパッケージをダウンロードしてUnityプロジェクトにインポートします。

次にFirebaseのコンソールにログインして、アプリ情報を登録してgoogle-services.jsonをダウンロードして、 Assets配下に任意の位置に配置します。

Authの有効化

FirebaseコンソールからAuthenticationを選択します。

f:id:dasuko:20200603015003p:plain

次にログイン方法を設定します。 f:id:dasuko:20200603015042p:plain

今回はメールのみ有効にしてみます。 f:id:dasuko:20200603015106p:plain

実装

公式のマニュアルを基に以下のようにAuthManagerを実装してみました。

using UnityEngine;

public class AuthManager
{
    public Firebase.Auth.FirebaseUser User
    {
        get;
        private set;
    }

    public Firebase.Auth.FirebaseAuth Auth
    {
        get;
        private set;
    }

    public System.Action<Firebase.Auth.FirebaseUser> onStateChanged = (Firebase.Auth.FirebaseUser user) => {};

    private static readonly object lockObject = new object();
    private static AuthManager instance;
    public static AuthManager Instance
    {
        get
        {
            if (instance == null)
            {
                lock (lockObject)
                {
                    if (instance == null)
                    {
                        instance = new AuthManager();
                    }
                }
            }

            return instance;
        }
    }

    private AuthManager()
    {
        Initialize();
    }

    ~AuthManager()
    {
        if (Auth != null)
        {
            Auth.StateChanged -= OnAuthStateChanged;
            Auth.Dispose();
            Auth = null;
        }
    }

    void Initialize()
    {
        Firebase.FirebaseApp.CheckAndFixDependenciesAsync().ContinueWith(task => {
            lock (lockObject)
            {
                Auth = Firebase.Auth.FirebaseAuth.DefaultInstance;
                Auth.StateChanged += OnAuthStateChanged;
            }

            OnAuthStateChanged(this, null);
        });
    }

    public void CreateUser(string email, string password, System.Action<Firebase.Auth.FirebaseUser> onComplete = null)
    {
        Auth.CreateUserWithEmailAndPasswordAsync(email, password).ContinueWith(task => {
            if (task.IsCanceled || task.IsFaulted)
            {
                Debug.LogError("CreateUserWithEmailAndPasswordAsync failed." + task.Exception);
                if (onComplete != null)
                {
                    onComplete.Invoke(null);
                }
                return;
            }

            lock (lockObject)
            {
                User = task.Result;
            }

            if (onComplete != null)
            {
                onComplete.Invoke(User);
            }

            Debug.LogFormat("Firebase user created successfully. DisplayName={0}, UserId={1}", User.DisplayName, User.UserId);
        });
    }

    public void SignIn(string email, string password, System.Action onComplete = null)
    {
        Auth.SignInWithEmailAndPasswordAsync(email, password).ContinueWith(task => {
            if (task.IsCanceled || task.IsFaulted)
            {
                Debug.LogError("CreateUserWithEmailAndPasswordAsync failed.");
                return;
            }

            if (onComplete != null)
            {
                onComplete.Invoke();
            }


            Debug.LogFormat("Firebase user signed in successfully. DisplayName={0}, UserId={1}", User.DisplayName, User.UserId);
        });
    }

    void OnAuthStateChanged(object sender, System.EventArgs eventArgs)
    {
        if (User == Auth.CurrentUser || Auth.CurrentUser == null)
        {
            if (User != null)
            {
                Debug.Log("Signed out " + User.UserId);
            }
        }

        lock (lockObject)
        {
            User = Auth.CurrentUser;
        }

        if (onStateChanged != null)
        {
            onStateChanged.Invoke(User);
        }
    }
}

まずはユーザ作成の動作をテストするためにClientを用意します。

using System.Collections;
using UnityEngine;

public class Client : MonoBehaviour
{
    IEnumerator Start()
    {
        AuthManager.Instance.onStateChanged += (Firebase.Auth.FirebaseUser user) =>
        {
            Debug.Log("userId=" + (user != null ? user.UserId : string.Empty));
        };

        yield return new WaitUntil(() => AuthManager.Instance.Auth != null);

        AuthManager.Instance.CreateUser("hogehoge@example.com", "12345abcdef");
    }
}

yield return new WaitUntil(() => AuthManager.Instance.Auth != null);では、AuthManagerの初期化が完了し、Authプロパティがセットされるまで待機しています。

結果は以下のようにユーザ作成に成功し、UserIdがログに出力されました。 f:id:dasuko:20200603020345p:plain

Firebaseコンソール上からもユーザが作成されたことを確認することができます。 f:id:dasuko:20200603020237p:plain

続いてユーザログインをテストするためのClientを用意してみます。

アプリが再起動した時でもFirebaseが認証情報を保持してくれているので、

基本的にはSignOutした後や別端末で認証したい場合などに使われるかと思います。

using System.Collections;
using UnityEngine;

public class Client : MonoBehaviour
{
    IEnumerator Start()
    {
        AuthManager.Instance.onStateChanged += (Firebase.Auth.FirebaseUser user) =>
        {
            Debug.Log("userId=" + (user != null ? user.UserId : string.Empty));
        };

        yield return new WaitUntil(() => AuthManager.Instance.Auth != null);

        AuthManager.Instance.SignIn("hogehoge@example.com", "12345abcdef");
    }
}

こちらも正しく動作しました。

Taskなので排他制御をしていますが、Firebase.Extensions namespaceにはTaskExtensionというTaskの拡張クラスがあり、

そこにはContinueWithOnMainThreadというメソッドが定義されています。

ContinueWithのブロック内は通常バックグラウンドスレッドになりますが、ContinueWIthOnMainThreadを使えば

メインスレッドで実行してくれます。

そのため、UnityのAPIも気にせず使うことができます。

参考

Unity で Firebase Authentication を使ってみる