21. 외부 서비스 지원 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 (카카오 게임)

Note

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

Warning

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

21.1. Apple Game Center

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

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

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

비동기 방식

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(
    message["bundle_id"].GetString(),
    message["player_id"].GetString(),
    message["public_key_url"].GetString(),
    // 클라이언트 측에서 signature, salt를 base64로 인코딩해서 보냈다고 가정합니다.
    message["signature"].GetString(),
    message["salt"].GetString(),
    message["timestamp"].GetInteger());

  Authenticate(request, OnAuthenticated);
}

Note

추후 지원 예정입니다.

21.2. Facebook

21.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);
}

21.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);
}

21.3. Google Play Game

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

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

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

비동기 방식

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. "
            << "app_id=" << response.app_id
            << ", name=" << response.name
            << ", picture=" << response.picture;
}


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

  GooglePlayGameAuthenticationRequest request(app_id, id_token);
  Authenticate(request, OnAuthenticated);
}
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}, app_id={2}",
            response.Name, response.Picture, response.AppId);
}

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

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

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

21.4. Google Plus

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

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

인터페이스는 /usr/include/funapi/service/google_plus_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;
  }

  GooglePlusAuthenticationRequest request(access_token);

  GooglePlusAuthenticationResponse 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
            << ", display_name=" << response.display_name
            << ", image_url=" << response.image_url;
}
public static void OnLogin (Session session, JObject message)
{
  String access_token = message ["access_token"].ToString ();
  if (String.IsNullOrEmpty (access_token)) {
    return;
  }

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

  GooglePlusAuthentication.AuthenticationResponse response;
  if (!GooglePlusAuthentication.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. display_name={0}, client_id={1}, image_url={2}",
            response.DisplayName, response.ClientId, response.ImageUrl);
}

비동기 방식

void OnAuthenticated(const GooglePlusAuthenticationRequest &request,
                     const GooglePlusAuthenticationResponse &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
            << ", display_name=" << response.display_name
            << ", image_url=" << response.image_url;
}


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

  GooglePlusAuthenticationRequest request(access_token);
  Authenticate(request, OnAuthenticated);
}
static void OnAuthenticated (GooglePlusAuthentication.AuthenticationRequest request,
                             GooglePlusAuthentication.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);
    return;
  }

  Log.Info ("Verifying the authentication succeeded. display_name={0}, client_id={1}, image_url={2}",
            response.DisplayName, response.ClientId, response.ImageUrl);
}


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

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

21.4.2. 사용자 정보 요청하기

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

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

동기 방식

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

  GooglePlusPersonalInfoRequest request(access_token, client_id);

  GooglePlusPersonalInfoResponse 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;
  }

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

  GooglePlusAuthentication.PersonalInfoRequest request =
    new GooglePlusAuthentication.PersonalInfoRequest (access_token, client_id);

  GooglePlusAuthentication.PersonalInfoResponse response;
  if (!GooglePlusAuthentication.GetPersonalInfoSync (request, out response)) {
    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 GooglePlusPersonalInfoRequest &request,
                       const GooglePlusPersonalInfoResponse &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();
  const string client_id = message["client_id"].GetString();
  if (access_token.empty() || client_id.empty()) {
    // error
    return;
  }

  GooglePlusPersonalInfoRequest request(access_token, client_id);
  GetPersonalInfo(request, OnPersonalInfoGot);
}
static void OnPersonalInfoAcquired (GooglePlusAuthentication.PersonalInfoRequest request,
                                    GooglePlusAuthentication.PersonalInfoResponse response,
                                    bool error)
{
  if (error || !response.Success) {
    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);
  }
}


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

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

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

21.5. LINE

21.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);
}

21.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);
}

21.6. Nexon Launcher

21.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);
}

21.7. Nexon Toy

21.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);
}

21.8. Kakao Game

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

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

아래의 설명과 설정 파일 (MANIFEST.json) 상세 를 참고하여 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=””)
  • authentication_google_play_verify_token: Google Play 인증할 때 클라이언트가 보낸 토큰을 검증한 후에 Google API를 호출하게 합니다. (type=bool, default=true)
  • authentication_google_play_preload_jwks: Google Play 토큰을 검증할 때 사용할 공개키를 미리 가져오게 합니다. (type=bool, default=false)