23. 외부 서비스 지원 Part 1: 인증 검증

Attention

iFun Authenticator 를 이용하는 Deprecated - iFun Authenticator 은 Deprecated 되어 제거될 예정입니다. 본 문서에서 설명하는 각 플랫폼별 인증 검증 기능을 사용하시기 바랍니다.

Apple Game Center, Facebook, Google Plus, Google Play Game, LINE, Nexon Toy, Nexon Launcher (PC) 등의 외부 플랫폼을 이용한다면 유저 인증을 수행하기 위해서는 Client-side, Server-side 두 단계 인증 구현이 필요합니다. 대부분의 외부 플랫폼은 클라이언트측 인증 처리를 위한 SDK 를 제공하지만 서버측에서의 유저 인증은 제공하지 않습니다. 따라서 게임 클라이언트에서 해당 플랫폼의 SDK를 이용하여 인증을 수행하고, 인증 사실을 식별 할 수 있는 키를 인증 서버로부터 받아 게임 서버로 전송 후 게임 서버에서 전달 받은 키의 유효성 검사를 수행해야 합니다. 인증에서의 이런 서버측 구현은 연동하는 플랫폼이 늘어날 수록 게임 개발자에게 부담이 될 수 있습니다.

아이펀 엔진은 주요 플랫폼과의 Server-side 인증 검증 기능과 친구목록, 닉네임 등의 데이터를 불러오는 기능을 단순화하여 서비스 형태로 제공합니다. 지원하는 외부 플랫폼은 다음과 같습니다.

  • Apple Game Center

  • Facebook

  • Google Play Game

  • Google Plus

  • LINE

  • Nexon Toy

  • Nexon Launcher (PC)

  • Kakao Game (카카오 게임)

  • Apple로 로그인 (Sign in with Apple)

Note

지원 가능한 외부 플랫폼은 향후 지속적으로 추가될 예정입니다.

Note

개발 또는 테스트를 진행하면서 인증 서비스를 꺼야 하는 경우가 생길 수도 있습니다. 아이펀 엔진에서는 인증 검증 서비스 설정 파라미터 에서 설명하는 use_authentication_service 를 false 로 하면 bypass 모드로 동작하게 되어 있습니다. 이를 이용하면 인증 처리를 위해 호출했던 인증 함수를 주석으로 막지 않아도 됩니다.

23.1. Apple Game Center

23.1.1. 클라이언트 인증 유효성 검사하기

다음은 아이펀 엔진에서 클라이언트가 로그인 요청을 했을 때 어떻게 Apple Game Center 인증을 처리하는지 예제를 설명합니다.

인터페이스는 /usr/include/funapi/service/apple_game_center_authentication.h 에서 확인 할 수 있습니다.

비동기 방식

// 앱 번들 아이디
const string bundle_id = "...";

void OnAuthenticated(const AppleGameCenterAuthenticationRequest2 &request,
                     const AppleGameCenterAuthenticationResponse &response,
                     bool error) {
  if (error) {
    LOG(ERROR) << "Authentication service error.";
    return;
  }

  if (not response.success) {
    LOG(ERROR) << "Authentication failed. "
               << "error code=" << response.error_code
               << ", error message=" << response.error_message;
    return;
  }

  LOG(INFO) << "Authentication succeeded.";
}

void OnLogin(const Ptr<Session> &session, const Json &message) {
  // base64 string 은 AppleGameCenterAuthenticationRequest2 를,
  // std::vector<uint8_t> 는 AppleGameCenterAuthenticationRequest 를
  // 사용해서 request 구조체를 만듭니다.
  AppleGameCenterAuthenticationRequest2 request(
    bundle_id,
    message["player_id"].GetString(),
    message["public_key_url"].GetString(),
    // 클라이언트 측에서 signature, salt를 base64로 인코딩해서 보냈다고 가정합니다.
    message["signature"].GetString(),
    message["salt"].GetString(),
    message["timestamp"].GetInteger());

  Authenticate(request, OnAuthenticated);
}

// 앱 번들 아이디 const String bundle_id = “…”;

static void OnAuthenticated (AppleGameCenterAuthentication.AuthenticationRequest2 request,

AppleGameCenterAuthentication.AuthenticationResponse response, bool error)

{
if (error || !response.Success) {

Log.Error (“Failed to verify the authentication.”); return;

}

Log.Info (“Verifying the authentication succeeded.”);

}

public static void OnLogin (Session session, JObject message) {

String player_id = message [“player_id”].ToString (); if (String.IsNullOrEmpty (player_id)) {

return;

}

String public_key_url = message [“public_key_url”].ToString (); if (String.IsNullOrEmpty (public_key_url)) {

return;

}

// 클라이언트 측에서 signature, salt를 base64로 인코딩해서 보냈다고 가정합니다. String signature = message [“signature”].ToString (); if (String.IsNullOrEmpty (signature)) {

return;

}

String salt = message [“salt”].ToString (); if (String.IsNullOrEmpty (salt)) {

return;

}

long timestamp = long.Parse(message [“timestamp”].ToString ()); if (timestamp == 0) {

return;

}

AppleGameCenterAuthentication.AuthenticationRequest2 request =
new AppleGameCenterAuthentication.AuthenticationRequest2 (

bundle_id, player_id, public_key_url, signature, salt, timestamp);

AppleGameCenterAuthentication.Authenticate (request, OnAuthenticated);

}

23.2. Facebook

23.2.1. 클라이언트 인증 유효성 검사하기

다음은 아이펀 엔진에서 클라이언트가 로그인 요청을 했을 때 어떻게 Facebook 인증을 처리하는지 예제를 설명합니다. 인터페이스는 /usr/include/funapi/service/facebook_authentication.h 에서 확인할 수 있습니다.

동기 방식

void OnLogin(const Ptr<Session> &session, const Json &message) {
  const string access_token = message["access_token"].GetString();
  if (access_token.empty()) {
    // error
    return;
  }

  FacebookAuthenticationRequest request(access_token);

  FacebookAuthenticationResponse response;

  if (not AuthenticateSync(request, &response)) {
    LOG(ERROR) << "Authentication service error.";
    return;
  }

  if (not response.success) {
    LOG(ERROR) << "Authentication failed. "
               << "error_code=" << response.error.code
               << ", error_message" << response.error.message;
    return;
  }

  LOG(INFO) << "Authentication succeeded. "
            << "client_id=" << response.client_id
            << ", name=" << response.name;
}
public static void OnLogin (Session session, JObject message)
{
  String access_token = message ["access_token"].ToString ();
  if (String.IsNullOrEmpty (access_token)) {
    return;
  }

  FacebookAuthentication.AuthenticationRequest request =
    new FacebookAuthentication.AuthenticationRequest (access_token);

  FacebookAuthentication.AuthenticationResponse response;
  if (!FacebookAuthentication.AuthenticateSync (request, out response)) {
    Log.Error ("Failed to verify the authentication.");
    return;
  }

  if (!response.Success) {
    Log.Error ("Failed to verify the authentication. error.code: {0}, error.message = {1}",
               response.Error.Code, response.Error.Message);
    return;
  }

  Log.Info ("Verifying the authentication succeeded. client_id={0}, name={1}",
            response.ClientId, response.Name);
}

비동기 방식

void OnAuthenticated(const FacebookAuthenticationRequest &request,
                     const FacebookAuthenticationResponse &response,
                     bool error) {
  if (error) {
    LOG(ERROR) << "Authentication service error.";
    return;
  }

  if (not response.success) {
    LOG(ERROR) << "Authentication failed. "
               << "error_code=" << response.error.code
               << ", error_message" << response.error.message;
    return;
  }

  LOG(INFO) << "Authentication succeeded. "
            << "client_id=" << response.client_id
            << ", name=" << response.name;
}


void OnLogin(const Ptr<Session> &session, const Json &message) {
  const string access_token = message["access_token"].GetString();
  if (access_token.empty()) {
    // error
    return;
  }

  FacebookAuthenticationRequest request(access_token);
  Authenticate(request, OnAuthenticated);
}
static void OnAuthenticated (FacebookAuthentication.AuthenticationRequest request,
                             FacebookAuthentication.AuthenticationResponse response,
                             bool error)
{
  if (error || !response.Success) {
    Log.Error ("Failed to verify the authentication.");
    return;
  }

  Log.Info ("Verifying the authentication succeeded. client_id={0}, name={1}",
            response.ClientId, response.Name);
}

public static void OnLogin (Session session, JObject message)
{
  String access_token = message ["access_token"].ToString ();
  if (String.IsNullOrEmpty (access_token)) {
    return;
  }

  FacebookAuthentication.AuthenticationRequest request =
    new FacebookAuthentication.AuthenticationRequest (access_token);
  FacebookAuthentication.Authenticate (request, OnAuthenticated);
}

23.2.2. 사용자 정보 요청하기

다음은 Facebook 사용자 정보를 요청하는 예제를 설명합니다.

인터페이스는 /usr/include/funapi/service/facebook_authentication.h 에서 확인할 수 있습니다.

동기 방식

void OnInfo(const Ptr<Session> &session, const Json &message) {
  const string access_token = message["access_token"].GetString();
  if (access_token.empty()) {
    // error
    return;
  }

  FacebookPersonalInfoRequest request(access_token);

  FacebookPersonalInfoResponse response;

  if (not GetPersonalInfoSync(request, &response)) {
    LOG(ERROR) << "Authentication service error.";
    return;
  }

  if (not response.success) {
    LOG(ERROR) <<  "error_code=" << response.error.code
               << ", error_message" << response.error.message;
    return;
  }

  LOG(INFO) << "Personal info request succeeded.";
  BOOST_FOREACH(const PlayerAccount &player_account, response.friends) {
    LOG(INFO) << "friend_service_provider=" << player_account.service_provider()
              << ", friend_id=" << player_account.id();
  }
}
public static void OnInfo (Session session, JObject message)
{
  String access_token = message ["access_token"].ToString ();
  if (String.IsNullOrEmpty (access_token)) {
    return;
  }

  FacebookAuthentication.PersonalInfoRequest request =
    new FacebookAuthentication.PersonalInfoRequest (access_token);

  FacebookAuthentication.PersonalInfoResponse response;
  if (!FacebookAuthentication.GetPersonalInfoSync (request, out response)) {
    Log.Error ("Failed to get the personal info.");
    return;
  }

  Log.Info ("Getting the personal info succeeded. number of friends: {0}",
            response.Friends.Count);
  foreach (PlayerAccount account in response.Friends) {
    Log.Info (" - friend: {0}", account.Id);
  }
}

비동기 방식

void OnPersonalInfoGot(const FacebookPersonalInfoRequest &request,
                       const FacebookPersonalInfoResponse &response,
                       bool error) {
  if (error) {
    LOG(ERROR) << "Authentication service error.";
    return;
  }

  if (not response.success) {
    LOG(ERROR) <<  "error_code=" << response.error.code
               << ", error_message" << response.error.message;
    return;
  }

  LOG(INFO) << "Personal info request succeeded.";
  BOOST_FOREACH(const PlayerAccount &player_account, response.friends) {
    LOG(INFO) << "friend_service_provider=" << player_account.service_provider()
              << ", friend_id=" << player_account.id();
  }
}


void OnInfo(const Ptr<Session> &session, const Json &message) {
  const string access_token = message["access_token"].GetString();
  if (access_token.empty()) {
    // error
    return;
  }

  FacebookPersonalInfoRequest request(access_token);
  GetPersonalInfo(request, OnPersonalInfoGot);
}
static void OnPersonalInfoAcquired (FacebookAuthentication.PersonalInfoRequest request,
                                    FacebookAuthentication.PersonalInfoResponse response,
                                    bool error)
{
  if (error || !response.Success) {
    Log.Error ("Failed to verify the authentication.");
    return;
  }

  Log.Info ("Getting the personal info succeeded. number of friends: {0}",
            response.Friends.Count);
  foreach (PlayerAccount account in response.Friends) {
    Log.Info (" - friend: {0}", account.Id);
  }
}

public static void OnInfo (Session session, JObject message)
{
  String access_token = message ["access_token"].ToString ();
  if (String.IsNullOrEmpty (access_token)) {
    return;
  }

  FacebookAuthentication.PersonalInfoRequest request =
    new FacebookAuthentication.PersonalInfoRequest (access_token);
  FacebookAuthentication.GetPersonalInfo (request, OnPersonalInfoAcquired);
}

23.3. Google Play Game

23.3.1. 클라이언트 인증 유효성 검사하기

다음은 아이펀 엔진에서 클라이언트가 로그인 요청을 했을 때 어떻게 Google Play Game 인증을 처리하는지 예제를 설명합니다.

인터페이스는 /usr/include/funapi/service/google_play_game_authentication.h 에서 확인할 수 있습니다.

비동기 방식

// 앱 아이디
const string app_id = "...";

void OnAuthenticated(const GooglePlayGameAuthenticationRequest &request,
                     const GooglePlayGameAuthenticationResponse &response,
                     bool error) {
  if (error) {
    LOG(ERROR) << "Authentication service error.";
    return;
  }

  if (not response.success) {
    LOG(ERROR) << "Authentication failed. "
               << "error_code=" << response.error_code
               << ", error_message" << response.error_message;
    return;
  }

  LOG(INFO) << "Authentication succeeded. "
            << ", name=" << response.name
            << ", picture=" << response.picture;
}

 // 게임 클라이언트 APP 의 OAuth 2.0 클라이언트 ID 로 설정합니다.
 // 자세한 사항은 아래 설명을 참고해 주세요.
 const string kOAuth2ClientId = "<APP OAuth 2.0 클라이언트 ID>";

void OnLogin(const Ptr<Session> &session, const Json &message) {
  const string id_token = message["id_token"].GetString();
  if (id_token.empty()) {
    // error.
    return;
  }

  GooglePlayGameAuthenticationRequest request(kOAuth2ClientId, id_token);
  Authenticate(request, OnAuthenticated);
}
const String app_id = "...";

static void OnAuthenticated (GooglePlayGameAuthentication.AuthenticationRequest request,
                             GooglePlayGameAuthentication.AuthenticationResponse response,
                             bool error)
{
  if (error || !response.Success) {
    Log.Error ("Failed to verify the authentication. error_code: {0}, error_description = {1}",
               response.ErrorCode, response.ErrorDescription);
    return;
  }

  Log.Info ("Verifying the authentication succeeded. name={0}, picture={1}",
            response.Name, response.Picture);
}

 // 게임 클라이언트 APP 의 OAuth 2.0 클라이언트 ID 로 설정합니다.
 // 자세한 사항은 아래 설명을 참고해 주세요.
 const string kOAuth2ClientId = "<APP OAuth 2.0 클라이언트 ID>";

public static void OnLogin (Session session, JObject message)
{
  string id_token = message ["id_token"].ToString ();
  if (string.IsNullOrEmpty (id_token)) {
    return;
  }

  GooglePlayGameAuthentication.AuthenticationRequest request =
    new GooglePlayGameAuthentication.AuthenticationRequest (kOAuth2ClientId, id_token);
  GooglePlayGameAuthentication.Authenticate (request, OnAuthenticated);
}
  • kOAuth2ClientId 상수는 Google Play 인증 기능을 사용 중인 클라이언트 게임 APP(예: 안드로이드 게임 APP)의 OAuth 2.0

client ID 로 설정합니다. `Google Play 게임 서비스`_ 에 클라이언트 게임 APP 을 정상적으로 추가하였다면, `Google API 콘솔 - API 및 서비스 - 사용자 인증 정보`_ 페이지에서 확인하실 수 있습니다. ‘OAuth 2.0 클라이언트 ID’ 테이블의 ‘클라이언트 ID’ 의 값으로 설정합니다. 780816631155-gbvyo1o7r2pn95qc4ei9d61io4uh48hl.apps.googleusercontent.com 같은 형태로 되어 있습니다. 앞부분 780816631155-gbvyo1o7r2pn95qc4ei9d61io4uh48hl 은 게임 클라이언트 APP 마다 다릅니다.

Tip

클라이언트 ID 는 서버를 띄우는 환경에 따라서 달라지는 경우가 많습니다. 예를 들어 개발 중이나 테스트 중에는 테스트 전용 프로젝트를 사용하고, 실제 운영 환경에서는 운영을 위한 전용 프로젝트를 사용할 수도 있습니다. MANIFEST 설정에서 값을 읽어 오는 방식으로 처리하면 좀 더 개발과 운영이 용이해집니다. Programming part 3: Program execution parameters 를 참고해 주세요.

  • OnLogin 함수는 `Google Play 게임 서비스`_ 를 통해, 게임 서버에 로그인을 요청하는 메시지 핸들러의 예입니다. 게임 클라이언트가 Google 서비스를 통해 인증을 성공하면 ID token 을 응답으로 받습니다. 게임 클라이언트 APP 은 게임 서버에 로그인 요청 메시지를 보내기 전에, Google 서비스를 통해 게임을 플레이중인 사용자의 ID token 을 얻어야 합니다. 게임 서버에 로그인을 시도할 때 미리 획득한 ID token 을 메시지에 포함시켜 전송해야 합니다.

    위 예제는 로그인 메시지의 "id_token" 에 포함되어 있는 경우입니다.

  • Authenticate 메써드가 클라이언트 게임 APP 이 보낸 ID token 이 서버에 설정되어 있는 클라이언트 ID 를 가진 APP 에 대해 유효한 Goole 계정인지 검증하는 API 입니다.

    검증 과정이 끝나면 예제에서 OnAuthenticated 로 정의되어 있는 콜백이 호출되는데, 정상적인 Google 계정이면 GooglePlayGameAuthenticationResponse.success (C++), GooglePlayGameAuthentication.AuthenticationResponse.Success (C#) 값이 true 로 설정되고, 계정의 성명 등이 설정됩니다. 이를 통해 로그인한 사용자가 누구인지 식별할 수 있습니다.

23.4. Google Plus (Deprecated)

Warning

Google+ 서비스가 2019년 3월 27일부로 종료되었기 때문에 관련 API 들은 더 이상 사용할 수 없습니다.

23.5. LINE

23.5.1. 클라이언트 인증 유효성 검사하기

다음은 아이펀 엔진에서 클라이언트가 로그인 요청을 했을 때 어떻게 LINE 인증을 처리하는지 예제를 설명합니다.

인터페이스는 /usr/include/funapi/service/line_authentication.h 에서 확인할 수 있습니다.

동기 방식

void OnLogin(const Ptr<Session> &session, const Json &message) {
  const string access_token = message["access_token"].GetString();
  if (access_token.empty()) {
    // error
    return;
  }

  LineAuthenticationRequest request(access_token);

  LineAuthenticationResponse response;

  if (not AuthenticateSync(request, &response)) {
    LOG(ERROR) << "Authentication service error.";
    return;
  }

  if (not response.success) {
    LOG(ERROR) << "Authentication failed. "
               << "error" << response.error
               << ", error_description" << response.error_description;
    return;
  }

  LOG(INFO) << "Authentication succeeded. "
            << "scope" << response.scope
            << ", client_id" << response.client_id
            << ", expires_in=" << response.expires_in;
}
public static void OnLogin (Session session, JObject message)
{
  String access_token = message ["access_token"].ToString ();
  if (String.IsNullOrEmpty (access_token)) {
    return;
  }

  LineAuthentication.AuthenticationRequest request =
    new LineAuthentication.AuthenticationRequest (access_token);

  LineAuthentication.AuthenticationResponse response;
  if (!LineAuthentication.AuthenticateSync (request, out response)) {
    Log.Error ("Failed to verify the authentication.");
    return;
  }

  if (!response.Success) {
    Log.Error ("Failed to verify the authentication. error: {0}, error_description = {1}",
               response.Error, response.ErrorDescription);
    return;
  }

  Log.Info ("Verifying the authentication succeeded. scope={0}, client_id={1}, expires_in={2}",
            response.Scope, response.ClientId, response.ExpiresIn);
}

비동기 방식

void OnAuthenticated(const LineAuthenticationRequest &request,
                     const LineAuthenticationResponse &response,
                     bool error) {
  if (error) {
    LOG(ERROR) << "Authentication service error.";
    return;
  }

  if (not response.success) {
    LOG(ERROR) << "Authentication failed. "
               << "error" << response.error
               << ", error_description" << response.error_description;
    return;
  }

  LOG(INFO) << "Authentication succeeded. "
            << "scope" << response.scope
            << ", client_id" << response.client_id
            << ", expires_in=" << response.expires_in;
}


void OnLogin(const Ptr<Session> &session, const Json &message) {
  const string access_token = message["access_token"].GetString();
  if (access_token.empty()) {
    // error
    return;
  }

  LineAuthenticationRequest request(access_token);
  Authenticate(request, OnAuthenticated);
}
static void OnAuthenticated (LineAuthentication.AuthenticationRequest request,
                             LineAuthentication.AuthenticationResponse response,
                             bool error)
{
  if (error || !response.Success) {
    Log.Error ("Failed to verify the authentication. error: {0}, error_description = {1}",
               response.Error, response.ErrorDescription);
    return;
  }

  Log.Info ("Verifying the authentication succeeded. scope={0}, client_id={1}, expires_in={2}",
            response.Scope, response.ClientId, response.ExpiresIn);
}


public static void OnLogin (Session session, JObject message)
{
  String access_token = message ["access_token"].ToString ();
  if (String.IsNullOrEmpty (access_token)) {
    return;
  }

  LineAuthentication.AuthenticationRequest request =
    new LineAuthentication.AuthenticationRequest (access_token);
  LineAuthentication.Authenticate (request, OnAuthenticated);
}

23.5.2. 사용자 정보 요청하기

다음은 LINE 사용자 정보를 요청하는 예제를 설명합니다.

인터페이스는 /usr/include/funapi/service/line_authentication.h 에서 확인할 수 있습니다.

동기 방식

void OnInfo(const Ptr<Session> &session, const Json &message) {
  const string access_token = message["access_token"].GetString();
  if (access_token.empty()) {
    // error
    return;
  }

  LinePersonalInfoRequest request(access_token);

  LinePersonalInfoResponse response;

  if (not GetPersonalInfoSync(request, &response)) {
    LOG(ERROR) << "Authentication service error.";
    return;
  }

  if (not response.success) {
    LOG(ERROR) << "Personal info request failed. "
               << "error_message" << response.error_message;
    return;
  }

  LOG(INFO) << "Personal info request succeeded. "
            << "display_name" << response.display_name
            << ", user_id" << response.user_id
            << ", picture_url=" << response.picture_url
            << ", status_message=" << response.status_message;
}
public static void OnInfo (Session session, JObject message)
{
  String access_token = message ["access_token"].ToString ();
  if (String.IsNullOrEmpty (access_token)) {
    return;
  }

  LineAuthentication.PersonalInfoRequest request =
    new LineAuthentication.PersonalInfoRequest (access_token);

  LineAuthentication.PersonalInfoResponse response;
  if (!LineAuthentication.GetPersonalInfoSync (request, out response)) {
    Log.Error ("Failed to get the personal info.");
    return;
  }

  Log.Info ("Getting the personal info succeeded. display_name={0}, picture_url={1}, status_message={2}, error_message={3}",
            response.DisplayName, response.PictureUrl, response.StatusMessage, response.ErrorMessage);
}

비동기 방식

void OnPersonalInfoGot(const LinePersonalInfoRequest &request,
                       const LinePersonalInfoResponse &response,
                       bool error) {
  if (error) {
    LOG(ERROR) << "Authentication service error.";
    return;
  }

  if (not response.success) {
    LOG(ERROR) << "Personal info request failed. "
               << "error_message" << response.error_message;
    return;
  }

  LOG(INFO) << "Personal info request succeeded. "
            << "display_name" << response.display_name
            << ", user_id" << response.user_id
            << ", picture_url=" << response.picture_url
            << ", status_message=" << response.status_message;
}


void OnInfo(const Ptr<Session> &session, const Json &message) {
  const string access_token = message["access_token"].GetString();
  if (access_token.empty()) {
    // error
    return;
  }

  LinePersonalInfoRequest request(access_token);
  GetPersonalInfo(request, OnPersonalInfoGot);
}
static void OnPersonalInfoAcquired (LineAuthentication.PersonalInfoRequest request,
                                    LineAuthentication.PersonalInfoResponse response,
                                    bool error)
{
  if (error || !response.Success) {
    Log.Error ("Failed to get the personal info. error_message = {0}",
               response.ErrorMessage);
    return;
  }

  Log.Info ("Getting the personal info succeeded. display_name={0}, picture_url={1}, status_message={2}, error_message={3}",
            response.DisplayName, response.PictureUrl, response.StatusMessage, response.ErrorMessage);
}


public static void OnInfo (Session session, JObject message)
{
  String access_token = message ["access_token"].ToString ();
  if (String.IsNullOrEmpty (access_token)) {
    return;
  }

  LineAuthentication.PersonalInfoRequest request =
    new LineAuthentication.PersonalInfoRequest (access_token);
  LineAuthentication.GetPersonalInfo (request, OnPersonalInfoAcquired);
}

23.6. Nexon Launcher

23.6.1. 클라이언트 인증 유효성 검사하기

다음은 아이펀 엔진에서 클라이언트가 로그인 요청을 했을 때 어떻게 Nexon Launcher 인증을 처리하는지 예제를 설명합니다.

인터페이스는 /usr/include/funapi/service/nexon_platform_authentication.h 에서 확인할 수 있습니다.

동기 방식

void OnLogin(const Ptr<Session> &session, const Json &message) {
  const string client_ticket = message["ticket"].GetString();
  if (client_ticket.empty()) {
    // error
    return;
  }

  NexonPlatformAuthenticationRequest request(client_ticket);

  NexonPlatformAuthenticationResponse response;

  if (not AuthenticateSync(request, &response)) {
    LOG(ERROR) << "Authentication service error.";
    return;
  }

  if (not response.success) {
    LOG(ERROR) << "Authentication failed. "
               << "error_code=" << response.error.code
               << ", error_type=" << response.error.type
               << ", error_message=" << response.error.message;
    return;
  }

  LOG(INFO) << "Authentication succeeded. "
            << "user_no=" << response.user_no
            << ", token=" << response.token
            << ", refresh_token=" << response.refresh_token
            << ", expires_in=" << response.expires_in
            << ", extra_data=" << response.extra_data.ToString();
}
public static void OnLogin (Session session, JObject message)
{
  String client_ticket = message ["client_ticket"].ToString ();
  if (String.IsNullOrEmpty (client_ticket)) {
    return;
  }

  NexonPlatformAuthentication.AuthenticationRequest request =
    new NexonPlatformAuthentication.AuthenticationRequest (client_ticket);

  NexonPlatformAuthentication.AuthenticationResponse response;
  if (!NexonPlatformAuthentication.AuthenticateSync (request, out response)) {
    Log.Error ("Failed to verify the authentication.");
    return;
  }

  Log.Info ("Verifying the authentication succeeded. user_no: {0}, token: {1}, refresh_token: {2}",
            response.UserNo, response.Token, response.RefreshToken);
  Log.Info ("Extra data: {0}", response.ExtraData.ToString ());
}

비동기 방식

void OnAuthenticated(const NexonPlatformAuthenticationRequest &request,
                     const NexonPlatformAuthenticationResponse &response,
                     bool error) {
  if (error) {
    LOG(ERROR) << "Authentication service error.";
    return;
  }

  if (not response.success) {
    LOG(ERROR) << "Authentication failed. "
               << "error_code=" << response.error.code
               << ", error_type=" << response.error.type
               << ", error_message=" << response.error.message;
    return;
  }

  LOG(INFO) << "Authentication succeeded. "
            << "user_no=" << response.user_no
            << ", token=" << response.token
            << ", refresh_token=" << response.refresh_token
            << ", expires_in=" << response.expires_in
            << ", extra_data=" << response.extra_data.ToString();
}


void OnLogin(const Ptr<Session> &session, const Json &message) {
  const string client_ticket = message["ticket"].GetString();
  if (client_ticket.empty()) {
    // error
    return;
  }

  NexonPlatformAuthenticationRequest request(client_ticket);
  Authenticate(request, OnAuthenticated);
}
static void OnAuthenticated (NexonPlatformAuthentication.AuthenticationRequest request,
                             NexonPlatformAuthentication.AuthenticationResponse response,
                             bool error)
{
  if (error || !response.Success) {
    Log.Error ("Failed to verify the authentication.");
    return;
  }

  Log.Info ("Verifying the authentication succeeded. user_no: {0}, token: {1}, refresh_token: {2}",
            response.UserNo, response.Token, response.RefreshToken);
  Log.Info ("Extra data: {0}", response.ExtraData.ToString ());
}


public static void OnLogin (Session session, JObject message)
{
  String client_ticket = message ["client_ticket"].ToString ();
  if (String.IsNullOrEmpty (client_ticket)) {
    return;
  }

  NexonPlatformAuthentication.AuthenticationRequest request =
    new NexonPlatformAuthentication.AuthenticationRequest (client_ticket);
  NexonPlatformAuthentication.Authenticate (request, OnAuthenticated);
}

23.7. Nexon Toy

23.7.1. 클라이언트 인증 유효성 검사하기

다음은 아이펀 엔진에서 클라이언트가 로그인 요청을 했을 때 어떻게 Nexon Toy 인증을 처리하는지 예제를 설명합니다.

인터페이스는 /usr/include/funapi/service/nexon_toy_authentication.h 에서 확인할 수 있습니다.

동기 방식

void OnLogin(const Ptr<Session> &session, const Json &message) {
  const int64_t svc_id = message["svc_id"].GetInteger();
  const string client_id = message["client_id"].GetString();
  const string np_sn = message["np_sn"].GetString();
  const string np_token = message["np_token"].GetString();

  if (client_id.empty() || np_sn.empty() || np_token.empty()) {
    // error
    return;
  }

  NexonToyAuthenticationRequest request(svc_id, client_id, np_sn, np_token);

  NexonToyAuthenticationResponse response;

  if (not AuthenticateSync(request, &response)) {
    LOG(ERROR) << "Authentication service error.";
    return;
  }

  if (not response.success) {
    LOG(ERROR) << "Authentication failed. "
               << "error_code=" << response.error.code
               << ", error_message" << response.error.message;
    return;
  }

  LOG(INFO) << "Authentication succeeded.";
}
public static void OnLogin (Session session, JObject message)
{
  long svc_id = long.Parse (message ["svc_id"].ToString ());
  if (svc_id == 0) {
    return;
  }

  String client_id = message ["client_id"].ToString ();
  if (String.IsNullOrEmpty (client_id)) {
    return;
  }

  String np_sn = message ["np_sn"].ToString ();
  if (String.IsNullOrEmpty (np_sn)) {
    return;
  }

  String np_token = message ["np_token"].ToString ();
  if (String.IsNullOrEmpty (np_token)) {
    return;
  }

  NexonToyAuthentication.AuthenticationRequest request =
    new NexonToyAuthentication.AuthenticationRequest (svc_id, client_id, np_sn, np_token);

  NexonToyAuthentication.AuthenticationResponse response;
  if (!NexonToyAuthentication.AuthenticateSync (request, out response)) {
    Log.Error ("Failed to verify the authentication.");
    return;
  }

  if (!response.Success) {
    Log.Error ("Failed to verify the authentication. error.code: {0}, error_message = {1}",
               response.Error.Code, response.Error.Message);
    return;
  }

  Log.Info ("Verifying the authentication succeeded.");
}

비동기 방식

void OnAuthenticated(const NexonToyAuthenticationRequest &request,
                     const NexonToyAuthenticationResponse &response,
                     bool error) {
  if (error) {
    LOG(ERROR) << "Authentication service error.";
    return;
  }

  if (not response.success) {
    LOG(ERROR) << "Authentication failed. "
               << "error_code=" << response.error.code
               << ", error_message" << response.error.message;
    return;
  }

  LOG(INFO) << "Authentication succeeded.";
}


void OnLogin(const Ptr<Session> &session, const Json &message) {
  const int64_t svc_id = message["svc_id"].GetInteger();
  const string client_id = message["client_id"].GetString();
  const string np_sn = message["np_sn"].GetString();
  const string np_token = message["np_token"].GetString();

  if (client_id.empty() || np_sn.empty() || np_token.empty()) {
    // error
    return;
  }

  NexonToyAuthenticationRequest request(svc_id, client_id, np_sn, np_token);
  Authenticate(request, OnAuthenticated);
}
static void OnAuthenticated (NexonToyAuthentication.AuthenticationRequest request,
                             NexonToyAuthentication.AuthenticationResponse response,
                             bool error)
{
  if (error || !response.Success) {
    Log.Error ("Failed to verify the authentication. error.code: {0}, error_message = {1}",
               response.Error.Code, response.Error.Message);
  }

  Log.Info ("Verifying the authentication succeeded.");
}


public static void OnLogin (Session session, JObject message)
{
  long svc_id = long.Parse (message ["svc_id"].ToString ());
  if (svc_id == 0) {
    return;
  }

  String client_id = message ["client_id"].ToString ();
  if (String.IsNullOrEmpty (client_id)) {
    return;
  }

  String np_sn = message ["np_sn"].ToString ();
  if (String.IsNullOrEmpty (np_sn)) {
    return;
  }

  String np_token = message ["np_token"].ToString ();
  if (String.IsNullOrEmpty (np_token)) {
    return;
  }

  NexonToyAuthentication.AuthenticationRequest request =
    new NexonToyAuthentication.AuthenticationRequest (svc_id, client_id, np_sn, np_token);
  NexonToyAuthentication.Authenticate (request, OnAuthenticated);
}

23.8. Kakao Game

카카오 게임은 라이센스 문제로 엔진과 함께 배포하지 않습니다. 카카오톡 인증 방법은 GitHub의 아이펀 엔진과 카카오 게임 연동 예제 를 참고해주세요.

23.9. Apple로 로그인

23.9.1. 클라이언트 인증 유효성 검사하기

다음은 아이펀 엔진에서 클라이언트가 로그인 요청을 했을 때 어떻게 Apple로 로그인 인증을 처리하는지 예제를 설명합니다.

인터페이스는 /usr/include/funapi/service/sign_in_with_apple_authentication.h 에서 확인할 수 있습니다.

비동기 방식

// 앱 아이디
const string app_id = "...";

void OnAuthenticated(const SignInWithAppleAuthenticationRequest &request,
                     const SignInWithAppleAuthenticationResponse &response,
                     bool error) {
  if (error) {
    LOG(ERROR) << "Authentication service error.";
    return;
  }

  if (not response.success) {
    LOG(ERROR) << "Authentication failed. "
               << "error_code=" << response.error_code
               << ", error_message" << response.error_message;
    return;
  }

  LOG(INFO) << "Authentication succeeded.";
}


void OnLogin(const Ptr<Session> &session, const Json &message) {
  const string id_token = message["id_token"].GetString();
  if (id_token.empty()) {
    // error.
    return;
  }

  SignInWithAppleAuthenticationRequest request(app_id, id_token);
  Authenticate(request, OnAuthenticated);
}
// 앱 아이디
const String app_id = "...";

static void OnAuthenticated (SignInWithAppleAuthentication.AuthenticationRequest request,
                             SignInWithAppleAuthentication.AuthenticationResponse response,
                             bool error)
{
  if (error || !response.Success) {
    Log.Error ("Failed to verify the authentication. error_code: {0}, error_description = {1}",
               response.ErrorCode, response.ErrorDescription);
    return;
  }

  Log.Info ("Verifying the authentication succeeded.");
}

public static void OnLogin (Session session, JObject message)
{
  String id_token = message ["id_token"].ToString ();
  if (String.IsNullOrEmpty (id_token)) {
    return;
  }

  SignInWithAppleAuthentication.AuthenticationRequest request =
      new SignInWithAppleAuthentication.AuthenticationRequest (app_id, id_token);
  SignInWithAppleAuthentication.Authenticate (request, OnAuthenticated);
}

23.10. 인증 검증 서비스 설정 파라미터

아래의 설명과 Configuration file (MANIFEST.json) details 를 참고하여 AuthenticationService 관련 설정을 합니다.

  • use_authentication_service: 인증 검증 서비스를 활성화합니다. 간단히 테스트 또는 개발 단계에서는 false 로 지정하면 bypass 하고 모든 인증 요청을 성공으로 처리합니다. (type=bool, default=false)

  • nexon_platform: Nexon Platform 인증 처리를 위한 JSON Object 입니다. 아래와 같은 JSON Attribute 를 입력합니다.

    • secret_key: 발급받은 secret key 를 JSON String 으로 입력합니다. (type=string, default=””)