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