I'm coding an app which makes use of Firebase Authentication to authenticate with my backend server.
I'm offering Google Sign-In and Email/Password based authentication. Email/Password is trivial.
This is my flow for using Google Sign-In:
1) I create a mGoogleApiClient
= new GoogleApiClient
with .addApi(Auth.GOOGLE_SIGN_IN_API, gso)
, where gso
= GoogleSignInOptions
with .requestIdToken(...))
and .requestEmail()
2) I call startActivityForResult(
signInIntent
, RC_GOOGLE_SIGN_IN);
where signInIntent
= Auth.GoogleSignInApi.getSignInIntent(
mGoogleApiClient
)
3) In the onActivityResult
I get the result
= Auth.GoogleSignInApi.getSignInResultFromIntent(data);
and from there GoogleSignInAccount
account
=
result
.getSignInAccount()
with which I finally log into Firebase by calling mFirebaseAuth.signInWithCredential(GoogleAuthProvider.getCredential(
account
.getIdToken(), null))
4) Then I "generate"/retrieve a Firebase JWT via fbUser.getToken(false)
which I then send to the backend server to create/authenticate the user over there.
The 4 steps work. I am able to sign the app into Firebase and into my backend server.
This is fine if I want to use only one account at a time. Actually, that is what I want to do, but my problem is the following:
I want to, just like Gmail or Inbox does, offer a list of accounts which have been signed in at least once via the 4 steps mentioned above.
When the user clicks on an account, then I want do so something which is a bit different than those 4 steps. I want to do something like a silent sign in, since we already know which account should get signed into. I don't want the Google Sign-In Account Selection Dialog to appear where I have to choose that address again. Worst case scenario the User initially taps the Account-1@gmail.com with the intention to sign into Account-1@gmail.com, but in the Chooser he then selects Account-2@gmail.com, which would be a bit tricky and annoying.
One "solution" to this is not to offer a list of known accounts, but just a "Sign-In with Google"-Button, which lets the Google Play Services present the chooser dialog. But this causes two problems: 1) It no longer makes sense to show a list of accounts which are known to the app like Gmail or Inbox does, because any click on any account will ask for a second time. 2) When I'm disconnected from the internet and initiate a Sign-In, the SignInIntent will fail, leaving the app in an odd semi-signed in state, where the data from Account1 is shown but Account1 is actually not signed in (or Account2 is shown but is actually not signed in), and upon a reconnect to the internet that Account Chooser Dialog must be shown again, where the user must then select the account which he is already using. This is awful.
Currently my solution is to not allow the user to get into a signed-in state unless Google Sign-In + Firebase manages to sign in correctly, which means that I won't load the data from the local database for that user and the user can't use his data until he gets internet access again. This is very bad.
I would like to allow the user to switch the account, and if Google Sign-In fails, then have Android/Google Sign-In remember the account, and I show the user his data for that account, and as soon as internet connectivity is there again I would like to sign-in the user silently into Firebase and my backend, without showing an Account Chooser Dialog.
Question 1: Can this archieved with Auth.GoogleSignInApi.silentSignIn(mGoogleApiClient)
? If so, how? Because I'm always getting a GoogleSignInStatusCodes.SIGN_IN_REQUIRED
which forces me to go through the traditional 4 steps from above and show the Account Chooser Dialog. On a note aside, after I sign into Google successfully, I sign out of that Google account after signing into the Firebase account, since I don't need that Google account anymore. Also, before any sign-in attempt I call Auth.GoogleSignInApi.signOut(mGoogleApiClient);
If that is the reason for the GoogleSignInStatusCodes.SIGN_IN_REQUIRED
, how can I persist such an account info, or will the library take care of that?
Also, I don't see a way to pass an Email address to Auth.GoogleSignInApi.silentSignIn(mGoogleApiClient)
, so I can't use that method to switch from one account to another, since I would have to sign out the one account before requesting the sign-in of the second one. This leads me to believe that I can't use this method to offer a list of already-known accounts, for example in a Navigation Drawer, since switch will require that Dialog to show up again, which messes with the workflow.
So, Question 2: How can I sign-in an account which already got signed in once in a silent manner only by using the email or the user_id?
As an extra, I would like to filter out from the Account Chooser Dialog the accounts which already signed in once, because those then would get signed into via the method mentioned in the second question (pass an email or user_id to the silentSignIn)
Thanks for taking the time to read this.
It is possible.
The key to solving the problem is to pass the email address to the GoogleSignInOptions
builder via gsob.setAccountName(...);
when a "silent" sign-in (no account chooser dialog) should occur.
GoogleSignInOptions.Builder gsob = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN);
gsob.requestIdToken(ApplicationCore.getMainActivity().getString(R.string.default_web_client_id));
gsob.requestEmail();
if (strEmail != null && !strEmail.equals("")) {
gsob.setAccountName(strEmail); // <------------------- here...
}
mGoogleApiClient = new GoogleApiClient.Builder(ApplicationCore.getMainActivity())
.enableAutoManage(ApplicationCore.getMainActivity(), new GoogleApiClient.OnConnectionFailedListener() {
@Override public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
// Do some cleanup here
}
})
.addApi(Auth.GOOGLE_SIGN_IN_API, gsob.build())
.build();
};
mGoogleApiClient.connect();
This then leads to the following flow, which is what I wanted.