사용자 지정 인증
기존의 인증 제공자 중 어떤 것도 요구사항에 맞지 않거나 사용자 식별을 위해 다른 서비스를 통합하고 싶다면(자체 사용자 백엔드 포함) Photon의 커스텀 인증을 사용합니다.
간단히 말해, 커스텀 인증 제공자는 여러분이 직접 구축하고 관리하는 웹 서비스입니다. Photon 서버는 설정된 URL로 인증 값을 전달하고 잠시 응답을 기다립니다. 결과에 따라 클라이언트가 Photon에 연결되거나 거부됩니다. 최선의 경우, 백엔드가 userID를 정의하고(선택적으로) 추가 정보를 안전하게 전달해 서버 플러그인에서 사용할 수 있습니다.
샘플 구현
인증 제공자의 샘플 구현을 Git 저장소로 제공합니다.
원하시면 저장소를 포크하고 Pull Request를 보내주세요.
소스는 GitHub에서 확인할 수 있습니다.
인증 흐름
다음 단계는 인증 프로세스의 일반적인 흐름을 설명합니다.

- 클라이언트는 어떤 인증 제공자를 사용할지와 필요한 인증 데이터를 Photon 서버로(클라이언트가 연결할 때) 전달합니다.
- Photon 서버는 애플리케이션에 설정된 인증 제공자를 조회하고 다음 중 하나를 수행합니다.
- 인증 제공자 구성이 발견됨 → 3단계로 진행.
- 인증 제공자 구성이 없음 → 애플리케이션 설정에 따라 클라이언트를 허용하거나 거부.
- Photon 서버는
Connect()
와 함께 전달된 인증 정보를 사용해 인증 제공자를 호출합니다.- 인증 제공자가 온라인임 → 4단계로 진행.
- 인증 제공자가 오프라인임 → 해당 제공자 설정에 따라 클라이언트를 허용하거나 거부.
- 인증 제공자는 인증 정보를 처리하고 결과를 Photon 서버에 반환합니다.
- 인증 결과에 따라 클라이언트는 인증에 성공하거나 거부됩니다.
구현
클라이언트 측
클라이언트 측에서는 한 번만 관련 파라미터와 커스텀 인증 서비스를 설정하면 API가 커스텀 인증을 처리합니다.
설정을 마친 뒤에는 연결하고, 발생 가능한 오류를 처리하면 됩니다.
예시:
서버 측
웹 서버가 인증 요청을 수신하면 쿼리 파라미터를 검사하고 검증해야 합니다.
예를 들어, 자격 증명을 데이터베이스에 저장된 기존 값과 비교할 수 있습니다.
수신한 파라미터가 없거나 유효하지 않을 경우, 다음 결과를 반환해야 합니다: { "ResultCode": 3, "Message": "Invalid parameters." }
검증을 마친 뒤 결과는 다음 형식으로 반환해야 합니다:
- 성공:
{ "ResultCode": 1, "UserId": <userId> }
- 실패:
{ "ResultCode": 2, "Message": "Authentication failed. Wrong credentials." }
고급 특징
사용자 인증 외에도, 인증 제공자가 추가 정보를 반환할 수 있습니다.
이를 위해서는 클라이언트와 웹 서비스(“authenticator” 역할) 간에 어떤 형태의 프로토콜을 정해야 합니다.
서버로 데이터 전송
가장 쉽고 간단한 방법은 “전부 혹은 전무” 전략입니다:
클라이언트에 정해진 개수의 변수들을 반환할지 말지만 결정합니다.
하지만 어떤 사용 사례에서는 클라이언트 요청에 따라 웹 서비스가 “온디맨드”로 데이터를 반환해야 할 수도 있습니다.
이 하위 섹션은 클라이언트가 웹 서비스로 데이터를 보내는 방법을 설명합니다.
데이터는 인증에 필요한 자격 증명에 더해 추가 파라미터가 될 수 있습니다.
이 추가 파라미터는 서버 측에서 이용 가능한 일부 데이터를 인증 응답에 포함해 반환하도록 요청하는 데 사용할 수 있습니다.
이는 추가 API 호출을 줄이고 로그인 워크플로를 단순화하는 데 유용합니다.
드물게 인증에 많은 데이터를 요구하는 경우가 있습니다.
한편 대부분의 웹 서버는 쿼리 스트링 문자 수나 URL 길이에 제한이 있습니다.
이 때문에 Photon은 클라이언트(C# SDK)에서 AuthenticationValues.AuthPostData
필드를 설정해 HTTP 메서드를 POST로 변경할 수 있도록 제공합니다.
이 값의 타입은 string
, byte[]
, Dictionary<string, object>
중 하나가 될 수 있습니다.
Dictionary<string, object>
의 경우 페이로드는 JSON 문자열로 변환되고 HTTP 요청의 Content-Type은 "application/json"으로 설정됩니다.
C# SDK의 AuthenticationValues
클래스는 각 타입을 위한 setter 메서드를 제공합니다.
요건이나 제약으로 인해 필요할 수 있으므로, 웹 서비스가 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 서버는 클라이언트와 웹 서비스 사이의 프록시이므로, Photon 서버가 처리할 수 있는 변수들을 염두에 두어야 합니다.
모든 HTTP 응답과 마찬가지로, 웹 서버는 ResultCode
와 선택적 Message
를 포함한 JSON 객체를 반환해야 합니다.
아래는 인증 중에 Photon 서버가 웹 서비스로부터 기대할 수 있는 항목 목록입니다.
UserId
:
인증 자체의 파라미터로 사용되거나 클라이언트에서 요청될 수 있습니다.
Photon 서버가 수신하면 항상 클라이언트로 전달됩니다.
그렇지 않고AuthenticationValues.UserId
가 처음에 설정되지 않았다면, 무작위로 생성된 UserId가 클라이언트에 반환됩니다.
이는 클라이언트의UserId
값을 덮어쓰며 이후 변경할 수 없습니다.
ResultCode
값 1인 경우에만 반환해야 합니다.
예:{ "ResultCode": 1, "UserId": "SomeUniqueStringId" }
Nickname
:
인증 파라미터로 사용되거나 클라이언트에서 요청될 수 있습니다.
웹 서비스에서 반환되면 클라이언트의Nickname
값을 덮어씁니다.
이후에도 클라이언트에서Nickname
을 변경할 수 있습니다.
ResultCode
값 1인 경우에만 반환해야 합니다.
예:{ "ResultCode": 1, "UserId": "SomeUniqueStringId", "Nickname": "SomeNiceDisplayName" }
AuthCookie
:
보안 데이터라고도 하며, 웹 서비스에서 반환되지만 클라이언트에서 직접 접근할 수 없습니다(암호화된 토큰에 포함됨).
이후 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
뿐이며, 나머지는 모두 선택 사항입니다.
아래 표는 웹 서버가 반환할 수 있는 내용을 요약합니다.
*: 예를 들어 OAuth 2.0 구현이나 2단계 인증 등에 유용할 수 있습니다.
클라이언트의 데이터 읽기
다음은 응답에서 반환된 값을 얻는 코드 스니펫입니다:
데이터 타입 변환
이 섹션에서는 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"
]
}
문제 해결
대시보드에 구성한 인증 URL이 HTTP 에러를 반환하는 경우, Photon 서버는 과부하를 피하기 위해 짧은 시간 동안 인증 호출을 일시 중지(백오프)합니다.
URL을 구성하거나 테스트할 때 이 “백오프” 시간을 고려하세요.
모범 사례
- 인증 제공자에서 반환하는 결과에 특히 실패 시 읽기 쉬운
메시지
를 포함하세요.
이는 디버깅 시간을 크게 절약합니다. - 대시보드에서는 클라이언트에서 설정하면 안 되는 정적 키/값을 설정하세요.
결과 쿼리 스트링에서 키 중복을 방지합니다. - 보안상의 이유로 인증 파라미터로 평문 비밀번호를 보내지 마세요.
- 요청의 출처를 확인할 수 있도록 쿼리 스트링 파라미터는 가능하면 Photon 대시보드에서 설정하는 것이 좋습니다.
- 파라미터 설정에는
AuthenticationValues
의 메서드를 사용하고AuthGetParameters
에 직접 값을 대입하지 마세요.
잘못된 쿼리 스트링을 방지할 수 있습니다.
유스 케이스 예제: 오래된 버전의 클라이언트 블록
커스텀 인증을 사용하면 구버전(또는 예상치 못한 버전)의 클라이언트에서 들어오는 연결을 거부하고, 사용자에게 업데이트를 요청하는 특정 에러를 반환할 수 있습니다.
이를 위해서는 커스텀 인증 요청에 버전을 전송해야 합니다. 쿼리 스트링 파라미터나 POST 데이터 중 원하는 방법을 사용할 수 있습니다.
아래 예에서는 쿼리 스트링 파라미터를 사용합니다:
커스텀 인증 URL이 https://example.com
이라면 요청은 https://example.com?version={version}
형태로 전송됩니다.
인증 제공자 구현에서는 수신한 버전을 확인해 비교해야 합니다.
허용된 버전이면 { "ResultCode": 1 }
을 반환하세요.
허용되지 않는 경우에는 ResultCode
를 1이 아닌 사용자 정의 값으로(가능하면 메시지와 함께) 반환하세요.
예: { "ResultCode": 5, "Message": "Version not allowed." }
.