This document is about: VOICE 2
SWITCH TO

カスタム認証

Photonのカスタム認証は、既存の認証プロバイダーのいずれも需要に適合しない場合、ほかのサービスを統合してユーザー(独自のユーザーバックエンドを含む)を特定したい場合に使用します。

つまり、カスタム認証プロバイダーは、自分で構築して管理するWebサービスであるということです。Photonサーバーがセットアップされ、構成したURLに認証値を受け渡し少しの間応答を待ちます。結果に応じて、クライアントがPhotonへの接続が許可されたり拒否されたりします。もっとも良いケースでは、バックエンドによってユーザーIDが定義されオプションで追加情報が安全な方法で受け渡されてサーバープラグインで使用されるようになります。

サンプル実装

Gitリポジトリで認証プロバイダーのサンプル実装を提供しています。リポジトリのフォークしおよびリクエストのプルはお気軽にお願いします。

ソースはGitHubでご確認いただけます。

認証フロー

以下のステップは、認証プロセスの一般的なフローです。

Photon Cloud: Custom Authentication Flow Diagram
カスタム認証フロー図表
  1. クライアントの接続時に、クライアントがどの認証プロバイダーを使用するかに関する情報および必要な認証データをPhotonサーバーに受け渡します。
  2. Photonサーバーはご自身のアプリケーションに望ましい認証プロバイダーを取り、以下のいずれかのステップを行います。
    • 認証プロバイダー設定が見つかった場合 -> 認証のステップ3へ進みます。
    • 認証プロバイダー設定が見つからなかった場合 -> アプリケーションの設定によって、クライアントの接続が許可または拒否されます。
  3. Photonサーバーが、Connect()で受け渡された認証情報を使用して認証プロバイダーを呼び出します。
    • 認証プロバイダーがオンラインの場合 -> 認証のステップ4へ進みます。
    • 認証プロバイダーがオフラインの場合 -> 対応するプロバイダーの設定によって、クライアントの接続が許可または拒否されます。
  4. 認証プロバイダーが認証情報を処理し、結果をPhtonServerへ返します。
  5. 認証結果によって、クライアン都が無事に認証されるか、拒否されます。

実装

Photon CloudでFacebook認証を使用している場合、このパートはスキップできます。

クライアントサイド

クライアントサイドでは、APIがカスタム認証に対応します。関連するパラメータと目的となるカスタム認証サービスを1度設定するだけです。
ひとたびセットアップしたら、接続して最終的に起こるエラーに対応します。

事例:

サーバー側

Webサーバーに認証リクエストが到達するとクエリパラメータの確認および検証が行われます。
その一例が、認証情報とデータベースに保管された既存の認証情報との照合です。

受け取ったパラメータに不足があったり、向こうで合ったりする場合、 { "ResultCode": 3, "Message": "Invalid parameters." }という結果が返されます。

検証が終わると、以下の結果が返されます。

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

高度な機能

ユーザーの認証以外にも、認証プロバイダーから追加情報を受け取ることができます。
追加情報を受け取るには、クライアントとWebサービス(「認証者の役割を担う」)間でプロトコルのようなものを確立されてる必要があります。

サーバーへデータを送る

最も簡単かつシンプルなのは、「全部か0か」戦略をとることです。
つまり、クライアントへ静的変数を返すか否かを選択するということです。
中には、より複雑なアプローチを必要とするケースもあります。Webサービスからクライアントがリクエストしたものに応じて、臨機応変にデータを返してくる場合などです。
本サブセクションでは、クライアントがWebサービスへデータを送る方法について説明しています。
データとは、認証に必要な認証情報と、さらにその他のパラメータです。
その他のパラメータは、とりわけ、認証応答内でサーバー側から取得できるデータのリクエストに使用するものを指します。
余計なAPI呼び出しの必要がなく、ログインワークフローを簡潔にすることができるため、便利なものです。

大量のデータが必要になる珍しいケースもあります。
その一方で、多くのWebサーバーではクエリの字列の文字数やURLの長さに制限があります。
これをカバーするため、PhotonではC# SDKでHTTPメソッドをクライアントからのPOSTに変更することができます。

この変更は、値に AuthenticationValues.AuthPostData フィールドを明示的に設定することで行えます。
後半部分をstring または byte[] または Dictionary<string, object>のタイプにすることもできます。
Dictionary<string, object>の場合は、ペイロードがJSON文字列に変換され、HTTPリクエストのコンテンツタイプが「applicaton/json」に設定されます。

C# SDKでは、AuthenticationValuesクラスが対応している各タイプに合わせてセッターメソッドを提供しています。

これは要件や制約になりかねないため、Webサービスからの認証リクエスト受信をPOSTメソッドとして選択する場合でも使えるように、POSTメソッドオプションが用意されています。

つまり、認証パラメータの送信に関して、クエリ文字列を選んでもPOSTデータを選んでも、その両方を選んでもいいということです。
以下の表で可能な組み合わせをご紹介します。

AuthPostData AuthGetParameters HTTP メソッド
null * GET
空白の文字列 * GET
文字列 (nullも空白も×) * POST
byte[] (nullは×、空白は〇) * POST
ディクショナリ<文字列、オブジェクト> (nullは×、空白は〇) * POST (コンテンツタイプ="application/json")

クライアントへデータを返す

PhotonサーバーはクライアントとWebサービス間のプロキシですので、Photonサーバーで処理できる変数について知っておく必要があります。

Photonサーバーが受信するすべてのHTTP受信応答で同じですが、Webサーバーが返すのはResultCode やオプションの Messageを含むJSONオブジェクトである必要があります。
以下は、認証中にPhotonサーバーがWebサービスから受け取れるものの一覧です。

  • UserId:
    認証そのもののパラメータとして使用したり、またはクライアント側からリクエストされることが可能。Photonサーバーに到達すると、必ずクライアントへ転送される。AuthenticationValues.UserIdが初めに設定されていないと、ランダムで生成されるUerIDがクライアントへ返送される。クライアントのUserIdがランダムなものに上書きされ、以降変更することは不可能。ResultCodeの値が1のときのみ返送すること。
    例: { "ResultCode": 1, "UserId": "SomeUniqueStringId" }
  • Nickname:
    認証そのもののパラメータとして使用したり、またはクライアント側からリクエストされることが可能。
    Webサービスから返されると、クライアントのNicknameが上書きされる。
    Nicknameは、クライアント側で後から変更可能。
    ResultCodeの値が1のときのみ返送すること。
    例: { "ResultCode": 1, "UserId": "SomeUniqueStringId", "Nickname": "SomeNiceDisplayName" }
  • AuthCookie:
    セキュアなデータとも呼ばれ、Webサービスから返されるが受信する暗号化トークンに埋め込まれているため、クライアント側からはアクセス不可能。
    後から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サーバーから返せるものをまとめています。

*: OAuth 2.0または2段階検証などの実装の際に便利です。

クライアントからのデータを読み取る

応答から、返された値を取得するコードスニペットを以下に記載します。

データ型の変換

ここでは、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"
    ]
}

トラブルシューティング

ダッシュボードで設定した認証URLがHTTPエラーを返す場合、Photonサーバーが認証呼び出しを短時間の間保留し、オーバーヘッドを回避します。
URLを設定したりテストする際は、この「バックオフ」時間があることを頭に入れておいてください。

ベストプラクティス

  • 認証プロバイダーから返される結果で、特に失敗した時に大事なのは、読めるMessageが含まれていることです。これがあれば、デバッグの苦しみからだいぶ解放されます。
  • ダッシュボードからクライアント側から設定されるべきでない静的キー/値のペアを設定してください。結果となるクエリ文字列でキーがダブらないようにするためです。
  • セキュリティの観点から、認証パラメータとしてパスワードをプレーンテキストで送らないようにしてください。
  • クエリ文字列パラメータは、Photonダッシュボードで設定することを推奨します。リクエストの送信元を確認することができます。
  • AuthenticationValuesメソッドを使用して パラメータを設定し、AuthGetParametersに直接値が影響しないようにしてください。クエリ文字列が崩れr内容にするためです。

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

カスタム認証を使用して、古いバージョン(または予期せぬバージョン)を使っているクライアントからの接続を拒否し、特定のエラーを返すことでユーザーにアップデートを促すことができます。
これを実現す津には、カスタム認証リクエストにバージョンを送信してください。クエリ文字列パラメータとして行うか、POSTデータ引数として行うかは自由に決められます。
以下の例では、クエリ文字列パラメータを使用しています。

カスタム認証URLが https://example.comの場合、リクエストはhttps://example.com?version={version}として送信されます。
認証プロバイダの実装から、受信したバージョンを取得して比較してください。
バージョンが許可されていれば、{ "ResultCode": 1 }を返し、許可されていない場合は、カスタム値(1以外)でResultCodeを返します。メッセージをつけることが望ましいです。
例: { "ResultCode": 5, "Message": "バージョンが許可されていません。" }.

Back to top