🦄

Twitter認証(OAuth) - UnityでSDKなしでFirebaseを使う 

2021/06/23に公開2

概要

UnityのプロジェクトにTwitterのアカウントでの認証機能を導入する方法を解説します。
TwitterのOAuthを利用して認証を行うため、Unityから直接APIは叩かずWebViewを使って認可を取得するところがポイントです。

この投稿はUnityでSDKなしでFirebaseを使うの一部です。

途中で出てくるソースコードの全体は以下にあります。
Unity側:
https://github.com/satouso0401/firebase-unity-not-use-sdk/tree/main/Assets/Scenes/Authentication

Firebase側:
https://github.com/satouso0401/firebase-unity-not-use-sdk-firebase

Twitterのデベロッパー登録とアプリケーションの作成

詳細な手順は省略しますが、Twitter Developer Platformにサインインします。
https://developer.twitter.com/

サインインができたら次にプロジェクトとアプリケーションの設定を行います。
以下のdashboardを開いて、Create Projectからプロジェクトとアプリケーションを作成します。
https://developer.twitter.com/en/portal/dashboard

以下の用にサイドメニューにプロジェクトとアプリケーションが表示されればアプリケーションの作成は完了です。

Firebase AuthenticationとTwitter Appの設定

Twitter Appの Key and Tokens タブの API Key and Secret の Regenerate ボタンでトークンを作成します。
作成した API KeyAPI Secret Key はこの後のFirebaseg側の設定で使用するので開いたままにしておきます。

次にFirebase Authenticationの Sign-in method タブでTwitter認証を有効にします。


APIキーとAPIシークレットには先ほどTwitterで生成した API KeyAPI Secret Key を設定します。
コールバックURL はこの後Twitterの設定に使用するので控えた後に保存します。

Developer Portalに戻って、Appの Settings タブの 3-legged OAuth を有効にします。

有効にすると各設定項目が入力できるようになるので、必要項目を入力します。
Callback URLs には先ほどFierbaseで表示された コールバックURL を設定します。
入力が終わったら保存します。

OAuth用のWebページ作成

ユーザーから認可を得るためのページを作成します。
ローカルのFirebase開発環境のpublicフォルダの配下にhtmlファイルを作成します。
以下のサンプルコードのいくつかのポイントを説明しますが、その他の設定等については参考にリンクを貼ったリポジトリを参照してください。

  1. firebasejsのバージョン
    https://www.gstatic.com/firebasejs/~ に指定するバージョンはFirebaseの プロジェクトの設定のマイアプリに記載されているスクリプトのバージョンと合わせるようにしてください。

  2. firebaseConfigの設定
    各設定内容についてはfirebasejsと同様マイアプリに記載されています。

  3. signInSuccessWithAuthResult
    認証結果のトークンをUnity側に渡すためにUnity.callを呼んでいます。

public/auth/auth.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Sample FirebaseUI App</title>
    <script src="https://www.gstatic.com/firebasejs/8.6.7/firebase-app.js"></script>
    <script src="https://www.gstatic.com/firebasejs/8.6.7/firebase-auth.js"></script>
    <script src="https://www.gstatic.com/firebasejs/ui/4.8.0/firebase-ui-auth.js"></script>
    <link type="text/css" rel="stylesheet" href="https://www.gstatic.com/firebasejs/ui/4.8.0/firebase-ui-auth.css"/>
    <script>
        // https://github.com/gree/unity-webview/issues/681
        if (window && window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.unityControl) {
            window.Unity = {
                call: function (msg) {
                    window.webkit.messageHandlers.unityControl.postMessage(msg);
                }
            }
        } else {
            window.Unity = {
                call: function (msg) {
                    window.location = 'unity:' + msg;
                }
            }
        }
    </script>
    <script type="text/javascript">
        var firebaseConfig = {
            apiKey: "****",
            authDomain: "****",
            projectId: "****",
            storageBucket: "****",
            messagingSenderId: "****",
            appId: "****",
            measurementId: "****"
        };
        // Initialize Firebase
        firebase.initializeApp(firebaseConfig);
        // FirebaseUI config.
        var uiConfig = {
            callbacks: {
                signInSuccessWithAuthResult: function (authResult, redirectUrl) {
                    console.log(authResult);
                    Unity.call(JSON.stringify(authResult));
                    return false;
                },
            },
            signInSuccessUrl: 'https://localhost',
            signInOptions: [
                firebase.auth.GoogleAuthProvider.PROVIDER_ID,
                firebase.auth.FacebookAuthProvider.PROVIDER_ID,
                firebase.auth.TwitterAuthProvider.PROVIDER_ID,
                firebase.auth.GithubAuthProvider.PROVIDER_ID,
                firebase.auth.EmailAuthProvider.PROVIDER_ID,
                firebase.auth.PhoneAuthProvider.PROVIDER_ID,
                firebaseui.auth.AnonymousAuthProvider.PROVIDER_ID
            ],
            tosUrl: '<your-tos-url>',
            privacyPolicyUrl: function () {
                window.location.assign('<your-privacy-policy-url>');
            }
        };

        // Initialize the FirebaseUI Widget using Firebase.
        var ui = new firebaseui.auth.AuthUI(firebase.auth());
        // The start method will wait until the DOM is loaded.
        ui.start('#firebaseui-auth-container', uiConfig);

    </script>
</head>
<body>
<h1>Welcome to My Awesome App</h1>
<div id="firebaseui-auth-container"></div>
</body>
</html>

htmlが作成できたら firebase deploy --only hosting コマンドの実行または、GitHub Actionの設定ができている場合はmainブランチを更新することでデプロイされます。

デプロイ後にFirebaseのHostingのダッシュボードにドメインが表示されるので、
ドメイン+ローカルで作ったpublic配下のパスのurlでアクセスできます。

例: https://fb-vanilla-sample.firebaseapp.com/auth/auth.html

参考:
https://github.com/firebase/firebaseui-web

認可取得用のUnityのWebViewの実装

今回は gree/unity-webview を使用してWebViewを扱うので、Unityプロジェクトのmanifest.jsonに以下の依存を追加します。

"net.gree.unity-webview": "https://github.com/gree/unity-webview.git?path=/dist/package"

WebViewの表示とOAuthの結果受け取り

以下のようなUnityからOAuthのフローを行うためのWebViewを表示する部分を実装します。

AuthWebViewScript.cs
private string url = "OAuth用のWebページのURL";
WebViewObject webViewObject;

void Start()
{
    webViewObject =
	(new GameObject("WebViewObject")).AddComponent<WebViewObject>();
    webViewObject.Init((msg) =>
    {
            // WebViewからのOAuthの結果のメッセージをmsgで受け取れるのでJsonからオブジェクトに変換
	// サインインを実装しているクラスから使えるように設定する
	TwitterScript.oAuthResult = TwitterApi.OAuthResultObject.FromJson(msg);
	GameObject.Find("Canvas/SignInButton").GetComponent<Button>().interactable = true;
	webViewObject.SetVisibility(false);
    });

    webViewObject.LoadURL(url);
    webViewObject.SetMargins(50, 100, 50, 50);
    webViewObject.SetVisibility(true);
}

UnityからのOAuthを使ったサインイン

AccessTokenとAccessTokenSecretを使って、Firebase Authenticationにサインインする部分の実装です。

TwitterScript.cs
private string _apiKey = "APIキー";
// WebViewからの送られてきたOAuthの結果が入っているオブジェクト
public static TwitterApi.OAuthResultObject oAuthResult;
public void SignIn()
{
    WebClient wc = new WebClient();
    wc.Headers[HttpRequestHeader.ContentType] = "application/json";
    var url = $"https://identitytoolkit.googleapis.com/v1/accounts:signInWithIdp?key={_apiKey}";
    var requestBody = FirebaseApi.SignInWithIdpRequest.SignInWithIdpTwitterRequest(
	oAuthResult.credential.oauthAccessToken,
	oAuthResult.credential.oauthTokenSecret
    ).ToJson();
    var response = wc.UploadString(new Uri(url), requestBody);
    var authResult = FirebaseApi.SignInWithIdpResponse.FromJson(response);
    Debug.Log($"idToken: {authResult.idToken}");
    Debug.Log($"refreshToken: {authResult.refreshToken}");
}

レスポンスに返ってくるidTokenとrefreshTokenをローカルストレージなどに保存しておくことで、パスワードなどを扱わずに安全に認証することができます。

Discussion

raisackraisack

こんにちは。コメント失礼いたします。
3月よりUnityを触り始め、TwitterApiを利用してデータを取得するアプリを作っています。
OAuth認証の段階で何日も時間を費やしており、、そこでsatousoさんの記事を見つけて、どの記事よりも詳しく書かれていたのでもうすぐ山を越えられると思ってわくわくしています!

しかし、UIをどう実装すればよいかで戸惑っております。。。
TwitterScript.cs のSighInメソッドをUnityで作成したButtonにアタッチさせればいいとは思っているのですが、いざ実行してみると「Object reference not set to an instance of an object」というエラーが出てきてしまいます。
「あ、なるほど、、Githubで作成したHTMLをimageとかに反映させればいいのね、」と解釈したのですが、どのように表示させればいいのか分からず、イメージも付けられず、、、

どのようにしたら「WebViewの表示とOAuthの結果受け取り」のような画面を表示させることができるのでしょうか?
ご教示いただけますと幸いです。
よろしくお願いいたします。

satousosatouso

誤認識の通りTwitterScript.csはButtonにアタッチさせれば良いです。WebViewを表示させている
WebViewについてはシーン内に適当な空のオブジェクトを作ってそのオブジェクトにAuthWebViewScript.csをアタッチさせれば動くかと思います。
(AuthWebViewScript.csのStart()内でWebViewを表示させる用のオブジェクトを作ってそこにWebViewを表示させるのでImageとかは自分で作らなくて良いです。)

Firebase側の設定ができていたら試せるサンプル用のリポジトリも用意してあるので、もし何かわからないことがあればこちらも見てみてください。
https://github.com/satouso0401/firebase-unity-not-use-sdk

細かな説明がなくて分かりにくいところもあるかと思うので、うまく行かない所があればお気軽にお声がけください😉