Use the One Tap sign-in client to request permission from the user to retrieve one of the credentials they previously used to sign in to your app. These credentials can be either a Google Account or a username-password combination they saved with Google using Chrome, Android autofill or Smart Lock for Passwords.
When credentials are successfully retrieved, you can use them to frictionlessly sign the user in to your app.
If the user hasn't saved any credentials, no UI is presented, and you can provide your normal signed-out experience.
Where should I use One Tap sign-in?
If your app requires users to sign in, display the One Tap UI on your sign-in screen. This can be helpful even if you already have a "Sign in with Google" button: because the One Tap UI can be configured to show only credentials the user previously used to sign-in, it can be a reminder to users who infrequently sign in how they signed in last time, and prevent them from accidentally creating new accounts with your app.
If sign-in is optional for your app, consider using One Tap sign-in on any screen that has an experience enhanced by signing in. For example, if users can browse content with your app while signed out, but can only post comments or add items to a shopping cart after signing in, that would be a sensible context for One Tap sign-in.
Sign-in optional apps should also use One Tap sign-in on their sign-in screens, for the reasons stated above.
Before you begin
- Set up your Google APIs console project and Android project as described in Get started with One Tap sign-in.
- If you support password-based sign-in, optimize your app for autofill (or use Smart Lock for Passwords) so users can save their password credentials after signing in.
1. Configure the One Tap sign-in client
You can configure the One Tap sign-in client to sign users in with saved passwords, saved Google Accounts, or either. (Supporting both is recommended, to enable one-tap account creation for new users and automatic or one-tap sign-in for as many returning users as possible.)
If your app uses password-based sign-in, use setPasswordRequestOptions()
to
enable password credential requests.
If your app uses Google Sign-in, use setGoogleIdTokenRequestOptions()
to
enable and configure Google ID token requests:
Set the server client ID to the ID you created in the Google APIs console. Note this is your server's client ID, not your Android client ID.
Configure the client to filter by authorized accounts. When you enable this option, the One Tap client only prompts users to sign in to your app with Google Accounts they've already used in the past. Doing so can help users sign in successfully when they're not sure if they already have an account or which Google Account they used, and prevents users from accidentally creating new accounts with your app.
If you want to sign users in automatically when possible, enable the feature with
setAutoSelectEnabled()
. Automatic sign-in is possible when the following criteria are met:- The user has exactly one credential saved for your app. That is, one saved password or one saved Google Account.
- The user hasn't disabled automatic sign-in in their Google Account settings.
While optional, we recommend that you strongly consider using a nonce to improve sign-in security and avoid replay attacks. Use setNonce to include a nonce in each request. See SafetyNet's Obtain a nonce section for suggestions and additional detail on generating a 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. Check for a signed-in user
If your Activity could be used by a signed-in user or a signed-out user, check the user's status before displaying the One Tap sign-in UI.
You should also keep track of whether the user has already declined to use One Tap sign-in by either closing the prompt or tapping outside of it. This can be as simple as a boolean property of your Activity. (See Stop displaying the One Tap UI, below.)
3. Display the One Tap sign-in UI
If the user isn't signed in and hasn't already declined to use One Tap sign-in,
call the client object's beginSignIn()
method, and attach listeners to the
Task
it returns. Apps typically do this in the Activity's onCreate()
method
or after screen transitions when using a single-Activity architecture.
The One Tap client will call the success listener if the user has any saved
credentials for your app. In the success listener, get the pending intent from
the Task
result and pass it to startIntentSenderForResult()
to start the
One Tap sign-in UI.
If the user doesn't have any saved credentials, the One Tap client will call the failure listener. In this case, no action is necessary: you can simply continue presenting the app's signed-out experience. However, if you support One Tap sign-up, you could start that flow here for a seamless account creation experience. See Create new accounts with 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. Handle the user's response
The user's response to the One Tap sign-in prompt will be reported to your app
using your Activity's onActivityResult()
method. If the user chose to sign in,
the result will be a saved credential. If the user declined to sign in, either
by closing the One Tap UI or tapping outside it, the result will return with the
code RESULT_CANCELED
. Your app needs to handle both possibilities.
Sign in with retrieved credentials
If the user chose to share credentials with your app, you can retrieve them by
passing the intent data from onActivityResult()
to the One Tap client's
getSignInCredentialFromIntent()
method. The credential will have a non-null
googleIdToken
property if the user shared a Google Account credential with
your app, or a non-null password
property if the user shared a saved password.
Use the credential to authenticate with your app's backend.
- If a username and password pair was retrieved, use them to sign in the same way you would if the user had manually supplied them.
If Google Account credentials were retrieved, use the ID token to authenticate with your backend. If you've chosen to use a nonce to help avoid replay attacks check the response value on your backend server. See Authenticate with a backend using ID tokens.
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) { // ... } } } } // ... }
Stop displaying the One Tap UI
If the user declined to sign in, the call to getSignInCredentialFromIntent()
will throw an ApiException
with a CommonStatusCodes.CANCELED
status code.
When this happens, you should temporarily disable the One Tap sign-in UI so you
don't annoy your users with repeated prompts. The following example accomplishes
this by setting a property on the Activity, which it uses to determine whether
to offer the user One Tap sign-in; however, you could also save a value to
SharedPreferences
or use some other method.
It's important to implement your own rate limiting of One Tap sign-in prompts. If you don't, and a user cancels several prompts in a row, the One Tap client will not prompt the user for the next 24 hours.
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. Handle sign-out
When a user signs out of your app, call the One Tap client's signOut()
method.
Calling signOut()
disables automatic sign-in until the user signs in again.
Even if you don't use automatic sign-in, this step is important because it ensures that when users sign out of your app, the authentication state of any Play services APIs you use also get reset.
Next steps
If you configured the One Tap client to retrieve Google credentials, your app can now get Google ID tokens that represent your users' Google Accounts. Learn how you can use these tokens on the backend.
If you support Google Sign-in, you can also use the One Tap client to add frictionless account creation flows to your app.