네이티브 안드로이드용 로그인 모듈

now.gg 로그인을 안드로이드앱에 적용하고 설정한 후 now.gg 로그인 버튼을 추가합니다.

시작 전

먼저 필수 요소 체크리스트를 완료하고 개발자 환경을 설정했는지 확인합니다.

권한, 종속성 및 자산 추가

1. 권한 추가하기

먼저 AndroidManifest.xml에 권한을 추가하세요.

 <uses-permission android:name="android.permission.GET_ACCOUNTS"/>
  <uses-permission android:name="android.permission.INTERNET"/> 

2. 의존성 추가하기

다음 의존성을 애플리케이션의 build.gradle 파일에 추가하세요:

// retrofit
 implementation 'com.squareup.retrofit2:retrofit:2.9.0'
 implementation 'com.squareup.okhttp3:logging-interceptor:3.8.0'
 implementation 'com.squareup.retrofit2:converter-scalars:2.9.0'
 implementation 'com.squareup.retrofit2:converter-gson:2.9.0'

3. now.gg 로그인 버튼 표시

3.1 now.gg 로그인 버튼 에셋 다운로드

now.gg 로그인 버튼 에셋은 now.gg SDK 패키지Assets/Login 디렉토리에 포함되어 있습니다.

3.2 로그인 버튼 옵션 선택
  • 권장 로그인 버튼: 표준 버튼 에셋인 기본 버튼을 사용하실 수 있습니다.
  • 커스텀 로그인 버튼: 제공되는 로고 에셋과 사용자 정의 텍스트를 사용해 앱의 스타일에 적당한 버튼을 디자인하여 “now.gg 로그인” 버튼을 생성하실 수 있습니다.
3.3 애플리케이션 레이아웃에 ‘now.gg 로그인 버튼’ 추가:
Button signInButton = findViewById(R.id.now_gg_login_button);


로그인 프로세스 시작

1. 로그인 버튼에 OnClick 이벤트 추가

Android 활동의 onCreate 메서드에서 로그인 버튼에 대한 OnClickListener를 추가합니다.

 findViewById(R.id.now_gg_login_button).setOnClickListener(new View.OnClickListener() {
     @Override
     public void onClick(View view) {
         signIn();
     }
 });

2. 권한 및 결과 확인하기

이제 GET_ACCOUNTS 권한과 결과를 확인해야 합니다.

메인 액티비티에 View.OnClickListener를 추가하고 GET_ACCOUNTS 권한을 확인합니다.
private final int GET_ACCOUNTS_REQUEST_CODE = 100;
 public class MainActivity extends AppCompatActivity implements View.OnClickListener {
 }
  • To check for GET_ACCOUNTS permission, add the following code to your onCreate method:
    @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
     
         if (checkSelfPermission(Manifest.permission.GET_ACCOUNTS) != PackageManager.PERMISSION_GRANTED) {
             requestPermissions(new String[] {
                     Manifest.permission.GET_ACCOUNTS
                 },
                 GET_ACCOUNTS_REQUEST_CODE);
         } else {
             // GET_ACCOUNTS 권한이 있으므로 로그인을 진행할 수 있습니다.
             Log.d(TAG, "onCreate: Already has permission, proceed with signin");
         }
     }
    
  • To check for results, add the following code to your onRequestPermissionsResult:
    @Override
     public void onRequestPermissionsResult(int requestCode,
         String permissions[], int[] grantResults) {
         super.onRequestPermissionsResult(requestCode, permissions, grantResults);
         if (requestCode == GET_ACCOUNTS_REQUEST_CODE) {
             if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                 // 권한이 부여되어 로그인을 진행할 수 있습니다.
                 signIn();
             }
         }
     }
    

유저 및 세션 정보 호출

  • 또한 now.gg 로그인은 더욱 안전한 고급 인증 코드 프로세스(Auth Code Flow)를 제공합니다.
    • 이를 통해 자세한 유저 및 세션 정보를 얻을 수 있습니다.
    • 위 단계를 완료한 후 여기로 이동하여 인증 코드 프로세스를 사용하여 now.gg 로그인을 적용합니다.
  • 유저가 로그인한 후에 기본 프로필 정보만 가져오려면 아래 단계를 확인해 보시기 바랍니다.

기본 로그인 프로세스


3. 로그인 수행

다음 코드 샘플은 ‘now.gg 계정으로 로그인하기’를 위한 것입니다. 로그인 버튼 클릭 시 트리거되어야 합니다.

 public static final String ID_TOKEN = "id_token"; // 변경 금지
  public static final String CLIENT_ID = "<your client id>"; // 사용 클라이언트 ID로 변경
  public static final String ACCOUNT_TYPE = "now.gg"; // 변경 금지
  public static final String HOST_URL = "hostUrl"; // 변경 금지

  private void signIn() {
     Account account = getNowggAccount();
     if (account != null) {
         Bundle bundle = new Bundle();
         bundle.putString("client_id", CLIENT_ID);
         String authTokenType = ID_TOKEN;
         AccountManager.get(getApplicationContext()).
                 getAuthToken(account, authTokenType, bundle, MainActivity.this, new OnTokenAcquired(), null);
     }
     else {
         addNowggAccount();
     }
  }

  private Account getNowggAccount() {
     Account[] accounts = AccountManager.get(getApplicationContext()).getAccountsByType(ACCOUNT_TYPE);
     if (accounts.length > 0) {
         Log.d(TAG, "getNowggAccount: account found");
         // 현재 하나의 now.gg 계정만 시스템에 추가할 수 있습니다.
         return accounts[0];
     }
     return null;
  }

  private void addNowggAccount() {
     try {
         Intent intent = new Intent();
         intent.setComponent(new ComponentName("gg.now.accounts", "gg.now.accounts.AuthenticatorActivity"));
         intent.setAction("IAP_ADD_ACCOUNT");
         intent.putExtra("resultReceiver", parcelResultReceiver(resultReceiver));
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         startActivity(intent);
     } catch (ActivityNotFoundException e) {
         e.printStackTrace();
     }
  }

  ResultReceiver parcelResultReceiver(ResultReceiver actualReceiver) {
      Parcel parcel = Parcel.obtain();
      actualReceiver.writeToParcel(parcel, 0);
      parcel.setDataPosition(0);
      ResultReceiver receiverForSending = ResultReceiver.CREATOR.createFromParcel(parcel);
      parcel.recycle();
      return receiverForSending;
  }

  ResultReceiver resultReceiver = new ResultReceiver(new Handler(Looper.getMainLooper())) {
      @Override
      protected void onReceiveResult(int resultCode, Bundle resultData) {
          super.onReceiveResult(resultCode, resultData);
          Log.d(TAG, "onReceiveResult() called with: resultCode = [" + resultCode + "], resultData = [" + resultData.toString() + "]");
          // onReceiveResult
          if (resultCode == 0) { // means sign in is okay
              signIn();
          } else {
              // sign in failed
             Log.d(TAG, "onReceiveResult: sign in failed");
          }
      }
  };

4. 요청된 토큰 받기

사용자가 로그인한 후, OnTokenAcquired 콜백 함수에서 요청된 토큰 (id_token)을 받을 수 있습니다. 이 토큰은 토큰을 검증하거나 백엔드 서버로 토큰을 전달하여 사용자의 세부 정보를 가져오는 데 사용할 수 있습니다. 서버로부터 응답으로 사용자 세부 정보를 받을 수 있습니다.

      
 private class OnTokenAcquired implements AccountManagerCallback {

 @Override
  public void run(AccountManagerFuture result) {
  try {
       Bundle bundle = (Bundle) result.getResult();
       boolean success = bundle.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);
         if (success) {
            final String token = bundle.getString(ID_TOKEN);
            // 유저 세부 정보를 얻고 토큰을 인증할 수 있는 now.gg 서버 URL을 가져옵니다.

            final String hostUrl = bundle.getString(HOST_URL);
            verifyToken(token, hostUrl);
            }
            else {
                 // 토큰 가져오기 실패
                 // 이때 오류를 표시하거나 다른 로그인 방식을 대신 표시할 수 있습니다.
                 Log.d(TAG, "run: get token failed " + bundle);
                 }
          } catch (AuthenticatorException | IOException | OperationCanceledException e) {
            e.printStackTrace();
          }
     }
 }

5. 토큰 검증 및 사용자 세부 정보 가져오기

이제 OnTokenAcquired에서 받은 id_token의 무결성을 verifyToken 함수를 호출하여 검증하고, 아래와 같이 사용자 세부 정보를 가져와야 합니다:

     
  private void verifyToken(String idToken, String hostUrl) {
  BackendApiCallService backendApiCallService = getBackendRetrofit(hostUrl).create(BackendApiCallService.class);

  Call < TokenVerifyResponse > tokenVerifyResponseCall = backendApiCallService.verifyIdToken(new TokenVerifyRequest(ID_TOKEN, idToken, CLIENT_ID));

  tokenVerifyResponseCall.enqueue(new Callback < TokenVerifyResponse > () {
    @Override
    public void onResponse(Call < TokenVerifyResponse > call, Response < TokenVerifyResponse > response) {
      if (response.isSuccessful()) {
        Log.d(TAG, "onResponse: " + response.body().toString());
      } else {
        Gson gson = new Gson();
        TokenVerifyResponse error = gson.fromJson(response.errorBody().charStream(), TokenVerifyResponse.class);

        if (!error.isSuccess() && ("EXPIRED_TOKEN".equals(error.getCode()) || "INVALID_TOKEN".equals(error.getCode()))) {
          // retry the verify token call, after getting a new TOKEN
        }
      }
    }
    @Override
    public void onFailure(Call call, Throwable t) {
      Log.d(TAG, "onFailure() called with: call = [" + call + "], t = [" + t + "]");
    }
  });

}


 public Retrofit getBackendRetrofit(String hostUrl) {
   OkHttpClient okHttpClient = new OkHttpClient.Builder().build();
   return new Retrofit.Builder()
     .baseUrl(hostUrl)
     .addConverterFactory(ScalarsConverterFactory.create())
     .addConverterFactory(GsonConverterFactory.create())
     .client(okHttpClient)
     .build();
 }

 public interface BackendApiCallService {
   @POST("/accounts/oauth2/v1/verify-token")
   Call < TokenVerifyResponse > verifyIdToken(@Body TokenVerifyRequest authRequest);
 }

 public class TokenVerifyRequest {
   String token_type;
   String token;
   String client_id;

   public TokenVerifyRequest(String token_type, String token, String client_id) {
     this.token_type = token_type;
     this.token = token;
     this.client_id = client_id;
   }
 }

 public class TokenVerifyResponse {
   boolean success;
   String code;
   String msg;
   @SerializedName("decodedData")
   UserDataVerified userDataVerified;

   public TokenVerifyResponse(boolean success, String code, String msg, UserDataVerified userDataVerified) {
     this.success = success;
     this.code = code;
     this.msg = msg;
     this.userDataVerified = userDataVerified;
   }

   public UserDataVerified getUserDataVerified() {
     return userDataVerified;
   }

   public boolean isSuccess() {
     return success;
   }
   public String getCode() {
    return code;
   }

   @Override
   public String toString() {
     return "TokenVerifyResponse{" +
       "success=" + success +
       ", code='" + code + '\'' +
       ", msg='" + msg + '\'' +
       ", userDataVerified=" + userDataVerified +
       '}';
   }
 }

UserDataVerified 클래스에 대한 자세한 내용은 프로필 정보 호출을 참조하시기 바랍니다.

중요:

  • EXPIRED_TOKEN 또는 INVALID_TOKEN과 같은 응답을 받을 시 signIn을 다시 호출하여 새 토큰을 요청할 수 있습니다.
  • 현재 로그인된 유저를 백엔드 서버에 전달하려면 ID 토큰을 백엔드 서버로 보내고 토큰을 인증합니다. (백엔드 서버 인증)
  • 사용자가 로그인하면 로그인 정보를 앱의 로컬 데이터베이스 또는 공유 환경설정에 저장하여, 이후 앱을 실행할 때 로그인 흐름을 다시 트리거하지 않고 자동으로 게임을 계속할 수 있도록 합니다.

6. 사용자 로그아웃 구현 -선택 사항

이 섹션에서는 사용자를 위한 로그아웃 구현 방법을 설명합니다.

사용자 로그아웃을 수행하려면:

  • 앱의 로컬 데이터베이스/공유 환경설정에서 저장된 로그인 정보를 제거하고 로그인 화면을 다시 표시합니다.
  • 로그아웃 후, 사용자는 게스트로 계속 플레이하거나 게임에서 제공하는 다른 로그인 옵션을 사용할 수 있습니다.



프로필 정보 사용

다음 섹션에서는 검색된 사용자 프로필 정보를 사용하는 과정을 설명합니다.

now.gg 계정으로 사용자를 로그인하면 id_token을 부여받게 되고 이후 OnTokenAcquired에서 받은 유저 세부 정보를 얻으려면 verifyToken을 호출하게 됩니다.
앱에 백엔드 서버가 있고 로그인한 유저의 프로필을 백엔드로 보내려는 경우 여기를 참조하시기 바랍니다.

주의

  • 현재 로그인한 유저를 앱의 백엔드 서버에 전달하기 위해 유저의 이메일 주소나 ID를 사용하시면 안 되며 대신 유저의 ID 토큰을 백엔드 서버로 보내고 토큰을 인증하시기 바랍니다.

앱에 백엔드 서버가 없는 경우 다음을 통해 유저 프로필 정보를 가져올 수 있습니다.

프로필 정보 호출

로그인한 유저의 프로필 정보를 가져오려면 verifyToken을 호출 후 받은 TokenVerifyResponseuserDataVerified 필드를 사용하실 수 있습니다.

UserDataVerified userData = tokenVerifyResponse.getUserDataVerified();
 String userId = userData.getUserId();
 String name = userData.getName();
 String email = userData.getEmail();
 String picture = userData.getPicture();
 
 public class UserDataVerified {
   String iss;
   String sub;
   String aud;
   String exp;
   String iat;
   String auth_time;
   String tokenId;
   String sessionId;
   String scope;

   String email;
   String name;
   String picture;
   String mobile;
   String userId;

   public UserDataVerified(String iss, String sub, String aud, String exp, String iat,
                           String auth_time, String email, String mobile, String userId,
                           String tokenId, String sessionId, String scope, String name, String picture) {
       this.iss = iss;
       this.sub = sub;
       this.aud = aud;
       this.exp = exp;
       this.iat = iat;
       this.auth_time = auth_time;
       this.email = email;
       this.mobile = mobile;
       this.userId = userId;
       this.tokenId = tokenId;
       this.sessionId = sessionId;
       this.scope = scope;
       this.name = name;
       this.picture = picture;
  }

   @Override
   public String toString() {
       return "UserDataVerified{" +
               "iss='" + iss + '\'' +
               ", sub='" + sub + '\'' +
               ", aud='" + aud + '\'' +
               ", exp='" + exp + '\'' +
               ", iat='" + iat + '\'' +
               ", auth_time='" + auth_time + '\'' +
               ", tokenId='" + tokenId + '\'' +
               ", sessionId='" + sessionId + '\'' +
               ", scope='" + scope + '\'' +
               ", email='" + email + '\'' +
               ", name='" + name + '\'' +
               ", picture='" + picture + '\'' +
               ", mobile='" + mobile + '\'' +
               ", userId='" + userId + '\'' +
               '}';
   }
 }         
유저 세부 정보가 있으면 해당 정보를 데이터베이스에 저장하고 다음에 유저가 앱을 시작할 때 로그인 프로세스를 건너뛰는 데 사용해야 합니다.

중요

  • 유저를 식별하기 위해 이메일 주소를 사용하지 않는 것이 좋습니다. 대신 userId를 사용하시는 것을 권장해 드립니다.
  • 앱에 백엔드 서버가 없으면 userData.getUserId()를 사용하여 클라이언트에서 userId를 가져올 수 있습니다.
  • 앱에 백엔드 서버가 있는 경우 토큰 인증 API를 호출하고 유저 데이터 응답 메시지를 통해 userId를 가져오실 수 있습니다.
×

유저 계정 서비스

네이티브 안드로이드용 로그인 모듈

유저 계정 서비스

문서 Rev. 1.0

클립보드에 복사된 텍스트
copyLinkText
질문이 있으신가요? 다음 주소로 문의하세요. dev-support@now.gg