Вход пользователей с помощью сохраненных учетных данных

Используйте клиент входа через One Tap, чтобы запросить у пользователя разрешение на получение одних из учетных данных, которые он ранее использовал для входа в ваше приложение. Это может быть аккаунт Google или комбинация имени пользователя и пароля, сохраненная в Google с помощью Chrome, автозаполнения Android или Smart Lock для паролей.

Интерфейс входа через One Tap

После успешного получения учетных данных вы можете использовать их для беспрепятственного входа пользователя в ваше приложение.

Если пользователь не сохранил учетные данные, интерфейс не будет показан, и вы сможете предоставить обычный интерфейс для выхода из системы.

Где следует использовать вход через One Tap?

Если в вашем приложении требуется вход, добавьте интерфейс One Tap на экран входа. Это может быть полезно, даже если у вас уже есть кнопка "Войти с аккаунтом Google". Поскольку интерфейс One Tap можно настроить так, чтобы в нем показывались только учетные данные, которые пользователь ранее использовал для входа, он может напоминать пользователям, которые редко входят в систему, о том, как они входили в последний раз, и предотвращать случайное создание новых аккаунтов в вашем приложении.

Если вход в аккаунт необязателен, рекомендуем использовать вход через One Tap на всех экранах, где вход в аккаунт может улучшить взаимодействие с приложением. Например, если пользователи могут просматривать контент в вашем приложении без входа в аккаунт, но для публикации комментариев или добавления товаров в корзину им нужно войти в аккаунт, то в этом случае вход через One Tap будет уместен.

В приложениях, где вход необязателен, также рекомендуется использовать One Tap на экранах входа по тем же причинам.

Подготовка

1. Настройте клиент для входа через One Tap

Вы можете настроить клиент входа через One Tap так, чтобы пользователи входили с помощью сохраненных паролей, сохраненных аккаунтов Google или обоих способов. Рекомендуется поддерживать оба варианта, чтобы новые пользователи могли создавать аккаунты одним нажатием, а вернувшиеся пользователи – входить в них автоматически или одним нажатием.

Если в вашем приложении используется вход по паролю, включите запросы учетных данных с помощью setPasswordRequestOptions().

Если в вашем приложении используется вход с аккаунтом Google, включите и настройте запросы токенов идентификации Google с помощью setGoogleIdTokenRequestOptions():

  • Укажите идентификатор клиента сервера, созданный в консоли Google Cloud. Обратите внимание, что это идентификатор клиента сервера, а не Android.

  • Настройте клиент так, чтобы он фильтровал данные по авторизованным аккаунтам. Если вы включите этот параметр, One Tap будет предлагать пользователям войти в ваше приложение только с помощью аккаунтов Google, которые они уже использовали в прошлом. Это поможет пользователям войти в систему, если они не уверены, есть ли у них аккаунт или какой аккаунт Google они использовали, и предотвратит случайное создание новых аккаунтов в вашем приложении.

  • Если вы хотите, чтобы пользователи входили в аккаунт автоматически, включите эту функцию с помощью setAutoSelectEnabled(). Автоматический вход возможен, если выполняются следующие условия:

    • Для вашего приложения у пользователя сохранены только одни учетные данные, то есть один пароль или один аккаунт Google.
    • Пользователь не отключил автоматический вход в настройках аккаунта Google.
  • Хотя это необязательно, мы настоятельно рекомендуем использовать однократно используемое число, чтобы повысить безопасность входа и избежать атак повторного воспроизведения. Используйте setNonce, чтобы включить одноразовый код в каждый запрос. Дополнительную информацию о создании одноразового кода можно найти в разделе Получение одноразового кода документации SafetyNet.

Java

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();
        // ...
    }
    // ...
}

Kotlin

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. Проверьте, выполнен ли вход в аккаунт

Если ваше действие может использоваться как пользователем, вошедшим в аккаунт, так и пользователем, не вошедшим в аккаунт, проверьте статус пользователя, прежде чем показывать интерфейс входа через One Tap.

Также следует отслеживать, отказался ли пользователь от входа через One Tap, закрыв запрос или нажав за его пределами. Это может быть логическое свойство вашего действия. (см. раздел Как отключить показ интерфейса One Tap ниже).

3. Как показать интерфейс входа через One Tap

Если пользователь не вошел в аккаунт и не отказался от входа через One Tap, вызовите метод beginSignIn() объекта клиента и прикрепите прослушиватели к возвращаемому объекту Task. Обычно это делается в методе onCreate() класса Activity или после перехода между экранами, если используется архитектура с одним классом Activity.

  • Сохраненные учетные данные найдены. Клиент One Tap вызовет прослушиватель успешного выполнения, если у пользователя есть сохраненные учетные данные для вашего приложения. В прослушивателе успешного выполнения получите отложенный интент из результата Task и передайте его в startIntentSenderForResult(), чтобы запустить интерфейс входа через One Tap.
  • Сохраненные учетные данные не найдены. Клиент One Tap вызовет прослушиватель сбоев. В этом случае никаких действий не требуется: вы можете продолжать предлагать пользователям функции приложения, доступные без входа в аккаунт. Однако если вы поддерживаете регистрацию через One Tap, то можете запустить этот процесс здесь, чтобы упростить создание аккаунта. Подробнее о том, как создавать аккаунты одним нажатием…

Java

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());
            }
        });

Kotlin

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. Обработка ответа пользователя

Ответ пользователя на предложение войти в аккаунт через One Tap будет передан в ваше приложение с помощью метода onActivityResult(). Если пользователь решил войти, результатом будут сохраненные учетные данные. Если пользователь отказался от входа, закрыв интерфейс One Tap или нажав за его пределами, будет возвращен результат с кодом RESULT_CANCELED. Ваше приложение должно поддерживать оба варианта.

Вход с полученными учетными данными

Если пользователь разрешил приложению доступ к учетным данным, вы можете получить их, передав данные intent из onActivityResult() в метод getSignInCredentialFromIntent() клиента One Tap. Если пользователь предоставил приложению доступ к учетным данным аккаунта Google, свойство googleIdToken будет иметь значение, отличное от null. Если пользователь предоставил доступ к сохраненному паролю, значение, отличное от null, будет иметь свойство password.

Используйте учетные данные для аутентификации в серверной части приложения.

  • Если пара "имя пользователя – пароль" была получена, используйте ее для входа так же, как если бы пользователь ввел ее вручную.
  • Если учетные данные аккаунта Google были получены, используйте токен идентификатора для аутентификации в серверной части. Если вы решили использовать одноразовый код для защиты от атак повторного воспроизведения, проверьте значение ответа на бэкенд-сервере. Подробнее об аутентификации с помощью токенов идентификации…

Java

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;
        }
    }
}

Kotlin

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) {
                      // ...
                  }
              }
          }
      }
      // ...
  }

Как отключить интерфейс One Tap

Если пользователь отказался от входа, вызов getSignInCredentialFromIntent() выдаст ApiException с кодом статуса CommonStatusCodes.CANCELED. В этом случае вам следует временно отключить интерфейс входа через One Tap, чтобы не раздражать пользователей повторяющимися подсказками. В примере ниже это делается путем настройки свойства в Activity, которое используется для определения того, предлагать ли пользователю вход через One Tap. Однако вы также можете сохранить значение в SharedPreferences или использовать другой метод.

Важно реализовать собственное ограничение частоты запросов на вход через One Tap. Если вы этого не сделаете и пользователь отменит несколько подсказок подряд, клиент One Tap не будет запрашивать у пользователя следующие 24 часа.

Java

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;
        }
    }
}

Kotlin

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() клиента One Tap. Вызов signOut() отключает автоматический вход, пока пользователь не войдет снова.

Даже если вы не используете автоматический вход, этот шаг важен, поскольку он гарантирует, что когда пользователи выходят из вашего приложения, состояние аутентификации всех используемых вами API сервисов Google Play также сбрасывается.

Дальнейшие действия

Если вы настроили клиент One Tap для получения учетных данных Google, ваше приложение теперь может получать токены идентификатора Google, которые представляют аккаунты Google ваших пользователей. Подробнее о том, как использовать эти токены на сервере…

Если вы поддерживаете вход с аккаунтом Google, то можете использовать клиент One Tap, чтобы добавить в приложение удобные способы создания аккаунта.