通过一次点触创建新帐号

如果您支持通过 Google 帐号登录,则可以使用一次点触式登录客户端,为用户提供顺畅的帐号创建体验,并且该体验不会让用户离开您的应用上下文。

One-tap sign-up UI

显示一次点触式界面时,系统会提示用户使用其设备上的某个 Google 帐号在您的应用中创建新帐号。如果用户选择继续,您会收到一个 ID 令牌,其中包含基本的资料信息(姓名、个人资料照片和经过验证的电子邮件地址),您可以使用这些信息创建新帐号。

实现一次点触式帐号创建分为两个部分

  • 将一次点触式客户端集成到您的应用中(本页对此进行了介绍)。这与使用一次点触式登录大致相同,但在配置上有一些区别。
  • 在后端添加从 Google ID 令牌创建用户帐号的功能,如在后端使用 ID 令牌中所述。

我应在哪里使用一次点触式注册?

提供一次点触式注册对用户影响最大的地方是登录能够启用新功能的场景。首先,尝试使用已保存的凭据让用户登录。如果找不到已保存的凭据,则提供为用户创建新帐号的选项。

开始之前

一次点触式登录入门中所述,设置您的 Google APIs 控制台项目和 Android 项目。

1. 配置一次点触式客户端

要配置用于帐号创建的一次点触式客户端,请执行以下操作

  • 不要启用密码凭据请求。(一次点触式注册仅通过基于令牌的身份验证实现。)
  • 使用 setGoogleIdTokenRequestOptions() 并采用以下设置启用 Google ID 令牌请求

Java

public class YourActivity extends AppCompatActivity {

  // ...

  private SignInClient oneTapClient;
  private BeginSignInRequest signUpRequest;

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

      oneTapClient = Identity.getSignInClient(this);
      signUpRequest = BeginSignInRequest.builder()
              .setGoogleIdTokenRequestOptions(GoogleIdTokenRequestOptions.builder()
                      .setSupported(true)
                      // Your server's client ID, not your Android client ID.
                      .setServerClientId(getString(R.string.your_web_client_id))
                      // Show all accounts on the device.
                      .setFilterByAuthorizedAccounts(false)
                      .build())
              .build();

      // ...
  }
}

Kotlin

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

    private lateinit var oneTapClient: SignInClient
    private lateinit var signUpRequest: BeginSignInRequest

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

        oneTapClient = Identity.getSignInClient(this)
        signUpRequest = BeginSignInRequest.builder()
            .setGoogleIdTokenRequestOptions(
                BeginSignInRequest.GoogleIdTokenRequestOptions.builder()
                    .setSupported(true)
                    // Your server's client ID, not your Android client ID.
                    .setServerClientId(getString(R.string.your_web_client_id))
                    // Show all accounts on the device.
                    .setFilterByAuthorizedAccounts(false)
                    .build())
            .build()
        // ...
    }
    // ...
}

2. 记录一次点触式界面取消事件

您应该记录用户是否已通过关闭提示或在提示框外点击来拒绝使用一次点触式注册。这可以像您 Activity 的一个布尔属性一样简单。(请参阅下面的停止显示一次点触式界面。)

3. 显示一次点触式注册界面

如果用户尚未拒绝使用一次点触式创建新帐号,请调用客户端对象的 beginSignIn() 方法,并将监听器附加到它返回的 Task。应用通常在一次点触式登录请求未找到任何已保存凭据时执行此步骤,即在登录请求的失败监听器中执行。

如果用户在设备上设置了一个或多个 Google 帐号,一次点触式客户端将调用成功监听器。在成功监听器中,从 Task 结果中获取待处理的 intent,并将其传递给 startIntentSenderForResult() 以启动一次点触式界面。

如果用户设备上没有任何 Google 帐号,一次点触式客户端将调用失败监听器。在这种情况下,无需采取任何操作:您可以简单地继续呈现应用的已退出登录体验,用户可以通过您的正常帐号创建流程进行注册。

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 Google Accounts found. Just continue presenting the signed-out UI.
                Log.d(TAG, e.getLocalizedMessage());
            }
        });

Kotlin

oneTapClient.beginSignIn(signUpRequest)
    .addOnSuccessListener(this) { result ->
        try {
            startIntentSenderForResult(
                result.pendingIntent.intentSender, REQ_ONE_TAP,
                null, 0, 0, 0)
        } catch (e: IntentSender.SendIntentException) {
            Log.e(TAG, "Couldn't start One Tap UI: ${e.localizedMessage}")
        }
    }
    .addOnFailureListener(this) { e ->
        // No Google Accounts found. Just continue presenting the signed-out UI.
        Log.d(TAG, e.localizedMessage)
    }

4. 处理用户的响应

用户对一次点触式注册提示的响应将通过您 Activity 的 onActivityResult() 方法报告给您的应用。如果用户选择创建帐号,结果将是 Google ID 令牌。如果用户拒绝注册(通过关闭一次点触式界面或在其外部点击),结果将返回 RESULT_CANCELED 代码。您的应用需要处理这两种可能性。

使用 Google ID 令牌创建帐号

如果用户选择使用 Google 帐号注册,您可以通过将 onActivityResult() 中的 intent 数据传递给一次点触式客户端的 getSignInCredentialFromIntent() 方法来获取用户的 ID 令牌。凭据将具有非 null 的 googleIdToken 属性。

使用 ID 令牌在您的后端创建帐号(请参阅使用 ID 令牌通过后端进行身份验证)并让用户登录。

凭据还包含您请求的任何附加详细信息,例如帐号的已验证电话号码(如果可用)。

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();
                  if (idToken !=  null) {
                      // Got an ID token from Google. Use it to authenticate
                      // with your backend.
                      Log.d(TAG, "Got ID token.");
                  }
              } 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
                    when {
                        idToken != null -> {
                            // Got an ID token from Google. Use it to authenticate
                            // with your backend.
                            Log.d(TAG, "Got ID token.")
                        }
                        else -> {
                            // Shouldn't happen.
                            Log.d(TAG, "No ID token!")
                        }
                    }
                } catch (e: ApiException) {
                    // ...
            }
        }
    }
    // ...
}

停止显示一次点触式界面

如果用户拒绝登录,调用 getSignInCredentialFromIntent() 将抛出带有 CommonStatusCodes.CANCELED 状态码的 ApiException。发生这种情况时,您应该暂时停止显示一次点触式登录界面,以免反复提示打扰用户。下面的示例通过在 Activity 上设置一个属性来实现此目的,该属性用于确定是否向用户提供一次点触式登录;但是,您也可以将值保存到 SharedPreferences 或使用其他方法。

实施一次点触式登录提示的速率限制非常重要。如果您不这样做,并且用户连续取消多次提示,一次点触式客户端将在接下来的 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})")
                        }
                    }
                }
            }
        }
    }
    // ...
}

后续步骤

当用户完成一次点触式注册流程时,您会获得一个 Google ID 令牌,其中包含一些基本的资料信息:用户的电子邮件地址、全名和个人资料图片网址。对于许多应用而言,这些信息足以让您在后端对用户进行身份验证并创建新帐号。

如果您需要附加信息来完成帐号创建,例如用户的出生日期,则向用户呈现注册详细信息流程,在该流程中请求这些附加信息。然后,将其发送到您的后端以完成帐号创建。