경고 : 이 데이터는 Google 사용자 데이터 정책에 따라 제공됩니다. 정책을 검토하고 준수하십시오. 그렇게하지 않으면 프로젝트가 정지되거나 계정이 정지 될 수 있습니다.

저장된 자격 증명으로 사용자 로그인

One Tap 로그인 클라이언트를 사용하여 이전에 앱에 로그인하는 데 사용한 자격 증명 중 하나를 검색할 수 있는 권한을 사용자에게 요청합니다. 이러한 자격 증명은 Google 계정이거나 Chrome, Android 자동 완성 또는 비밀번호용 Smart Lock을 사용하여 Google에 저장한 사용자 이름-비밀번호 조합일 수 있습니다.

원터치 로그인 UI

자격 증명이 성공적으로 검색되면 이를 사용하여 사용자가 앱에 원활하게 로그인할 수 있습니다.

사용자가 자격 증명을 저장하지 않은 경우 UI가 표시되지 않으며 일반적인 로그아웃 환경을 제공할 수 있습니다.

원탭 로그인은 어디에서 사용해야 하나요?

앱에서 사용자가 로그인해야 하는 경우 로그인 화면에 원 탭 UI를 표시합니다. 이는 이미 "Google로 로그인" 버튼이 있는 경우에도 유용할 수 있습니다. One Tap UI는 사용자가 이전에 로그인하는 데 사용한 자격 증명만 표시하도록 구성할 수 있으므로 자주 로그인하지 않는 사용자에게 알림이 될 수 있습니다. 마지막으로 로그인한 방법을 확인하고 실수로 앱에 새 계정을 만드는 것을 방지합니다.

앱에서 로그인이 선택 사항인 경우 로그인을 통해 향상된 경험이 있는 모든 화면에서 원 탭 로그인을 사용하는 것이 좋습니다. 예를 들어 사용자가 로그아웃한 상태에서 앱으로 콘텐츠를 탐색할 수 있지만 댓글을 게시하거나 댓글을 게시할 수만 있는 경우 로그인 후 장바구니에 항목을 추가하는 것은 One Tap 로그인에 대한 합리적인 컨텍스트가 될 것입니다.

로그인 옵션 앱은 위에 명시된 이유로 로그인 화면에서 원 탭 로그인을 사용해야 합니다.

시작하기 전에

1. 원탭 로그인 클라이언트 구성

저장된 비밀번호, 저장된 Google 계정 또는 둘 중 하나로 사용자가 로그인하도록 One Tap 로그인 클라이언트를 구성할 수 있습니다. (신규 사용자의 경우 원 탭 계정 생성과 최대한 많은 재방문 사용자의 자동 로그인 또는 원 탭 로그인을 가능하게 하려면 두 가지를 모두 지원하는 것이 좋습니다.)

만약 앱 사용 암호 기반 로그인, 사용 setPasswordRequestOptions() 암호 자격 증명 요청을 가능하게합니다.

앱 구글 로그인, 사용 사용하는 경우 setGoogleIdTokenRequestOptions() 활성화하고 구성 구글 ID 토큰을 요청하기 :

  • 에 서버 클라이언트 ID를 설정 하면 Google API를 콘솔에서 만든 ID를 . 이것은 Android 클라이언트 ID가 아니라 서버의 클라이언트 ID입니다.

  • 승인된 계정으로 필터링하도록 클라이언트를 구성합니다. 이 옵션을 활성화하면 One Tap 클라이언트는 사용자에게 과거에 이미 사용한 Google 계정으로 앱에 로그인하라는 메시지만 표시합니다. 이렇게 하면 사용자가 이미 계정이 있는지 또는 사용한 Google 계정이 확실하지 않을 때 성공적으로 로그인하는 데 도움이 되며 사용자가 실수로 앱에서 새 계정을 만드는 것을 방지할 수 있습니다.

  • 자동 가능한 경우에 사용자가 서명 할 경우에이 기능 활성화 setAutoSelectEnabled() . 자동 로그인은 다음 기준을 충족하는 경우 가능합니다.

    • 사용자는 앱에 대해 정확히 하나의 자격 증명을 저장했습니다. 즉, 하나의 저장된 비밀번호 또는 하나의 저장된 Google 계정입니다.
    • 사용자는 자동 로그인을 해제하지 않은 자신의 Google 계정 설정 .
  • 선택 사항이지만 로그인 보안을 개선하고 재생 공격을 피하기 위해 nonce 사용을 적극 고려하는 것이 좋습니다. 사용 setNonce를 각 요청에 넌스를 포함 할 수 있습니다. 는 SafetyNET를 참조입니다 난스의 확보 난스를 생성에 대한 제안에 대한 섹션을 추가 세부 사항.

자바

public class YourActivity extends AppCompatActivity {
  // ...

  private SignInClient oneTapClient;
  private BeginSignInRequest signInRequest;

  @Override
  public void onCreate(@Nullable Bundle savedInstanceState,
                       @Nullable PersistableBundle persistentState) {
      super.onCreate(savedInstanceState, persistentState);

      oneTapClient = Identity.getSignInClient(this);
      signInRequest = BeginSignInRequest.builder()
              .setPasswordRequestOptions(PasswordRequestOptions.builder()
                      .setSupported(true)
                      .build())
              .setGoogleIdTokenRequestOptions(GoogleIdTokenRequestOptions.builder()
                      .setSupported(true)
                      // Your server's client ID, not your Android client ID.
                      .setServerClientId(getString(R.string.default_web_client_id))
                      // Only show accounts previously used to sign in.
                      .setFilterByAuthorizedAccounts(true)
                      .build())
              // Automatically sign in when exactly one credential is retrieved.
              .setAutoSelectEnabled(true)
              .build();
      // ...
  }
  // ...
}

코틀린

class YourActivity : AppCompatActivity() {
    // ...

    private lateinit var oneTapClient: SignInClient
    private lateinit var signInRequest: BeginSignInRequest

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        oneTapClient = Identity.getSignInClient(this)
        signInRequest = BeginSignInRequest.builder()
            .setPasswordRequestOptions(BeginSignInRequest.PasswordRequestOptions.builder()
                .setSupported(true)
                .build())
            .setGoogleIdTokenRequestOptions(
                BeginSignInRequest.GoogleIdTokenRequestOptions.builder()
                    .setSupported(true)
                    // Your server's client ID, not your Android client ID.
                    .setServerClientId(getString(R.string.your_web_client_id))
                    // Only show accounts previously used to sign in.
                    .setFilterByAuthorizedAccounts(true)
                    .build())
            // Automatically sign in when exactly one credential is retrieved.
            .setAutoSelectEnabled(true)
            .build()
        // ...
    }
    // ...
}

2. 로그인한 사용자 확인

로그인한 사용자 또는 로그아웃한 사용자가 내 활동을 사용할 수 있는 경우 원 탭 로그인 UI를 표시하기 전에 사용자의 상태를 확인하세요.

또한 사용자가 프롬프트를 닫거나 외부를 탭하여 원 탭 로그인 사용을 이미 거부했는지 여부도 추적해야 합니다. 이것은 활동의 부울 속성만큼 간단할 수 있습니다. (참조 정지는 하나의 탭 UI를 표시 아래에.)

3. 원탭 로그인 UI 표시

사용자가 로그인하지 않고 이미 하나의 탭 로그인에 사용을 거부하지 않은 경우, 클라이언트 객체의 전화 beginSignIn() 메소드를하고 리스너를 부착 Task 이 반환합니다. 앱은 일반적으로 작업의에서이 작업을 수행 onCreate() 단일 활동 아키텍처를 사용하는 경우 방법 또는 화면 전환 후.

사용자가 앱에 대해 저장된 자격 증명이 있는 경우 One Tap 클라이언트는 성공 수신기를 호출합니다. 성공 리스너에서에서 보류중인 의도를 얻을 Task 결과와에 전달 startIntentSenderForResult() 하나 개의 탭 로그인 UI를 시작합니다.

사용자에게 저장된 자격 증명이 없는 경우 One Tap 클라이언트는 실패 수신기를 호출합니다. 이 경우 조치가 필요하지 않습니다. 앱의 로그아웃 환경을 계속 표시하면 됩니다. 그러나 One Tap 가입을 지원하는 경우 원활한 계정 생성 경험을 위해 여기에서 해당 흐름을 시작할 수 있습니다. 참조 하나 개의 탭이있는 새 계정을 만들 수도 있습니다 .

자바

oneTapClient.beginSignIn(signUpRequest)
        .addOnSuccessListener(this, new OnSuccessListener<BeginSignInResult>() {
            @Override
            public void onSuccess(BeginSignInResult result) {
                try {
                    startIntentSenderForResult(
                            result.getPendingIntent().getIntentSender(), REQ_ONE_TAP,
                            null, 0, 0, 0);
                } catch (IntentSender.SendIntentException e) {
                    Log.e(TAG, "Couldn't start One Tap UI: " + e.getLocalizedMessage());
                }
            }
        })
        .addOnFailureListener(this, new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                // No saved credentials found. Launch the One Tap sign-up flow, or
                // do nothing and continue presenting the signed-out UI.
                Log.d(TAG, e.getLocalizedMessage());
            }
        });

코틀린

oneTapClient.beginSignIn(signInRequest)
    .addOnSuccessListener(this) { result ->
        try {
            startIntentSenderForResult(
                result.pendingIntent.intentSender, REQ_ONE_TAP,
                null, 0, 0, 0, null)
        } catch (e: IntentSender.SendIntentException) {
            Log.e(TAG, "Couldn't start One Tap UI: ${e.localizedMessage}")
        }
    }
    .addOnFailureListener(this) { e ->
        // No saved credentials found. Launch the One Tap sign-up flow, or
        // do nothing and continue presenting the signed-out UI.
        Log.d(TAG, e.localizedMessage)
    }

4. 사용자의 응답 처리

하나의 탭 로그인 프롬프트에 대한 사용자의 반응은 액티비티의 사용 앱에보고됩니다 onActivityResult() 메소드를. 사용자가 로그인을 선택한 경우 결과는 저장된 자격 증명이 됩니다. 사용자가를 하나의 탭 UI를 닫거나 외부 눌러 중, 로그인을 거부하는 경우, 결과는 코드를 반환합니다 RESULT_CANCELED . 앱은 두 가지 가능성을 모두 처리해야 합니다.

검색된 자격 증명으로 로그인

사용자가 앱을 공유 자격 증명을 선택한 경우에서 의도 된 데이터를 전달하여이를 검색 할 수 있습니다 onActivityResult() 하나 개의 탭 클라이언트에 getSignInCredentialFromIntent() 방법. 자격 증명은 null이 아닌있을 것이다 googleIdToken 사용자가 앱을 Google 계정 자격 증명, 또는 비 - 널 공유하는 경우 자산 password 사용자가 저장된 비밀번호를 공유하는 경우 속성을.

자격 증명을 사용하여 앱의 백엔드로 인증하세요.

  • 사용자 이름과 암호 쌍을 검색한 경우 사용자가 수동으로 제공한 경우와 동일한 방식으로 이를 사용하여 로그인합니다.
  • Google 계정 자격 증명이 검색된 경우 ID 토큰을 사용하여 백엔드로 인증하세요. 재생 공격을 피하기 위해 nonce를 사용하기로 선택한 경우 백엔드 서버의 응답 값을 확인하십시오. 참조 ID 토큰을 사용하여 백엔드 인증을 .

자바

public class YourActivity extends AppCompatActivity {

  // ...
  private static final int REQ_ONE_TAP = 2;  // Can be any integer unique to the Activity.
  private boolean showOneTapUI = true;
  // ...

  @Override
  protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
      super.onActivityResult(requestCode, resultCode, data);

      switch (requestCode) {
          case REQ_ONE_TAP:
              try {
                  SignInCredential credential = oneTapClient.getSignInCredentialFromIntent(data);
                  String idToken = credential.getGoogleIdToken();
                  String username = credential.getId();
                  String password = credential.getPassword();
                  if (idToken !=  null) {
                      // Got an ID token from Google. Use it to authenticate
                      // with your backend.
                      Log.d(TAG, "Got ID token.");
                  } else if (password != null) {
                      // Got a saved username and password. Use them to authenticate
                      // with your backend.
                      Log.d(TAG, "Got password.");
                  }
              } catch (ApiException e) {
                  // ...
              }
              break;
      }
  }
}

코틀린

class YourActivity : AppCompatActivity() {

    // ...
    private val REQ_ONE_TAP = 2  // Can be any integer unique to the Activity
    private var showOneTapUI = true
    // ...

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)

        when (requestCode) {
             REQ_ONE_TAP -> {
                try {
                    val credential = oneTapClient.getSignInCredentialFromIntent(data)
                    val idToken = credential.googleIdToken
                    val username = credential.id
                    val password = credential.password
                    when {
                        idToken != null -> {
                            // Got an ID token from Google. Use it to authenticate
                            // with your backend.
                            Log.d(TAG, "Got ID token.")
                        }
                        password != null -> {
                            // Got a saved username and password. Use them to authenticate
                            // with your backend.
                            Log.d(TAG, "Got password.")
                        }
                        else -> {
                            // Shouldn't happen.
                            Log.d(TAG, "No ID token or password!")
                        }
                    }
                } catch (e: ApiException) {
                    // ...
                }
            }
        }
    }
    // ...
}

원 탭 UI 표시 중지

사용자가 로그인을 거부하는 경우, 호출 getSignInCredentialFromIntent() 발생합니다 ApiException A를 CommonStatusCodes.CANCELED 상태 코드를. 이 경우 반복되는 메시지로 사용자를 짜증나게 하지 않도록 One Tap 로그인 UI를 일시적으로 비활성화해야 합니다. 다음 예제에서는 사용자에게 One Tap 로그인을 제공할지 여부를 결정하는 데 사용하는 Activity의 속성을 설정하여 이를 수행합니다. 그러나, 당신은 또한에 값 절약 할 수있다 SharedPreferences 또는 다른 방법을 사용합니다.

One Tap 로그인 프롬프트에 대한 고유한 속도 제한을 구현하는 것이 중요합니다. 그렇게 하지 않고 사용자가 여러 메시지를 연속으로 취소하면 One Tap 클라이언트는 다음 24시간 동안 사용자에게 메시지를 표시하지 않습니다.

자바

public class YourActivity extends AppCompatActivity {

  // ...
  private static final int REQ_ONE_TAP = 2;  // Can be any integer unique to the Activity.
  private boolean showOneTapUI = true;
  // ...

  @Override
  protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
      super.onActivityResult(requestCode, resultCode, data);

      switch (requestCode) {
          case REQ_ONE_TAP:
              try {
                  // ...
              } catch (ApiException e) {
                  switch (e.getStatusCode()) {
                      case CommonStatusCodes.CANCELED:
                          Log.d(TAG, "One-tap dialog was closed.");
                          // Don't re-prompt the user.
                          showOneTapUI = false;
                          break;
                      case CommonStatusCodes.NETWORK_ERROR:
                          Log.d(TAG, "One-tap encountered a network error.");
                          // Try again or just ignore.
                          break;
                      default:
                          Log.d(TAG, "Couldn't get credential from result."
                                  + e.getLocalizedMessage());
                          break;
                  }
              }
              break;
      }
  }
}

코틀린

class YourActivity : AppCompatActivity() {

    // ...
    private val REQ_ONE_TAP = 2  // Can be any integer unique to the Activity
    private var showOneTapUI = true
    // ...

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)

        when (requestCode) {
            REQ_ONE_TAP -> {
                try {
                    // ...
                } catch (e: ApiException) {
                    when (e.statusCode) {
                        CommonStatusCodes.CANCELED -> {
                            Log.d(TAG, "One-tap dialog was closed.")
                            // Don't re-prompt the user.
                            showOneTapUI = false
                        }
                        CommonStatusCodes.NETWORK_ERROR -> {
                            Log.d(TAG, "One-tap encountered a network error.")
                            // Try again or just ignore.
                        }
                        else -> {
                            Log.d(TAG, "Couldn't get credential from result." +
                                " (${e.localizedMessage})")
                        }
                    }
                }
            }
        }
    }
    // ...
}

5. 로그아웃 처리

사용자가 로그인 앱 중의 하나 개를 눌러 클라이언트의 호출 할 때 signOut() 메소드를. 호출 signOut() 자동 로그인을 비활성화 다시에있는 사용자가 로그인 할 때까지.

자동 로그인을 사용하지 않더라도 이 단계는 사용자가 앱에서 로그아웃할 때 사용하는 모든 Play 서비스 API의 인증 상태도 재설정되도록 하기 때문에 중요합니다.

다음 단계

Google 자격 증명을 검색하도록 One Tap 클라이언트를 구성한 경우 이제 앱에서 사용자의 Google 계정을 나타내는 Google ID 토큰을 가져올 수 있습니다. 당신이 수있는 방법을 알아보십시오 백엔드에이 토큰을 사용합니다 .

당신이 구글 로그인을 지원하는 경우, 당신은 또한 할 하나 개를 눌러 클라이언트를 사용할 수 있습니다 마찰 계정 생성이 앱에 흐르는 추가 .