讓使用者使用已儲存的憑證登入

使用 One Tap 登入用戶端,要求使用者授予擷取權限 先前用於登入應用程式的其中一個憑證。這些 憑證可以是 Google 帳戶或使用者名稱和密碼的組合 透過 Chrome、Android 自動填入或 Smart Lock 儲存到 Google 的檔案 密碼,

輕觸登入使用者介面

成功擷取憑證後,您就能使用憑證來順暢無阻 登入應用程式

如果使用者尚未儲存任何憑證,系統就不會顯示使用者介面,您可以 請提供您一般的未登入體驗。

哪裡可以使用 One Tap 登入功能?

如果應用程式要求使用者登入,請在登入時顯示 One Tap UI 。即使您已具備「使用 Google 帳戶登入」功能,這項功能還是相當實用。 按鈕:因為 One Tap UI 可以設定為只顯示憑證 登入頻率不高的使用者 避免使用者不小心登入帳戶 透過應用程式建立新帳戶

如果應用程式可以選擇登入,建議你透過任何方式使用 One Tap 登入功能 螢幕,讓使用者能輕鬆登入。舉例來說,如果使用者 在登出狀態下使用應用程式瀏覽內容,但只能張貼留言或新增 這就是使用者登入後 One Tap 登入。

選用登入功能的應用程式也應在登入畫面使用 One Tap 登入功能。 。

事前準備

1. 設定 One Tap 登入用戶端

您可以設定 One Tap 登入用戶端,讓使用者透過已儲存的位置登入 或已儲存的 Google 帳戶(建議同時支援這兩種類型, 為新使用者啟用一鍵建立帳戶功能,以及自動或輕觸一下即可登入 盡可能吸引最多回訪者)

如果應用程式使用密碼登入,請使用 setPasswordRequestOptions() 執行下列操作: 啟用密碼憑證要求。

如果您的應用程式使用 Google 登入功能,請透過 setGoogleIdTokenRequestOptions() 執行以下操作: 啟用並設定 Google ID 權杖要求:

  • 將伺服器用戶端 ID 設為您在 Google API 中建立的 ID 控制台。請注意,這是伺服器的用戶端 ID,不是 您的 Android 用戶端 ID。

  • 設定用戶端,依授權帳戶進行篩選。啟用這項功能後 選項,One Tap 用戶端只會提示使用者透過 他們過去用過的 Google 帳戶。這有助於使用者登入 當客戶不確定自己是否已擁有帳戶,或 使用者使用的 Google 帳戶,可防止使用者不小心建立新帳戶 帳戶。

  • 如要盡可能讓使用者自動登入,請啟用這項功能 只在 setAutoSelectEnabled()。只要 符合以下條件:

    • 使用者只儲存了一組應用程式憑證,也就是只儲存一組密碼或一個 Google 帳戶。
    • 使用者未在 Google 帳戶設定中停用自動登入功能。
  • 雖然非必要,但我們強烈建議您考慮使用 Nonce 提升登入安全性,並避免重送攻擊使用 setNonce ,在每個要求中加入 Nonce。查看 SafetyNet 的 取得 Nonce 一節,取得有關產生 Nonce 的其他建議和其他詳細資訊。

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 登入 UI。

您也應該追蹤使用者是否已拒絕使用 One Tap 登入會關閉提示,或輕觸提示以外的位置。這可以 如同 Activity 的布林值屬性 (請參閱下方的「停止顯示 One Tap UI」一節。)

3. 顯示 One Tap 登入 UI

如果使用者尚未登入,且尚未拒絕使用 One Tap 登入功能, 呼叫用戶端物件的 beginSignIn() 方法,並將事件監聽器附加至 傳回 Task。應用程式通常會在活動的 onCreate() 方法中執行此操作 或是在採用單一活動架構的情況下,轉換畫面後進行轉換。

使用者儲存任何 Google 帳戶時,One Tap 用戶端會呼叫成功事件監聽器 所需的憑證在成功事件監聽器中,透過 傳回 Task 結果並傳遞至 startIntentSenderForResult(),即可啟動 One Tap 登入 UI。

如果使用者未儲存任何憑證,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 UI 或輕觸外部位置,結果就會傳回 代碼 RESULT_CANCELED。您的應用程式需要同時處理這兩種可能性。

使用擷取到的憑證登入

如果使用者選擇將憑證提供給應用程式,您可以透過下列方式擷取憑證: 將意圖資料從 onActivityResult() 傳遞至 One Tap 用戶端 getSignInCredentialFromIntent() 方法。憑證會有非空值 googleIdToken 屬性:如果使用者與其他人共用 Google 帳戶憑證 如果使用者分享已儲存的密碼,則傳回非空值的 password 屬性。

使用憑證,透過應用程式後端進行驗證。

  • 如果系統擷取了使用者名稱和密碼組合,請用這些資料登入 如何向使用者提供這些資訊
  • 如果已擷取 Google 帳戶憑證,請使用 ID 權杖進行驗證 與您的後端互動如果您已選擇使用 Nonce 以避免重玩 攻擊會檢查後端伺服器上的回應值。詳情請見 使用 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();
                  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 UI

如果使用者拒絕登入,系統會呼叫 getSignInCredentialFromIntent() 將會擲回包含 CommonStatusCodes.CANCELED 狀態碼的 ApiException。 發生這種情況時,建議您暫時停用 One Tap 登入 UI, 可以重複提示,不讓使用者感到不悅以下範例完成 只要在 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. 處理登出動作

當使用者登出應用程式時,請呼叫 One Tap 用戶端的 signOut() 方法。 呼叫 signOut() 會停用自動登入功能,直到使用者再次登入為止。

即使您並未使用自動登入功能,這個步驟還是相當重要,因為 這能確保使用者登出應用程式時, 你使用的 Play 服務 API 也會重設。

後續步驟

如果您已設定 One Tap 用戶端擷取 Google 憑證, 現在可以取得代表使用者的 Google ID 權杖Google 帳戶。學習新知 如何在後端使用這些權杖

如果您支援 Google 登入,也可以使用 One Tap 用戶端新增 帳戶建立流程更加順暢無礙