This nice tutorial is a very good introduction to account authentication on Android and doing it by utilizing Android's AccountManager
.
However, I need to create a client app for an OAuth2 API using a Bearer token for authentication. At the time of obtaining the token, I receive the expiry timestamp for it, but I am unclear about where to store and how to make use of it properly. Problem is, if I don’t want to have unnecessary trips to the server, the app would realize that the Bearer had become invalid only after it receives a HTTP 401 error from the server when requesting any random resource. So, what is the best practice to tackle this:
invalidateAuthToken
when catching the exception and retry. As I am new to Android development, I expect that the solution may also be something completely different than I expect.
If it is relevant, I intend to use Volley for the server communication.
I found out my own answers after a bit of investigation:
Yes, calling AccountManager#invalidateAuthToken
removes the last saved authentication token (access token in the OAuth2 case) and expects that you are detecting that on the next AccountAuthenticator#getAuthToken
call. For example, the following is my code for that method:
@Override
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
// Extract the username and password from the Account Manager, and ask
// for an appropriate AuthToken.
final AccountManager am = AccountManager.get(mContext);
String authToken = am.peekAuthToken(account, authTokenType);
// EXTRA: I am also storing the OAuth2 refresh token in the AccountManager
Map<String, String> refreshResult = null;
String refreshToken = am.getUserData(account, KEY_REFRESH_TOKEN);
if (TextUtils.isEmpty(authToken) && !TextUtils.isEmpty(refreshToken)) {
// lets try to refresh the token
// EXTRA: AuthenticationProvider is my class for accessing the authentication server, getting new access and refresh token based on the existing refresh token
refreshResult = AuthenticationProvider.
refreshAccessToken(am.getUserData(account, KEY_REFRESH_TOKEN));
}
// If we get a result from the refresh - we return it
if (!refreshResult.isEmpty()) {
authToken = refreshResult.get(AccountManager.KEY_AUTHTOKEN);
// EXTRA: new refresh token used only in OAuth2
refreshToken = refreshResult.get(KEY_REFRESH_TOKEN);
final Bundle result = new Bundle();
result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
// store the new tokens in the system
am.setAuthToken(account, authTokenType, authToken);
am.setUserData(account, KEY_REFRESH_TOKEN, refreshToken);
result.putString(AccountManager.KEY_AUTHTOKEN, refreshResult.get(AccountManager.KEY_AUTHTOKEN));
result.putString(KEY_REFRESH_TOKEN, refreshResult.get(KEY_REFRESH_TOKEN));
return result;
}
// If we get here, then we couldn't access the user's password - so we
// need to re-prompt them for their credentials. We do that by creating
// an intent to display our AuthenticatorActivity.
final Intent intent = new Intent(mContext, LoginActivity.class);
intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
final Bundle bundle = new Bundle();
bundle.putParcelable(AccountManager.KEY_INTENT, intent);
return bundle;
}
I also received a confirmation from the author of the blog post mentioned in the question.
SyncAdapter
s cannot help directly, as their true purpose is obtaining data from network asynchronously (for the developer) and transparently (for the user). They just use AbstractAccountAuthenticator
and call its methods where appropriate.
I was going through the same problem before and make this library to handel OAuth2 in Android. but the library is an extension for Retrofit that simplifies the process of authenticating against an OAuth 2 provider but still you can use it.
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