GS2 Blog

Game Server Services(https://gs2.io/) の最新情報をお届けします

OpenID Connect による引き継ぎ処理の実装がより便利になりました

はじめに

GS2-Account はアカウント管理をするためのマイクロサービスで、匿名アカウントの発行や認証機能を持ちます。
匿名アカウントの引き継ぎ情報の管理機能も用意されています。

OpenID Connect とは Twitter が提唱した OAuth を標準化した技術で、認可処理にかかわるフローを標準化しています。
現在では Google, Apple, Facebook, Yahoo, LINE などさまざまな会社の認証基盤が OpenID Connect に準拠した認可の仕組みを提供しています。

機能追加の背景

GS2-Account が持つ引き継ぎ情報の管理機能は OpenID Connect をはじめとした認証基盤などのもつユーザーIDと、ユーザーしか知り得ないパスワードの組み合わせを匿名アカウントに紐づけておくことで
将来的にスマートフォンを機種変更した時などに、OpenID Connect をはじめとした認証基盤の認証結果を使用して匿名アカウントの認証情報を復元できるようにする仕組みを提供しています。

1つの匿名アカウントに複数の認証基盤のアカウント情報を関連づけられるようになっており、その識別にはスロット番号を使用します。
スロット番号と認証基盤の関連づけはゲーム側で任意に設定し、運用することを想定しており、ユーザー識別子とパスワードとなる文字列の組み合わせさえあれば OpenID Connect であろうが、メールアドレスとパスワードだろうがなんでもいいというスタンスでした。

一方で、引き継ぎ処理を実装するにあたって、多くの場合 Google のアカウントと、Apple のアカウントで引き継ぎを行えるようにすることが一般的で、この一般的なケースを実装しようと思った時に認可フローは GS2 のサポート外となっており、実装の手間が発生するのが課題でした。

追加された機能の詳細

OpenID Connect の認証基盤が発行したIDトークンを使用して引き継ぎ情報の登録・引き継ぎの実行が直接行えるようになりました。
GS2-Account の新機能としては、以下が追加されました。

  • OpenID Connect の認可ページURLの生成
  • OpenID Connect のコールバック受付処理
  • OpenID Connect のIDトークンの発行処理
  • ユーザー識別子 + パスワード の代わりに OpenID Connect の IDトークンを使用した引き継ぎ情報の登録・引き継ぎ実行処理

マスターデータ

これまで GS2-Account にはマスターデータは存在しませんでしたが、OpenID Connect の認証サービスとスロット番号を関連づけるためのマスターデータを登録できるようになりました。

マスターデータはスロット番号ごとに定義し、OpenID Connect Discovery のスペックURLを指定することで認証基盤の種類を設定します。

Google であれば

https://accounts.google.com/.well-known/openid-configuration

Apple であれば

https://appleid.apple.com/.well-known/openid-configuration

Microsoft であれば

https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration

Facebook であれば

https://www.facebook.com/.well-known/openid-configuration

となります。

このほかに認証基盤で発行されたクライアントIDとクライアントシークレットを設定します。

コールバックハンドリング

認証基盤で認証処理が終わったあとのコールバックハンドリングと、認可コードからIDトークンを発行するまでの処理も提供しています。
そのほか代表的なサービスとして Firebase Authenticator などもありますが、これらのサービスを通して受け取ったIDトークンを使って引き継ぎ情報の処理も可能ですので、Firebase Authenticator を使わなくてもIDトークンがとれるんだ と思っていただければ大丈夫です。

ここからしばらくは IDトークンを受け取るまでの処理の流れのため、GS2-Account で IDトークン取得を行わない場合は読み飛ばしてOKです。

認証基盤には以下のコールバックURLを設定してください。
この値はマネージメントコンソールでも確認が可能です。

https://account.{region}.gen2.gs2io.com/{ownerId}/{namespaceName}/type/{type}/callback

プレースホルダを埋めた状態のサンプル値は以下です

https://account.ap-northeast-1.gen2.gs2io.com/aAbBcCdD-project/namespace-0001/type/0/callback

認証処理

(await gs2.Account.Namespace(
    namespaceName
).Me(
    gameSession
).GetAuthorizationUrlAsync(
    type
)).AuthorizationUrl

これで認可ページのURLを取得できます。WebView を使用してこのURLを開いてログインします。

ログインが完了すると、あらかじめ認証基盤に登録したコールバックURLに遷移し、コールバックURLは受け取った認可コードを使用してIDトークンを発行します。
IDトークンの発行が完了すると、以下のURLに遷移します。

https://account.{region}.gen2.gs2io.com/{ownerId}/{namespaceName}/type/{type}/done?id_token={idToken}

WebView のページ遷移イベントをハンドリングして、/done に遷移したらURLの ?id_token= に続く IDトークンを取り出せば、認証プロセスは終了です。

以下に GitHub - gree/unity-webview を使用した実装例を示します。

    public static async UniTask<string> OpenAuthentication(
        WebViewObject webView,
        Gs2Domain gs2,
        string namespaceName,
        IGameSession gameSession,
        int type
    ) {
        string idToken = null;
        webView.Init(
            separated: true,
            ld: url =>
            {
                if (new Uri(url).LocalPath.EndsWith("/done")) {
                    var codeField = new Uri(url).Query.Replace("?", "").Split("&").Select(v => new KeyValuePair<string,string>(v[..v.IndexOf("=", StringComparison.Ordinal)], v[(v.IndexOf("=", StringComparison.Ordinal)+1)..])).FirstOrDefault(v => v.Key == "id_token");
                    idToken = Uri.UnescapeDataString(codeField.Value);
                    webView.SetVisibility(false);
                }
            }
        );
        webView.LoadURL(
            (await gs2.Account.Namespace(
                namespaceName
            ).Me(
                gameSession
            ).GetAuthorizationUrlAsync(
                type
            )).AuthorizationUrl
        );
        webView.SetInteractionEnabled(true);
        webView.SetVisibility(true);

        await UniTask.WaitWhile(() => idToken == null);

        return idToken;
    }

引き継ぎ情報の登録

    var result = await gs2.Account.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).TakeOver(
        type: 0
    ).AddTakeOverSettingOpenIdConnectAsync(
        idToken: "id-token"
    );

引き継ぎの実行

    var result = await gs2.Account.Namespace(
        namespaceName: "namespace-0001"
    ).DoTakeOverOpenIdConnectAsync(
        type: 0,
        idToken: "id-token"
    );

    var item = await result.ModelAsync();
    var userId = item.UserId;
    var password = item.Password;
(C) Game Server Services, Inc.