Google 계정으로 로그인으로 사용자 인증

Google 계정으로 로그인을 사용하면 사용자 인증을 Android 앱과 빠르게 통합할 수 있습니다. 사용자는 Google 계정을 사용하여 앱에 로그인하고 동의한 후 앱과 프로필 정보를 안전하게 공유할 수 있습니다. Android의 인증 관리자 Jetpack 라이브러리를 사용하면 이러한 통합이 원활하게 이루어지므로 단일 API를 사용하여 Android 기기 전반에서 일관된 환경을 제공할 수 있습니다.

이 문서에서는 Android 앱에서 Google 계정으로 로그인을 구현하는 방법, Google 계정으로 로그인 버튼 UI를 설정하는 방법, 앱에 최적화된 원탭 등록 및 로그인 환경을 구성하는 방법을 안내합니다. 원활한 기기 이전을 위해 Google 계정으로 로그인은 자동 로그인을 지원하며 Android, iOS, 웹 표시 경로 전반에서의 크로스 플랫폼 특성 덕분에 모든 기기에서 앱에 대한 로그인 액세스를 제공할 수 있습니다.

Google 계정으로 로그인을 설정하려면 다음 두 가지 기본 단계를 따르세요.

Google 계정으로 로그인을 인증 관리자의 하단 시트 UI 옵션으로 구성 사용자에게 로그인하라는 메시지가 자동으로 표시되도록 구성할 수 있습니다. 패스키 또는 비밀번호를 구현한 경우 사용자는 이전에 로그인할 때 사용한 옵션을 기억하지 않아도 되도록 모든 관련 사용자 인증 정보 유형을 동시에 요청할 수 있습니다.

인증 관리자 하단 시트
그림 1. 인증 관리자 하단 시트의 사용자 인증 정보 선택 UI

앱의 UI에 Google 계정으로 로그인 버튼 추가 'Google 계정으로 로그인' 버튼을 사용하면 사용자가 기존 Google 계정을 사용하여 Android 앱에 가입하거나 로그인할 수 있는 간소화된 방법을 제공합니다. 사용자는 하단 시트 UI를 닫거나 가입 및 로그인에 Google 계정을 명시적으로 사용하려는 경우 Google 계정으로 로그인 버튼을 클릭합니다. 개발자의 경우 사용자 온보딩이 더 쉬워지고 가입 중 발생하는 불편이 줄어듭니다.

Google 계정으로 로그인 흐름을 보여주는 애니메이션
그림 2. 인증 관리자 Google 계정으로 로그인 버튼 UI

이 문서에서는 Google ID 도우미 라이브러리를 사용하여 Google 계정으로 로그인 버튼과 하단 시트 대화상자를 인증 관리자 API와 통합하는 방법을 설명합니다.

Google API 콘솔 프로젝트 설정

  1. API Console에서 프로젝트를 열거나 아직 프로젝트가 없는 경우 프로젝트를 만듭니다.
  2. OAuth 동의 화면 페이지에서 모든 정보가 완전하고 정확한지 확인합니다.
    1. 앱에 올바른 앱 이름, 앱 로고, 앱 홈페이지가 할당되어 있는지 확인합니다. 이러한 값은 가입 시 Google 계정으로 로그인 동의 화면과 서드 파티 앱 및 서비스 화면에 사용자에게 표시됩니다.
    2. 앱의 개인정보처리방침 및 서비스 약관 URL을 지정했는지 확인합니다.
  3. 인증 정보 페이지에서 앱의 Android 클라이언트 ID를 만듭니다(아직 없는 경우). 앱의 패키지 이름과 SHA-1 서명을 지정해야 합니다.
    1. 사용자 인증 정보 페이지로 이동합니다.
    2. 사용자 인증 정보 만들기 > OAuth 클라이언트 ID를 클릭합니다.
    3. Android 애플리케이션 유형을 선택합니다.
  4. 인증 정보 페이지에서 아직 만들지 않은 경우 새 '웹 애플리케이션' 클라이언트 ID를 만듭니다. 지금은 '승인된 JavaScript 출처' 및 '승인된 리디렉션 URI' 필드를 무시해도 됩니다. 이 클라이언트 ID는 Google 인증 서비스와 통신할 때 백엔드 서버를 식별하는 데 사용됩니다.
    1. 사용자 인증 정보 페이지로 이동합니다.
    2. 사용자 인증 정보 만들기 > OAuth 클라이언트 ID를 클릭합니다.
    3. 웹 애플리케이션 유형을 선택합니다.

종속 항목 선언

모듈의 build.gradle 파일에서 인증 관리자의 최신 버전을 사용하여 종속 항목을 선언합니다.

dependencies {
  // ... other dependencies

  implementation "androidx.credentials:credentials:<latest version>"
  implementation "androidx.credentials:credentials-play-services-auth:<latest version>"
  implementation "com.google.android.libraries.identity.googleid:googleid:<latest version>"
}

Google 로그인 요청 인스턴스화

구현을 시작하려면 Google 로그인 요청을 인스턴스화하세요. GetGoogleIdOption을 사용하여 사용자의 Google ID 토큰을 가져옵니다.

val googleIdOption: GetGoogleIdOption = GetGoogleIdOption.Builder()
  .setFilterByAuthorizedAccounts(true)
  .setServerClientId(WEB_CLIENT_ID)
  .setAutoSelectEnabled(true)
  .setNonce(<nonce string to use when generating a Google ID token>)
  .build()

먼저 setFilterByAuthorizedAccounts 매개변수를 true로 설정하여 API를 호출하여 사용자가 이전에 앱에 로그인하는 데 사용한 계정이 있는지 확인합니다. 사용자는 사용 가능한 계정 중에서 선택하여 로그인할 수 있습니다.

사용 가능한 승인된 Google 계정이 없는 경우 사용자에게 사용 가능한 계정으로 가입하라는 메시지가 표시됩니다. 이렇게 하려면 API를 다시 호출하고 setFilterByAuthorizedAccountsfalse로 설정하여 사용자에게 메시지를 표시합니다. 가입에 대해 자세히 알아보기

재방문 사용자의 자동 로그인 사용 설정 (권장)

개발자는 단일 계정으로 등록하는 사용자의 자동 로그인을 사용 설정해야 합니다. 이렇게 하면 특히 기기 이전 중에 사용자 인증 정보를 다시 입력하지 않고도 기기 간에 원활한 환경을 제공하여 사용자가 계정에 빠르게 다시 액세스할 수 있습니다. 이렇게 하면 사용자가 이전에 이미 로그인한 경우 불필요한 불편을 겪지 않아도 됩니다.

자동 로그인을 사용 설정하려면 setAutoSelectEnabled(true)를 사용합니다. 다음 기준을 충족하는 경우에만 자동 로그인이 가능합니다.

  • 요청과 일치하는 사용자 인증 정보가 하나 있습니다. 이 사용자 인증 정보는 Google 계정 또는 비밀번호일 수 있으며, 이 사용자 인증 정보는 Android 기기의 기본 계정과 일치합니다.
  • 사용자가 명시적으로 로그아웃하지 않았습니다.
  • 사용자가 Google 계정 설정에서 자동 로그인을 사용 중지하지 않았습니다.
val googleIdOption: GetGoogleIdOption = GetGoogleIdOption.Builder()
  .setFilterByAuthorizedAccounts(true)
  .setServerClientId(WEB_CLIENT_ID)
  .setAutoSelectEnabled(true)
  .setNonce(<nonce string to use when generating a Google ID token>)
  .build()

자동 로그인을 구현할 때는 사용자가 앱에서 명시적으로 로그아웃한 후 항상 적절한 계정을 선택할 수 있도록 로그아웃을 올바르게 처리해야 합니다.

보안을 강화하기 위해 nonce 설정

로그인 보안을 강화하고 재생 공격을 방지하려면 setNonce를 추가하여 각 요청에 nonce를 포함하세요. nonce 생성 자세히 알아보기

val googleIdOption: GetGoogleIdOption = GetGoogleIdOption.Builder()
  .setFilterByAuthorizedAccounts(true)
  .setServerClientId(WEB_CLIENT_ID)
  .setAutoSelectEnabled(true)
  .setNonce(<nonce string to use when generating a Google ID token>)
  .build()

Google 계정으로 로그인 흐름 만들기

Google 계정으로 로그인 흐름을 설정하는 단계는 다음과 같습니다.

  1. GetCredentialRequest를 인스턴스화한 다음 addCredentialOption()를 사용하여 이전에 만든 googleIdOption을 추가하여 사용자 인증 정보를 가져옵니다.
  2. 이 요청을 getCredential()(Kotlin) 또는 getCredentialAsync()(Java) 호출에 전달하여 사용자의 사용 가능한 사용자 인증 정보를 가져옵니다.
  3. API가 성공하면 GoogleIdTokenCredential 데이터의 결과가 포함된 CustomCredential을 추출합니다.
  4. CustomCredential의 유형은 GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL의 값과 같아야 합니다. GoogleIdTokenCredential.createFrom 메서드를 사용하여 객체를 GoogleIdTokenCredential로 변환합니다.
  5. 변환에 성공하면 GoogleIdTokenCredential ID를 추출하고 검증한 후 서버에서 사용자 인증 정보를 인증합니다.

  6. GoogleIdTokenParsingException과 함께 변환에 실패하면 Google 계정으로 로그인 라이브러리 버전을 업데이트해야 할 수도 있습니다.

  7. 인식할 수 없는 맞춤 사용자 인증 정보 유형을 포착합니다.

val request: GetCredentialRequest = Builder()
  .addCredentialOption(googleIdOption)
  .build()

coroutineScope.launch {
  try {
    val result = credentialManager.getCredential(
      request = request,
      context = activityContext,
    )
    handleSignIn(result)
  } catch (e: GetCredentialException) {
    handleFailure(e)
  }
}

fun handleSignIn(result: GetCredentialResponse) {
  // Handle the successfully returned credential.
  val credential = result.credential

  when (credential) {

    // Passkey credential
    is PublicKeyCredential -> {
      // Share responseJson such as a GetCredentialResponse on your server to
      // validate and authenticate
      responseJson = credential.authenticationResponseJson
    }

    // Password credential
    is PasswordCredential -> {
      // Send ID and password to your server to validate and authenticate.
      val username = credential.id
      val password = credential.password
    }

    // GoogleIdToken credential
    is CustomCredential -> {
      if (credential.type == GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL) {
        try {
          // Use googleIdTokenCredential and extract the ID to validate and
          // authenticate on your server.
          val googleIdTokenCredential = GoogleIdTokenCredential
            .createFrom(credential.data)
          // You can use the members of googleIdTokenCredential directly for UX
          // purposes, but don't use them to store or control access to user
          // data. For that you first need to validate the token:
          // pass googleIdTokenCredential.getIdToken() to the backend server.
          GoogleIdTokenVerifier verifier = ... // see validation instructions
          GoogleIdToken idToken = verifier.verify(idTokenString);
          // To get a stable account identifier (e.g. for storing user data),
          // use the subject ID:
          idToken.getPayload().getSubject()
        } catch (e: GoogleIdTokenParsingException) {
          Log.e(TAG, "Received an invalid google id token response", e)
        }
      } else {
        // Catch any unrecognized custom credential type here.
        Log.e(TAG, "Unexpected type of credential")
      }
    }

    else -> {
      // Catch any unrecognized credential type here.
      Log.e(TAG, "Unexpected type of credential")
    }
  }
}

Google 계정으로 로그인 버튼 흐름 트리거

Google 계정으로 로그인 버튼 흐름을 트리거하려면 GetGoogleIdOption 대신 GetSignInWithGoogleOption을 사용합니다.

val signInWithGoogleOption: GetSignInWithGoogleOption = GetSignInWithGoogleOption.Builder()
  .setServerClientId(WEB_CLIENT_ID)
  .setNonce(<nonce string to use when generating a Google ID token>)
  .build()

다음 코드 예에 설명된 대로 반환된 GoogleIdTokenCredential를 처리합니다.

fun handleSignIn(result: GetCredentialResponse) {
  // Handle the successfully returned credential.
  val credential = result.credential

  when (credential) {
    is CustomCredential -> {
      if (credential.type == GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL) {
        try {
          // Use googleIdTokenCredential and extract id to validate and
          // authenticate on your server.
          val googleIdTokenCredential = GoogleIdTokenCredential
            .createFrom(credential.data)
        } catch (e: GoogleIdTokenParsingException) {
          Log.e(TAG, "Received an invalid google id token response", e)
        }
      }
      else -> {
        // Catch any unrecognized credential type here.
        Log.e(TAG, "Unexpected type of credential")
      }
    }

    else -> {
      // Catch any unrecognized credential type here.
      Log.e(TAG, "Unexpected type of credential")
    }
  }
}

Google 로그인 요청을 인스턴스화한 후 Google 계정으로 로그인 섹션에서 언급한 것과 유사한 방식으로 인증 흐름을 시작합니다.

신규 사용자 가입 사용 설정 (권장)

Google 계정으로 로그인은 사용자가 몇 번만 탭하여 앱 또는 서비스에서 새 계정을 만드는 가장 쉬운 방법입니다.

저장된 사용자 인증 정보가 없으면 (getGoogleIdOption에서 반환된 Google 계정이 없음) 사용자에게 가입하라는 메시지를 표시합니다. 먼저 setFilterByAuthorizedAccounts(true)를 확인하여 이전에 사용한 계정이 있는지 확인합니다. 찾을 수 없는 경우 사용자에게 setFilterByAuthorizedAccounts(false)를 사용하여 Google 계정에 가입하라는 메시지를 표시합니다.

예:

val googleIdOption: GetGoogleIdOption = GetGoogleIdOption.Builder()
  .setFilterByAuthorizedAccounts(false)
  .setServerClientId(WEB_CLIENT_ID)
  .build()

Google 가입 요청을 인스턴스화한 후 인증 흐름을 시작합니다. 사용자가 가입 시 Google 계정으로 로그인을 사용하고 싶지 않은 경우 자동 완성을 위해 앱을 최적화해 보세요. 사용자가 계정을 만든 후에는 계정 생성의 마지막 단계로 패스키에 등록하는 것이 좋습니다.

로그아웃 처리

사용자가 앱에서 로그아웃하면 API clearCredentialState() 메서드를 호출하여 모든 사용자 인증 정보 제공업체에서 현재 사용자 인증 정보 상태를 지웁니다. 그러면 모든 사용자 인증 정보 제공업체에 지정된 앱의 저장된 사용자 인증 정보 세션을 삭제해야 한다고 알림이 전송됩니다.

사용자 인증 정보 제공업체는 활성 사용자 인증 정보 세션을 저장하고 이를 사용하여 향후 get-credential 호출의 로그인 옵션을 제한할 수 있습니다. 예를 들어 사용 가능한 다른 사용자 인증 정보보다 활성 사용자 인증 정보에 우선순위를 둘 수 있습니다. 사용자가 앱에서 명시적으로 로그아웃한 경우 다음에 전체 로그인 옵션을 가져오려면 이 API를 호출하여 제공업체가 저장된 모든 사용자 인증 정보 세션을 삭제하도록 허용해야 합니다.