Search code examples
androidgoogle-plusgoogle-plus-signin

G+ Sign-In Ian Barber Implementation : onConnectionFailed not getting called when disconnecting rebuilding GoogleApiClient


I have integrated google+ sign in using the developer site and for server side i followed @ianbarber 's solution on riskcompletefailure blog

This how i am bulding the GoogleApiClient:

return new GoogleApiClient.Builder(getActivity())
            .addConnectionCallbacks(this)
            .addOnConnectionFailedListener(this)
            .addApi(Plus.API, Plus.PlusOptions.builder().build())
            .addScope(new Scope("email")).addScope(Plus.SCOPE_PLUS_LOGIN)
            .build();

This is how getting the id token in async task

scopes = "audience:server:client_id:" + Util.CLIENT_ID_SERVER;
            id = GoogleAuthUtil.getToken(getActivity(),
                    Plus.AccountApi.getAccountName(mGoogleApiClient),
                    scopes);

after which createSession is called and based on the response from server it updates the hashMap & rebuild GoogleApiClient and again retries to connect, but it is going to onConnceted() rather than onConnectionFailed() as server returned 401 (no refresh token available)

if (cookie.equals("NO_REFRESH")) {
        Log.v(TAG, "create session for " + mAccountName);
        mSessionCookie = null;
        mUserHasRefreshOnServer.put(mAccountName, false);
    } else {
        Log.v(TAG, "refresh available on server");
        mSessionCookie = cookie;
        mUserHasRefreshOnServer.put(mAccountName, true);
    }
    if (mGoogleApiClient.isConnected()) {
        mGoogleApiClient.disconnect();
        Log.v(TAG, "disconnect googleclient ");
    }
    Log.v(TAG, "try connect googleclient ");

    mGoogleApiClient = buildGoogleApiClient();
    mGoogleApiClient.connect();
}

this what i need to perform in onConnectionFailed()

public void onConnectionFailed(ConnectionResult result) {

    if ((mAccountName != null)
            && mUserHasRefreshOnServer.containsKey(mAccountName)
            && mUserHasRefreshOnServer.get(mAccountName) == Boolean.FALSE) {
            getCode(); //code for getting onetime authorization code
    }

Please point out what am i doing wrong and for details look at this document i have followed exactly

EDIT:

I changed the flow.. replicating exactly this gist but now after sending the id token (provided server doesn't have a refresh & no consent asked) when i go for getCode i get consent only for Plus.SCOPE_PLUS_LOGIN and not for offline access hence when i reconnect i still end up in onConnectionFailed with error code=4 (Sign-in required)

But, The alternate flow wherein if i resolve errors beforehand(consent to Plus.SCOPE_PLUS_LOGIN) and then go for getCode this time i get this offline access consent also and consequently an onConnected which is what is required.


Solution

  • That's a valid state to get into! You can get there if the user has previously signed in, but you have lost the refresh token on the server.

    If you look at the gist (which is out of date now since moving to GoogleApiClient of course), you can see we have a getCode call in the onConnected as well: https://gist.github.com/ianbarber/7105273#file-codeactivity-java-L97

    if (mSessionCookie == null) {
      // If the server hasn't got a refresh token, grab it.
      if(mUserHasRefreshOnServer.containsKey(mAccountName)) {
        getCode();
      } else {
        // Otherwise, just get the session.
        getSession();
      }
    }
    

    This is precisely for handling that case - we know the user has signed but, but we still need to get a refresh token on the server, so we're going to have to send them down the getCode route and pop a consent dialog via the UserRecoverableAuthException.