연결된 계정 로그인을 사용하면 이미 Google 계정이 서비스에 연결된 사용자에게 Google 원탭 로그인을 사용할 수 있습니다. 이렇게 하면 사용자 이름과 비밀번호를 다시 입력하지 않고도 클릭 한 번으로 로그인할 수 있으므로 사용자 환경이 개선됩니다. 또한 사용자가 서비스에서 중복 계정을 만들 가능성도 줄어듭니다.
연결된 계정 로그인은 Android의 원탭 로그인 흐름의 일부로 사용할 수 있습니다. 즉, 앱에 이미 원탭 기능이 사용 설정되어 있는 경우 별도의 라이브러리를 가져올 필요가 없습니다.
이 문서에서는 연결된 계정 로그인을 지원하도록 Android 앱을 수정하는 방법을 설명합니다.
작동 방식
- 원탭 로그인 과정에서 연결된 계정을 표시하도록 선택합니다.
- 사용자가 Google에 로그인되어 있고 Google 계정을 서비스의 계정과 연결한 경우 연결된 계정에 대한 ID 토큰이 반환됩니다.
- 연결된 계정으로 서비스에 로그인할 수 있는 옵션이 포함된 원탭 로그인 메시지가 사용자에게 표시됩니다.
- 사용자가 연결된 계정으로 계속 진행하면 사용자의 ID 토큰이 앱에 반환됩니다. 이 토큰을 2단계에서 서버로 전송된 토큰과 일치시켜 로그인한 사용자를 식별합니다.
설정
개발 환경 설정
개발 호스트에서 최신 Google Play 서비스를 가져옵니다.
- Android SDK Manager를 엽니다.
SDK Tools에서 Google Play 서비스를 찾습니다.
이러한 패키지의 상태가 '설치됨'이 아닌 경우 두 패키지를 모두 선택하고 패키지 설치를 클릭합니다.
앱 구성
프로젝트 수준
build.gradle
파일의buildscript
및allprojects
섹션에 Google의 Maven 저장소가 포함되어야 합니다.buildscript { repositories { google() } } allprojects { repositories { google() } }
모듈의 앱 수준 Gradle 파일(일반적으로
app/build.gradle
)에 'Link with Google' API의 종속 항목을 추가합니다.dependencies { implementation 'com.google.android.gms:play-services-auth:21.3.0' }
연결된 계정 로그인을 지원하도록 Android 앱 수정
연결된 계정 로그인 흐름이 끝나면 ID 토큰이 앱에 반환됩니다. 사용자를 로그인하기 전에 ID 토큰의 무결성을 확인해야 합니다.
다음 코드 샘플은 ID 토큰을 가져와 확인한 후 사용자를 로그인하는 단계를 자세히 보여줍니다.
로그인 인텐트의 결과를 수신하는 활동 만들기
Kotlin
private val activityResultLauncher = registerForActivityResult( ActivityResultContracts.StartIntentSenderForResult()) { result -> if (result.resultCode == RESULT_OK) { try { val signInCredentials = Identity.signInClient(this) .signInCredentialFromIntent(result.data) // Review the Verify the integrity of the ID token section for // details on how to verify the ID token verifyIdToken(signInCredential.googleIdToken) } catch (e: ApiException) { Log.e(TAG, "Sign-in failed with error code:", e) } } else { Log.e(TAG, "Sign-in failed") } }
자바
private final ActivityResultLauncher<IntentSenderResult> activityResultLauncher = registerForActivityResult( new ActivityResultContracts.StartIntentSenderForResult(), result -> { If (result.getResultCode() == RESULT_OK) { try { SignInCredential signInCredential = Identity.getSignInClient(this) .getSignInCredentialFromIntent(result.getData()); verifyIdToken(signInCredential.getGoogleIdToken()); } catch (e: ApiException ) { Log.e(TAG, "Sign-in failed with error:", e) } } else { Log.e(TAG, "Sign-in failed") } });
로그인 요청 빌드
Kotlin
private val tokenRequestOptions = GoogleIdTokenRequestOptions.Builder() .supported(true) // Your server's client ID, not your Android client ID. .serverClientId(getString("your-server-client-id") .filterByAuthorizedAccounts(true) .associateLinkedAccounts("service-id-of-and-defined-by-developer", scopes) .build()
자바
private final GoogleIdTokenRequestOptions tokenRequestOptions = GoogleIdTokenRequestOptions.Builder() .setSupported(true) .setServerClientId("your-service-client-id") .setFilterByAuthorizedAccounts(true) .associateLinkedAccounts("service-id-of-and-defined-by-developer", scopes) .build()
로그인 대기 중 인텐트 실행
Kotlin
Identity.signInClient(this) .beginSignIn( BeginSignInRequest.Builder() .googleIdTokenRequestOptions(tokenRequestOptions) .build()) .addOnSuccessListener{result -> activityResultLauncher.launch(result.pendingIntent.intentSender) } .addOnFailureListener {e -> Log.e(TAG, "Sign-in failed because:", e) }
자바
Identity.getSignInClient(this) .beginSignIn( BeginSignInRequest.Builder() .setGoogleIdTokenRequestOptions(tokenRequestOptions) .build()) .addOnSuccessListener(result -> { activityResultLauncher.launch( result.getPendingIntent().getIntentSender()); }) .addOnFailureListener(e -> { Log.e(TAG, "Sign-in failed because:", e); });
ID 토큰의 무결성 확인
Google API 클라이언트 라이브러리 사용
프로덕션 환경에서 Google ID 토큰을 확인하는 권장 방법은 Java Google API 클라이언트 라이브러리를 사용하는 것입니다.
자바
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken.Payload;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier;
...
GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(transport, jsonFactory)
// Specify the CLIENT_ID of the app that accesses the backend:
.setAudience(Collections.singletonList(CLIENT_ID))
// Or, if multiple clients access the backend:
//.setAudience(Arrays.asList(CLIENT_ID_1, CLIENT_ID_2, CLIENT_ID_3))
.build();
// (Receive idTokenString by HTTPS POST)
GoogleIdToken idToken = verifier.verify(idTokenString);
if (idToken != null) {
Payload payload = idToken.getPayload();
// Print user identifier
String userId = payload.getSubject();
System.out.println("User ID: " + userId);
// Get profile information from payload
String email = payload.getEmail();
boolean emailVerified = Boolean.valueOf(payload.getEmailVerified());
String name = (String) payload.get("name");
String pictureUrl = (String) payload.get("picture");
String locale = (String) payload.get("locale");
String familyName = (String) payload.get("family_name");
String givenName = (String) payload.get("given_name");
// Use or store profile information
// ...
} else {
System.out.println("Invalid ID token.");
}
GoogleIdTokenVerifier.verify()
메서드는 JWT 서명, aud
클레임, iss
클레임, exp
클레임을 확인합니다.
ID 토큰이 Google Workspace 또는 Cloud 조직 계정을 나타내는지 확인해야 하는 경우 Payload.getHostedDomain()
메서드에서 반환된 도메인 이름을 확인하여 hd
클레임을 확인할 수 있습니다.