Google 계정으로 로그인을 사용하여 인증 관리자 통합

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

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

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

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

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

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

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

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

Google API 콘솔 프로젝트 설정

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

Google 가입 요청을 인스턴스화한 후 인증 흐름을 시작합니다. 사용자가 Google에 가입하고 싶지 않은 경우 계정 생성을 위해 자동 완성 서비스나 패스키를 사용하는 것이 좋습니다.

로그아웃 처리

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

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