This document is about: PUN 2
SWITCH TO

PUN Classic (v1)、PUN 2、Boltはメンテナンスモードとなっております。Unity2022についてはPUN 2でサポートいたしますが、新機能が追加されることはありません。お客様のPUNプロジェクトおよびBoltプロジェクトが停止することはなく、将来にわたってパフォーマンス性能が落ちることはありません。 今後の新しいプロジェクトについては、Photon FusionまたはQuantumへ切り替えていただくようよろしくお願いいたします。

カスタム認証

全てのアプリケーションは、デフォルトで匿名ユーザーの接続を許可していて、認証メカニズムはありません。Photonは、アプリケーションにカスタム認証を実装するオプションを提供しています。

Photonのカスタム認証は柔軟性が高く、有名なサードパーティーの認証プロバイダーから、完全に個別のソリューションまで対応しています。

  • Exit GamesがホスティングするFacebook認証プロバイダーは、このページをご覧ください
  • ユーザーが独自に構築・ホスティングしているカスタム認証プロバイダーについて、
    弊社はGitリポジトリで認証プロバイダーの実装例を提供しています。
    リポジトリのフォークや、プルリクエストは自由です。
    GitHubでソースを確認してください。

認証フロー

以下の図に、認証プロセスの一般的なフローを示します。

Photon Cloud: Custom Authentication Flow Diagram
カスタム認証フロー図
  1. クライアントは、使用する認証プロバイダーと必要な認証データの情報を、Connect()でPhoton Serverに渡します。
  2. Photon Serverは、要求した認証プロバイダーを取得し、以下のいずれかのステップに進みます。
    • 認証プロバイダー設定を検出した場合は、ステップ3に進みます。
    • 認証プロバイダー設定が検出されない場合は、アプリケーションの設定に応じて、クライアントは接続を許可/拒否されます。
  3. Photon Serverは、Connect()で渡された認証情報で、認証プロバイダーを呼び出します。
    • 認証プロバイダーがオンラインの場合、ステップ4に進みます。
    • 認証プロバイダーがオフラインの場合、該当の認証プロバイダーの設定に応じて、クライアントは接続を許可/拒否されます。
  4. 認証プロバイダーが認証情報を処理し、Photon Serverに結果を返します。
  5. 認証結果に応じて、クライアントは正常に認証/拒否されます。

実装

Photon CloudでFacebook認証を使用している場合、この部分はスキップしてください。

クライアントサイド

クライアントサイドでは、APIでカスタム認証を処理します。これは、関連するパラメーターと、対象のカスタム認証サービスを一度設定するだけです。セットアップが完了したら、接続とその後のエラー処理を行います。

C#

AuthenticationValues authValues = new AuthenticationValues();
authValues.AuthType = CustomAuthenticationType.Custom;
authValues.AddAuthParameter("user", userId);
authValues.AddAuthParameter("pass", pass);
authValues.UserId = userId; // this is required when you set UserId directly from client and not from web service
PhotonNetwork.AuthValues = authValues;
// connect

サーバーサイド

Webサーバーは認証リクエストを受信したら、クエリパラメーターのチェックとバリデーションを行うべきです。
例えば、認証資格情報をデータベースに存在する既存の情報と比較したりします。

受信したパラメーターが見つからないまたは無効な値だった場合、{ "ResultCode": 3, "Message": "Invalid parameters." }のような結果が返ります。

バリデーションが完了した後は、以下のような結果が返ります。

  • 成功:{ "ResultCode": 1, "UserId": <userId> }
  • 失敗:{ "ResultCode": 2, "Message": "Authentication failed. Wrong credentials." }

高度な機能

ユーザー認証以外にも、認証プロバイダーから追加情報を返すことができます。
これを行うには、クライアントと、認証の役割を担うWebサービス間に、何らかのプロトコルを確立する必要があります。

サーバーへのデータ送信

最も簡単でシンプルな方法は「オール・オア・ナッシング」方式で、クライアントに固定数の変数を返すか、何も返さないかを選択します。
一部のユースケースでは、より複雑なアプローチとして、クライアントがリクエストした内容に応じた「オンデマンド」で、データを返すWebサービスが必要になります。
ここでは、クライアントからWebサービスにデータを送信する方法を説明します。
データには、必須の認証資格情報に加えて、任意の追加パラメーターを含めます。
追加パラメーターは、サーバーサイドで取得可能なデータの一部をリクエストし、認証レスポンスに含めて返したい時などに、特に使用されます。
これにより、余分なAPIコールが抑えられ、ログインのワークフローが単純化されるため、非常に有用です。

認証に使用されるデフォルトのHTTPメソッドはGETになるため、パラメーターはクエリ文字列のキーバリューペアとして送信されます。 最終的なURLには、クライアントやダッシュボードから設定されたキーバリューペアを「結合」したものが含まれます。 両方の場所で同じキーが使用されている場合、ダッシュボードの値のみが送信されます。 ベストプラクティスとして、クライアントからは見えないセンシティブで固定のキーバリューペア(例えば、APIキー、APIバージョン)のみを、ダッシュボードで設定してください。 また、ダッシュボードのキーバリューは、クライアントを更新することなく、オンザフライで変更できます。

ごくまれなケースとして、認証で多くのデータを必要とすることがあります。しかしその一方、ほとんどのWebサーバーは、クエリ文字列の文字数や、URLの長さに制限があります。
そのためPhotonでは、クライアント(C# SDK)から明示的にAuthenticationValues.AuthPostDataの値を設定することで、HTTPメソッドをPOSTに変更できるようになっています。
値は、stringbyte[]Dictionary<string, object>型が有効です。
Dictionary<string, object>の場合、ペイロードはJSON文字列に変換され、HTTPリクエストのContent-Typeは「application/json」に設定されます。
C# SDKのAuthenticationValuesクラスは、サポートする型にそれぞれsetterメソッドが用意されています。

これは要件や制約になることがあるため、認証リクエストをWebサービスからのPOSTメソッドとして受信したいユーザーのために、POSTメソッドのオプションも提供されています。

つまり、認証パラメーターの送信は、クエリ文字列またはPOSTのデータ、もしくはその両方を自由に使用できます。
以下の表に、有効な組み合わせを示します。

AuthPostData AuthGetParameters HTTP method
null * GET
empty string * GET
string (not null, not empty) * POST
byte[] (not null, can be empty) * POST
Dictionary<string, object> (not null, can be empty) * POST (Content-Type="application/json")

クライアントへのデータ返送

Photon Serverは、クライアントとWebサービス間のプロキシになるので、どの変数がPhoton Serverで処理されるのかを覚えておく必要があります。

Photon Serverが受信する全てのHTTPレスポンスのように、Webサーバーは、ResultCodeと任意のMessageを含むJSONオブジェクトを返す必要があります。
Photon Serverが、認証時にWebサービスから受信可能なリストは、以下の通りです。

  • UserId:この値は、認証のパラメーターや、クライアント側からのリクエストとして使用できます。Photon Serverが受信した値は、必ずクライアントに転送されます。AuthenticationValues.UserIdが設定されていない場合は、ランダムに生成されたUserIdがクライアントに返送されます。これでクライアントのUserIdの値が上書きされた以降、その値は変更できなくなります。この値は、ResultCodeが1の場合のみ返されます。(例:{ "ResultCode": 1, "UserId": "SomeUniqueStringId" }
  • Nickname:この値は、認証のパラメーターや、クライアント側からのリクエストとして使用できます。Webサービスから返された値で、クライアントのNicknameの値は上書きされますが、それ以降もクライアント側で変更可能です。この値は、ResultCodeが1の場合のみ返されます。(例:{ "ResultCode": 1, "UserId": "SomeUniqueStringId", "Nickname": "SomeNiceDisplayName" }
  • AuthCookie:セキュアデータとも呼ばれる、Webサービスで返されるJSONオブジェクトです。暗号化されたトークンに埋め込まれるため、クライアント側からアクセスすることはできません。WebhookまたはWebRPCのHTTPリクエストで、後から送信することもできます。この値は、ResultCodeが1の場合のみ返されます。(例:{ "ResultCode": 1, "UserId": "SomeUniqueStringId", "AuthCookie": { "SecretKey": "SecretValue", "Check": true, "AnotherKey": 1000 } }
  • Data:クライアントに返したい全ての追加の値を含むJSONオブジェクトです。配列やオブジェクトの入れ子には対応していないことに注意してください。この値は、ResultCodeが0か1の場合のみ返されます。(例:{ "ResultCode": 0, "Data": { "S": "Vpqmazljnbr=", "A": [ 1, -5, 9 ] } }

ResultCodeのみが必須の戻り値で、他の変数は任意となります。
以下の表に、Webサーバーで返すことができる変数を示します。

ResultCode Description UserId Nickname AuthCookie Data
0 Authentication incomplete, only Data returned.*
1 Authentication successful. (optional) (optional) (optional) (optional)
2 Authentication failed. Wrong credentials.
3 Invalid parameters.

*:例えば、OAuth 2.0や2段階認証を実装するのに有用です。

クライアントからのデータ読み込み

レスポンスから返された値を取得する方法は、以下のコードスニペットの通りです。

The callback to retrieve custom optional Data returned during authentication with your custom server:

C#

void OnCustomAuthenticationResponse(Dictionary<string, object> data)
{
    // here you can access the returned data
}

データ型の変換

ここでは、Photon ServerとWebサービス間で通信するデータ型のみを説明します。
クライアントとPhoton Server間で通信するデータ型の詳細は、Photonのシリアライゼーションをご覧ください。

Photon Server -> Webサービス

C# / .NET (Photon supported types) JavaScript / JSON
byte number
short
int
long
double
bool bool
string string
byte[] (byte array length < short.MaxValue) string (Base64 encoded)
T[] (array of supported type T, length < short.MaxValue) array
Hashtable (of supported types, count < short.MaxValue, preferably Photon implementation) object
Dictionary (keys and values of supported types, count < short.MaxValue) object
null null

リクエストデータ(型を連結済み)のサンプル

Photon Serverからの送信

JSON

{
    "(Dictionary<String,Object>)Dictionary":{
        "(Int32)dk_int":"1",
        "(String)dk_str":"dv2",
        "(Boolean)dk_bool":"True"
    },
    "(Hashtable)Hashtable":{
        "(Byte)hk_byte":"255",
        "(Object[])hk_array":[
            "(Int32)0",
            "(String)xy",
            "(Boolean)False"
        ],
        "hk_null":"null"
    },
    "null":"null",
    "(String[])string[]":[
        "PUN",
        "TB",
        "RT",
        "Bolt",
        "Chat"
    ],
    "(Byte[])byte[]":[
        "255",
        "0"
    ],
    "(Int16[])short[]":[
        "-32768",
        "32767"
    ],
    "(Int32[])int[]":[
        "-2147483648",
        "2147483647"
    ],
    "(Int64[])long[]":[
        "-9223372036854775808",
        "9223372036854775807"
    ],
    "(Single[])float[]":[
        "-3.402823E+38",
        "3.402823E+38"
    ],
    "(Double[])double[]":[
        "-1.79769313486232E+308",
        "1.79769313486232E+308"
    ],
    "(Boolean[])bool[]":[
        "True",
        "False"
    ]
}

Webサービスでの読み込み

JSON

{
    "(object)Dictionary":{
        "dk_int":"(number)1",
        "dk_str":"(string)dv2",
        "dk_bool":"(boolean)true"
    },
    "(object)Hashtable":{
        "(number)hk_byte":"255",
        "(array)hk_array":[
            "(number)0",
            "(string)xy",
            "(boolean)false"
        ],
        "hk_null":null
    },
    "null":null,
    "(array)string[]":[
        "(string)PUN",
        "(string)TB",
        "(string)RT",
        "(string)Bolt",
        "(string)Chat"
    ],
    "byte[]":"(string)/wA=",
    "(array)short[]":[
        "(number)-32768",
        "(number)32767"
    ],
    "(array)int[]":[
        "(number)-2147483648",
        "(number)2147483647"
    ],
    "(array)long[]":[
        "(number)-9223372036854776000",
        "(number)9223372036854776000"
    ],
    "(array)float[]":[
        "(number)-3.40282347e+38",
        "(number)3.40282347e+38"
    ],
    "(array)double[]":[
        "(number)-1.7976931348623157e+308",
        "(number)1.7976931348623157e+308"
    ],
    "(array)bool[]":[
        "(boolean)true",
        "(boolean)false"
    ]
}

Webサービス -> Photon Server

JavaScript/JSONの型と、C#/.Netの型との対応表は、以下の通りです。

JavaScript / JSON C# / .Net
object Dictionary
array object[] (array of objects)
number (integral) long
number (floating) double
string string
boolean bool
null (not a type) null
undefined (when sent) null

レスポンスデータ(型を連結済み)のサンプル

Webサービスからの送信

JSON

{
    "(object)number": {
        "(number)MAX_VALUE": "1.7976931348623157e+308",
        "(number)MIN_VALUE": "5e-324"
    },
    "(object)object": {
        "(string)string": "xyz",
        "null": null,
        "(boolean)bool": "false",
        "(undefined)undefined": "undefined",
        "(number)float": "-3.14",
        "(number)integer": "123456"
    },
    "(array)array": [
        "(string)xyz",
        "(number)0",
        "(boolean)true",
        null,
        "(undefined)undefined"
    ]
}

Photon Serverでの読み込み

JSON

{
    "(Dictionary<String,Object>)number":{
        "(Double)MAX_VALUE":"1.79769313486232E+308",
        "(Double)MIN_VALUE":"4.94065645841247E-324"
    },
    "(Dictionary<String,Object>)object":{
        "(String)string":"xyz",
        "null":"null",
        "(Boolean)bool":"False",
        "(Double)float":"-3.14",
        "(Int64)integer":"123456"
    },
    "(Object[])array":[ 
        "(String)xyz",
        "(Int64)0",
        "(Boolean)True",
        "null",
        "null"
    ]
}

トラブルシューティング

カスタム認証に失敗すると、以下のコールバックがトリガーされます。

C#

void OnCustomAuthenticationFailed(string debugMessage)
{
   // The `debugMessage` could be what the authentication provider returned.
}

ダッシュボードで設定した認証URLがHTTPエラーを返す場合、Photon Serverはオーバーヘッドを避けるため、短時間だけ認証コールを一時停止します。
URLを設定またはテストする時は、この「バックオフ」の時間を考慮してください。

ベストプラクティス

  • 認証プロバイダーから返される結果には、判読可能なMessageを含めてください。特に認証が失敗したケースでは、デバッグの手間を大幅に省くことができます。
  • 固定のキーバリューペアは、クライアント側からではなくダッシュボードから設定してください。クエリ文字列のキーが重複するのを防ぐことができます。
  • セキュリティ上の理由から、認証パラメーターで平文のパスワードを送信しないでください。
  • ダッシュボードからクエリ文字列パラメーターを設定してください。リクエストのオリジンが確認できます。
  • パラメーターの設定には、AuthenticationValuesのメソッドを使用し、AuthGetParametersの値を直接変更しないでください。クエリ文字列が不正な形式になるのを防ぐことができます。

ユースケース例:古いクライアントバージョンをブロック

カスタム認証によって、古いバージョン(または不正なバージョン)を使用するクライアントの接続を拒否し、ユーザーにアップデートを促す特定のエラーを返すことができます。
そのためには、カスタム認証リクエストでバージョン情報を送信する必要があります。クエリ文字列パラメーターまたはPOSTデータ引数のどちらで送信するかは、ユーザー自身で決めることができます。
クエリ文字列パラメーターを使用する例は、以下の通りです。

C#

string version = PhotonNetwork.GameVersion;
// string version = Application.version; // if you use the version from Unity
PhotonNetwork.AuthValues = new AuthenticationValues();
PhotonNetwork.AuthValues.AuthType = CustomAuthenticationType.Custom;
PhotonNetwork.AuthValues.AddAuthParameter("version", version); // HTTP GET
PhotonNetwork.ConnectUsingSettings();

カスタム認証のURLがhttps://example.comなら、リクエストはhttps://example.com?version={version}で送信されます。
認証プロバイダーの実装で、バージョンの取得と比較を行ってください。
そのバージョンを許可するなら{ "ResultCode": 1 }を返します。
許可しないなら、ResultCodeで任意の値(1以外)と、できればメッセージを返してください。
(例:{ "ResultCode": 5, "Message": "Version not allowed." }

Back to top