I am creating a new Android app using SyncAdapter
to handle db sync.
I have everything in place and the app is working fine but I noticed that I am logged in twice.
The first login takes place when the AuthenticatorActivity
class (it extends AccountAuthenticatorActivity
) validates the user and password.
If the user and password are correct the AuthenticatorActivity
then does:
account
didn't exist it creates it using mAccountManager.addAccountExplicitly()
authToken
is saved using intent.putExtra(AccountManager.KEY_AUTHTOKEN, authToken);
This was basically copied/pasted from the Android samples, so I guess it's ok.
The issue is that when the SyncAdapter
launches and uses
authtoken = mAccountManager.blockingGetAuthToken(account,
AuthenticatorActivity.PARAM_AUTHTOKEN_TYPE, true);
The getAuthToken()
method inside the Authenticator
class which extends AbstractAccountAuthenticator
is called. And inside this method I am hitting the login endpoint once again.
From that point onwards the login endpoint is not hit again until the authToken
expires.
This is not something that bothers me a lot but I would like to know if there is a way to avoid doing the login twice.
As you've seen, though Authenticator.java
in the SampleSyncAdapter says
The interesting thing that this class demonstrates is the use of authTokens as part of the authentication process. ... If we already have an authToken stored in the account, we return that authToken. If we don't, but we do have a username and password, then we'll attempt to talk to the sample service to fetch an authToken.
that's a lie. Authenticator.getAuthToken
doesn't to any cache checking, it just hits the network to get a token.
The solution is to add in the missing check:
Authenticator.java:
@Override
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account,
String authTokenType, Bundle loginOptions) throws NetworkErrorException {
// check that authToken type supported
...
// Check if we already have a cached token to return
final AccountManager am = AccountManager.get(mContext);
String cachedAuthToken = am.peekAuthToken(account, authTokenType);
if (cachedAuthToken != null) {
final Bundle result = new Bundle();
result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
result.putString(AccountManager.KEY_ACCOUNT_TYPE, Constants.ACCOUNT_TYPE);
result.putString(AccountManager.KEY_AUTHTOKEN, cachedAuthToken);
return result;
}
// Get new authToken from server
...
// If all else fails, prompt the user for credentials
...
}
Note that the rest of your project needs to religiously use AccountManager.invalidateAuthToken
when calls fail or else you'll wind up with an infinite loop of calls failing, trying to get a new auth token, and then failing again when the same cached auth token is returned.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With