커스텀 인증
기본적으로 모든 애플리케이션은 익명의 사용자가 접속할 수 있으며 인증 메커니즘이 없어도 됩니다.
Photon 은 Photon 애플리케이션에 대해 커스텀 인증을 구현할 수 있는 옵션을 제공하고 있습니다.
Photon의 커스텀 인증은 유연합니다.
이에 대해서 완전 개인화된 솔루션 뿐만 아니라 잘 알려진 제3자 인증 공급자를 지원하고 있습니다.
- 커스텀 인증 공급자, 구축 - 내가 호스트 할 수 도 있음.
Git 리포지토리에서 인증 공급자 구현에 대한 샘플을 제공 하고 있습니다.
리포지토리를 마음껏 포크 하시고 요청을 저희에게 보내 주세요.
GitHub 에서 소스를 볼 수 있습니다.
인증 흐름
다음 단계들에는 인증 프로세스의 일반적인 흐름을 설명하고 있습니다.
- 인증 공급자가 사용할 정보와 인증에 필요한 데이터를
Connect()
할때 같이 Photon 서버로 클라이언트가 전달합니다. 이 사항은 Photon Bolt와 Photon Cloud 통합 내부에서 처리됩니다. - Photon 서버는 애플리케이션에 필요한 인증 공급자를 얻고 다음 단계 중의 하나를 처리합니다.
- 인증 공급자 환경 설정을 찾았으면 -> 인증은 단계 3에서 처리됩니다.
- 인증 공급자 환경 설정을 찾지 못했으면 -> 애플리케이션 설정에 따라 클라이언트는 접속이 허용 또는 거부됩니다.
- Photon 서버는
Connect()
에서 전달된 인증 정보를 가지고 인증 공급자를 호출 합니다.- 인증 공급자가 온라인의 경우 -> 인증은 단계 4로 처리됩니다.
- 인증 공급자가 오프라인의 경우 -> 상응하는 공급자 설정에 따라 클라이언트가 접속 허용 또는 거부 됩니다.
- 인증 공급자는 인증 정보를 처리하고 Photon 서버로 결과를 리턴해 줍니다.
- 인증 결과에 따라서 클라이언트는 성공적으로 인증 또는 거부 되게 될 것입니다.
Photon Cloud 설정
Photon 애플리케이션 관리 화면에서 모든 인증 공급자에 대하여 설정할 수 있습니다.
애플리케이션 상세 페이지로 가서 커스텀 인증 섹션을 오픈합니다.
인증 공급자 추가
커스텀 인증 공급자에 대한 환경설정은 쉬우며 Photon 애플리케이션 관리 화면에서 수 초 내에 완료할 수 있습니다.
스크린샷에서 보이고 있는 것처럼 인증 URL 을 입력할 수 있고, 인증 서비스가 온라인이 아닌 경우와 또는 다른 이유에 의해서 동작하지 않을 경우에 클라이언트를 거부할지 결정할 수 있습니다.
추가적으로 선택적인 키/값 쌍을 추가하여 인증 서비스에 요청마다 쿼리 스트링 파라미터에 전송할 수 있도록 할 수 있습니다.
- API 공개 키로 실제 Photon 서버 중에서 오는 요청인지 확인합니다.
- 커스텀 인증의 API 버전은 앞으로 바뀌게 될 사항에 대해서 더 잘 처리하기 위하여 사용합니다.
또한 설정된 인증 공급자 없이도 애플리케이션에 접속하는 익명의 클라이언트를 허용할지 거부할지를 설정할 수 있습니다.
기본적으로 허용하는 것으로 되어 있으며 초기에는 모든 클라이언트들이 애플리케이션에 인증 여부에 관계없이 접속할 수 있다는 것을 의미합니다.
Photon 애플리케이션 대시보드 애플리케이션 상세 페이지의 인증 섹션에서 체크 할 수 있습니다.
이 옵션은 최소 하나 이상의 인증 공급자를 추가할 때만 보입니다.
설정된 공급자가 없으면 기본 값(사용)으로 숨겨지게 됩니다.
인증 공급자의 변경 또는 삭제
애플리케이션 상세 페이지에서 기존의 인증 공급자를 편집할 수 있습니다.
편집 양식에서 모든 설정을 변경하거나 인증 공급자 전체를 삭제할 수 있습니다.
구현
클라이언트 측
클라이언트 측에서 LoadBalancing API는 커스텀 인증을 처리할 것입니다 - 관련된 파라미터와 타겟 커스텀 인증 서비스를 한번 설정 해 주기만 하면 됩니다.
설정이 완료되었으면 연결과 연속적인 오류를 처리할 수 있습니다.
예제:
플레이어로부터 Photon Bolt 루프로 인증 정보를 전달하려면 PhotonPlatform
을 초기화하고 Photon.Realtime.AuthenticationValues
인스턴스를 전달하는 것만큼 간단합니다.
Bolt는 이 정보를 Photon Cloud로 전송하고 인증 서버에서 전송되는 모든 데이터를 처리합니다.
다음은 새로운 PhotonPlatform
을 생성하고 AuthenticationValues
를 전달하는 예제 코드입니다.
피어를 시작하기 전 반드시 서버 또는 클라이언트에서 UdpPlatform
를 설정해야 합니다.
C#
using Photon.Realtime;
using UdpKit.Platform;
public class Menu : Bolt.GlobalEventListener
{
public void StartServer(string user, string password)
{
SetupPlatform(user, password);
BoltLauncher.StartServer();
}
public void StartClient(string user, string password)
{
SetupPlatform(user, password);
BoltLauncher.StartClient();
}
private void SetupPlatform(string user, string password)
{
var auth = new AuthenticationValues();
auth.AuthType = CustomAuthenticationType.Custom;
auth.AddAuthParameter("user", user);
auth.AddAuthParameter("pass", password);
auth.UserId = "my local user ID";
var platform = new PhotonPlatform(new PhotonPlatformConfig()
{
AuthenticationValues = auth
});
BoltLauncher.SetUdpPlatform(platform);
}
}
이 코드에서는 간결하게 하기 위해 인증 자격에 기반한 매우 간단한 비밀번호로 선택했습니다.
이 결과 쿼리 스트링은 다음과 같습니다:?user={user}&pass={pass}
일반적으로 자격 증명은 값이 쌍으로 구성되어 있으며, 첫 번째 값은 유일한 식별자(userId, username, email 등)이며 다른 하나는 "진짜의 증명" (해시 된 비밀번호, 키, 시크릿, 토큰 등)입니다.
보안상의 이유로 패스워드를 일반 텍스트로 전송하는 것은 권장하지 않습니다.
인증 오퍼레이션
인증 오퍼레이션은 인증값이 실제로 서버에게 전송되는 것입니다.
일반적으로 클라이언트 코드에서 직접적으로 사용되는 것이 아닌 당사의 API에 의해 사용됩니다.
중요한 부분: 서버로의 접속은 항상 인증 단계가 포함됩니다.
최초에 오퍼레이션은 실제의 인증 값을 암호화된 오퍼레이션으로 전송합니다.
이후 Photon 자신의 토큰을 제공하고 있는 서버는 자동으로 암호화하여 사용되게 됩니다.
서버측
웹서버가 인증 요청을 받은 즉시 쿼리 파라미터들이 유효한지 검사되어야 합니다.
예를 들어, 자격 증명은 데이터베이스에서 저장된 것과 비교 할 수 있습니다.
만약 수신된 파라미터들이 누락되었거나 유효하지 않는 것이면 결과는 { "ResultCode": 3, "Message": "Invalid parameters." }
로 리턴되어야 합니다.
검증이 끝난 후 결과는 다음과 같이 리턴되어야 합니다:
- 성공:
{ "ResultCode": 1, "UserId": <userId> }
- 실패:
{ "ResultCode": 2, "Message": "Authentication failed. Wrong credentials." }
고급 기능
사용자의 인증 이외에 인증 공급자로부터 추가적인 정보가 리턴 될 수 있습니다.
이렇게 하기 위해서 사용자는 클라이언트와 인증자 역할을 하는 웹 서비스 간에 특정한 프로토콜을 만들어야 합니다.
서버로 데이터 전송
가장 쉽고 간단한 방법은 "모두 아니면 아무것도(All or noting)" 전략입니다:
클라이언트에게 변수의 정적 숫자를 리턴할지 하지 않을지를 선택하세요.
하지만 클라이언트가 요청한 것을 기반으로 "온-디멘드" 데이터를 리턴하는 웹서비스와 같은 일부 유즈 케이스에서는 더 복잡한 접근법이 요구됩니다.
다음의 하위 섹션에서는 클라이언트가 웹서비스로 어떻게 데이터를 전송하는지에 대해 설명합니다.
데이터는 인증과 추가 파라미터의 인증에 필요한 자격 증명이 될 수 있습니다.
추가 파라미터들은 인증 응답의 리턴으로 서버측으로 부터 사용할 수 있는 데이터를 요청하기 위해서 사용 될 수 있습니다.
이것은 추가적인 API 호출이 없고 로그인 흐름이 간단 해지므로 매우 유용합니다.
아주 드문 경우지만 인증할 때 수많은 데이터가 필요하여 부하가 크게 걸릴 수도 있습니다.
다른 한편으로 대부분의 웹서버는 쿼리 스트링 또는 URL의 길이는 제한을 가지고 있습니다.
이 사항 때문에 Photon 은 C# SDK에서 AuthenticationValues.AuthPostData
를 명시적으로 설정하여 클라이언트에서 오는 HTTP 메소드를 POST로 변경을 제안하는 것입니다.
후자는 string
또는 byte[]
또는 Dictionary<string, object>
타입이 될 수 있습니다.
Dictionary<string, object>
의 경우, 페이로드는 JSON 문자열로 변환된고 HTTP 요청의 Content-Type는 "applicaton/json"입니다.
C# SDK에서, AuthenticationValues
클래스는 각 타입에 대하여 두 개의 setter 를 제공 하고 있습니다.
이것은 필요사항 또는 제약사항이 될 수 있으므로 웹 서비스에게 POST 메소드 인증 요청을 선택한 누구라도 가능하도록 해야 합니다.
다시 말하자면, 인증 파라미터를 전송하기 위해서 쿼리 스트링을 사용하던 POST 데이터를 사용하던 둘 다 사용하든지 관계없습니다.
다음 테이블에는 가능한 조합을 보여 주고 있습니다.
AuthPostData | AuthGetParameters | HTTP 메소드 |
---|---|---|
null | * | GET |
빈 문자열 | * | GET |
string (null 아님, 빈 문자열 아님) | * | POST |
byte[] (null 아님, 빈 문자열 가능) | * | POST |
Dictionary<string, object> (null 아님, 빈 문자열 가능) | * | POST (Content-Type="application/json") |
클라이언트에게 데이터 리턴
Photon 서버가 클라이언트와 웹 서비스 사이의 프록시이기 때문에 Photon 서버에 의해서 처리되는 변수들에 대해서 주의해야 합니다.
Photon 서버에 의해 수신되는 HTTP 수신 응답과 같이 웹 서버는 ResultCode
와 선택적으로 Message
를 포함한 JSON 객체를 리턴해야 합니다.
추가적으로 인증하는 동안에 웹 서비스로부터 수신할 수 있는 목록이 있습니다.
UserId
:
인증 자체의 파라미터로 사용되거나 클라이언트 측에서 요구 될 수 도 있습니다.
이것은 Photon 서버에 의해 수신될 때 클라이언트에게 항상 포워드 됩니다.
만약 초기에AuthenticationValues.UserId
값이 설정되어 있지 않으면 클라이언트로 다시 전송될 때 UserId는 무작위로 생성되어 전송됩니다.
이렇게 하면 클라이언트 내에서UserId
의 값을 오버라이드하고 이후에 변경될 수 없습니다.
ResultCode
값이 1인 경우에만 리턴되어야 합니다.
예:{ "ResultCode": 1, "UserId": "SomeUniqueStringId" }
ResultCode
값이 1인 경우에만 리턴되어야 합니다.
예:{ "ResultCode": 1, "UserId": "SomeUniqueStringId", "AuthCookie": { "SecretKey": "SecretValue", "Check": true, "AnotherKey": 1000 } }
{% endif %}Data
: JSON 객체로 클라이언트에게 리턴되어야 하는 모든 추가적인 값을 가지고 있습니다.
중첩된 배열 또는 객체들이 지원되지 않는다는 것에 주의하세요.
ResultCode
값이 1 또는 0인 경우에만 리턴되어야 합니다.
예:{ "ResultCode": 0, "Data": { "S": "Vpqmazljnbr=", "A": [ 1, -5, 9 ] } }
ResultCode
가 필수 리턴 변수이며 나머지 값은 다 선택적인 것들입니다.
다음의 테이블에서 웹 서버가 리턴해 줄 수 있는 것에 대해 요약해 놓았습니다.
ResultCode | Description | UserId | Nickname | Data | AuthCookie |
---|---|---|---|---|---|
0 | 인증 미완료, 데이터만 리턴됨.* | ||||
1 | 인증 성공. | (optional) | (optional) | (optional) | (optional) |
2 | 인증 실패. 잘못된 자격증명. | ||||
3 | 잘못된 파라미터. |
*: OAuth 2.0 구현 및 이중 인증에 유용하다는 것을 나타냅니다.
클라이언트로 온 데이터 읽기
Photon Bolt가 시작되면 Photon Cloud에 자동으로 연결하여 앞에서 설명한 이증 프로세스가 실행됩니다.
이 프로세스가 완료되면 BoltStartDone
이 호출되며, 현재 BoltMatchmaking.CurrentMetadata
속성을 확인하여 서버가 전송하는 모든 사용자 지정 정보를 읽을 수 있습니다.
사용할 수 있도록 하기 위해 Photon 통합을 통해 생성됩니다.
다음 코드는 인증 응답을 해석할 때 어떻게 데이터 타입을 변환하는지 보여 줍니다:
C#
using System.Collections.Generic;
using Bolt.Matchmaking;
using Bolt.Utils;
using Photon.Realtime;
using UdpKit.Platform;
public class Menu : Bolt.GlobalEventListener
{
// ...
public override void BoltStartDone()
{
var meta = BoltMatchmaking.CurrentMetadata;
// Read all custom data sent from your auth server
Dictionary<string, object> customData;
if (meta.TryGetValue("Data", out customData))
{
var text = string.Empty;
foreach (var item in customData)
{
text += string.Format("{0} : {1}\n", item.Key, item.Value);
}
BoltLog.Info(text);
}
// Read the UserId of the local player
string userID;
if (meta.TryGetValue("UserId", out userID))
{
BoltLog.Info("UserID: {0}", userID);
}
// Read the Nickname of the local player
string nickName;
if (meta.TryGetValue("Nickname", out nickName))
{
BoltLog.Info("Nickname: {0}", nickName);
}
// Your usual BoltStartDone behaviour: setup game server or join a session
}
}
데이터 타입 변환
이 섹션에서는 Photon 서버와 웹 서비스간의 데이터 교환의 타입에 대해서만 설명합니다.
클라이언트와 Photon 서버간의 데이터 타입에 대해서는 Photon 의 직렬화 페이지를 참고 하시기 바랍니다.
Photon 서버 -> 웹 서비스
C# / .NET (Photon 지원 타입) | JavaScript / JSON |
---|---|
byte
|
number |
short
|
|
int
|
|
long
|
|
double
|
|
bool
|
bool |
string
|
string |
byte[] (byte 배열 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 서버 전송:
JavaScript
{
"(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":[
"(Object)0",
"(Object)xy",
"(Object)False"
],
"hk_null":"null"
},
"null":"null",
"(String[])string[]":[
"(String)PUN",
"(String)TB",
"(String)RT",
"(String)Bolt",
"(String)Chat"
],
"(Byte[])byte[]":[
"(Byte)255",
"(Byte)0"
],
"(Int16[])short[]":[
"(Int16)-32768",
"(Int16)32767"
],
"(Int32[])int[]":[
"(Int32)-2147483648",
"(Int32)2147483647"
],
"(Int64[])long[]":[
"(Int64)-9223372036854775808",
"(Int64)9223372036854775807"
],
"(Single[])float[]":[
"(Single)-3.402823E+38",
"(Single)3.402823E+38"
],
"(Double[])double[]":[
"(Double)-1.79769313486232E+308",
"(Double)1.79769313486232E+308"
],
"(Boolean[])bool[]":[
"(Boolean)True",
"(Boolean)False"
]
}
Web 서비스 수신:
JavaScript
{
"(object)Dictionary":{
"dk_int":"(number)1",
"dk_str":"(string)dv2",
"dk_bool":"(boolean)true"
},
"(object)Hashtable":{
"hk_byte":"(number)255",
"hk_null":null,
"hk_array":[
"(number)0",
"(string)xy",
"(boolean)false"
]
},
"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"
]
}
웹 서비스 -> Photon 서버
C#/.Net 에서 JavaScript/JSON 타입과 각각 대응하는 테이블입니다.
Here is a table that matches each JavaScript/JSON type to its equivalent one in 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 Service 전송:
JavaScript
{
"(object)number": {
"MAX_VALUE": "(number)1.7976931348623157e+308",
"MIN_VALUE": "(number)5e-324"
},
"(object)object": {
"string": "(string)xyz",
"null": null,
"bool": "(boolean)false",
"undefined": "(undefined)undefined",
"number": "(number)-3.14"
},
"(array)array": [
"(string)xyz",
"(number)0",
"(boolean)true",
null,
"(undefined)undefined"
]
}
Photon Server 수신:
JavaScript
{
"(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)number":"-3.14"
},
"(Object[])array":[
"(Object)xyz",
"(Object)0",
"(Object)True",
"null",
"null"
]
}
문제 해결
사용자 정의 인증이 실패하면, BoltStartFailed
콜백은 매개변수 UdpConnectionDisconnectReason.Authentication
을 갖고 트리거 됩니다:
전에 언급했듯이, 인증 절차는 Bolt를 시작할 때 발생합니다.
서버가 플레이어 인증 정보를 무효화했다면, Bolt의 시작이 실패하고 다음의 메소드에서 리포트 됩니다.
C#
public override void BoltStartFailed(UdpConnectionDisconnectReason disconnectReason)
{
Debug.LogErrorFormat("BoltStartFailed. Reason: {0}", disconnectReason);
}
관리 화면에서 구성한 인증 URL이 일부 HTTP 오류를 반환하는 경우 Photon 서버는 일부 오버헤드를 방지하기 위해 인증 호출을 잠시 일시 중지합니다.
URL을 구성하거나 테스트할 때 이 "백오프" 시간을 고려하십시오.
모범 사례
- 인증 공급자로부터 반환된 결과에는, 특히 오류 발생 시 읽을 수 있는
메시지
가 포함되어야 합니다.
이렇게 하면 힘든 디버깅 과정을 줄일 수 있습니다. - 관리 화면에서 정적 키/값이 클라이언트에서 설정될 수 없도록 하세요.
쿼리 스트링 결과에 중복 키를 방지하게 해 줍니다. - 보안상의 이유로 패스워드를 인증 파라미터로 일반 텍스트로 전송하지 마시기 바랍니다.
- Photon 관리 화면에서 쿼리 문자열 파라미터를 설정하는 것을 권장하고 있습니다.
이 방식을 통해 요청의 원천을 확인할 수 있습니다. AuthenticationValues
를 이용하여 파라미터를 설정하여AuthGetParameters
의 값에 직접적인 영향이 가지 않도록 해주세요.
이렇게 하면 잘못된 쿼리 스트링을 방지할 수 있습니다.
유즈 케이스 예제: Block 이전 클라이언터 버전
커스텀 인증을 사용하여 이전 버전(또는 예기치 않은 버전)을 사용하는 클라이언트에서 들어오는 연결을 거부하고 사용자에게 업데이트를 요청할 수 있도록 특정 오류를 리턴할 수 있습니다.
이렇게 하려면 커스텀 인증 요청에서 버전을 전송해야 합니다. 쿼리 문자열 매개 변수 또는 POST 데이터 인수로 할지 결정하는 것은 사용자의 몫입니다.
아래 예에서는 쿼리 문자열 매개 변수를 사용합니다:
C#
private void SetupPlatform(string user, string password)
{
var version = BoltNetwork.CurrentVersion;
var auth = new AuthenticationValues();
auth.AuthType = CustomAuthenticationType.Custom;
auth.AddAuthParameter("version", version);
var platform = new PhotonPlatform(new PhotonPlatformConfig()
{
AuthenticationValues = auth
});
BoltLauncher.SetUdpPlatform(platform);
}
커스텀 인증 URL이 https://example.com
인 경우, https://example.com?version={version}
으로 요청될 것입니다.
인증 공급자 구현에서 버전 정보를 받아 비교해야 합니다.
버전이 허용되는 경우 { "ResultCode": 1 }
을 리턴합니다.
그렇지 않으면 사용자 지정 값(1과 다름)이 포함된 ResultCode
를 메시지와 함께 반환해야 합니다.
예: { "ResultCode": 5, "Message": "Version not allowed." }
.