now.gg 로그인을 안드로이드앱에 적용하고 설정한 후 now.gg 로그인 버튼을 추가합니다.
먼저 필수 요소 체크리스트를 완료하고 개발자 환경을 설정했는지 확인합니다.
먼저 AndroidManifest.xml
에 권한을 추가하세요.
<uses-permission android:name="android.permission.GET_ACCOUNTS"/> <uses-permission android:name="android.permission.INTERNET"/>
다음 의존성을 애플리케이션의 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'
now.gg 로그인 버튼 에셋은 now.gg SDK 패키지의 Assets/Login
디렉토리에 포함되어 있습니다.
Button signInButton = findViewById(R.id.now_gg_login_button);
Android 활동의 onCreate
메서드에서 로그인 버튼에 대한 OnClickListener
를 추가합니다.
findViewById(R.id.now_gg_login_button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { signIn(); } });
이제 GET_ACCOUNTS
권한과 결과를 확인해야 합니다.
View.OnClickListener
를 추가하고 GET_ACCOUNTS
권한을 확인합니다.private final int GET_ACCOUNTS_REQUEST_CODE = 100; public class MainActivity extends AppCompatActivity implements View.OnClickListener { }
@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"); } }
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 계정으로 로그인하기’를 위한 것입니다. 로그인 버튼 클릭 시 트리거되어야 합니다.
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"); } } };
사용자가 로그인한 후, 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(); } } }
이제 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 클래스에 대한 자세한 내용은 프로필 정보 호출을 참조하시기 바랍니다.
중요:
signIn
을 다시 호출하여 새 토큰을 요청할 수 있습니다.이 섹션에서는 사용자를 위한 로그아웃 구현 방법을 설명합니다.
사용자 로그아웃을 수행하려면:
다음 섹션에서는 검색된 사용자 프로필 정보를 사용하는 과정을 설명합니다.
id_token
을 부여받게 되고 이후 OnTokenAcquired
에서 받은 유저 세부 정보를 얻으려면 verifyToken
을 호출하게 됩니다.앱에 백엔드 서버가 없는 경우 다음을 통해 유저 프로필 정보를 가져올 수 있습니다.
로그인한 유저의 프로필 정보를 가져오려면 verifyToken
을 호출 후 받은 TokenVerifyResponse
의 userDataVerified
필드를 사용하실 수 있습니다.
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 + '\'' + '}'; } }
userData.getUserId()
를 사용하여 클라이언트에서 userId
를 가져올 수 있습니다.userId
를 가져오실 수 있습니다.유저 계정 서비스
유저 계정 서비스
문서 Rev. 1.0