The is the third part of a series of posts on accounts and authentication in android. General definitions and terminology are covered in this post, with the topic on account registration and its procedure here.
My complete code for account authentication is available as an open-source library in Github.
In this part, the objective is to describe how to get an access token for an already-created account.
Account authentication
This simply means getting access token. If one exists, and is valid upon server verification, the user is allowed to access the protected service or material which is otherwise not accessible to them. The starting point, likewise registraion, is through the AccountManager:
1 2 |
AccountManager accountManager = AccountManager.get(context); accountManager.getAuthToken(account, authTokenType, options, activity, accountManagerCallback, handler); |
The accountManagerCallback runs after getting access token, and can handle access token verification with the server.
Similar to registration,
AbstractAccountAuthenticator is also responsible for authenticating accounts using
getAuthToken in the
AbstractAccountAuthenticator base class. The details of the implementation, however, is left to the user.
In the
AppAccountAuthenticator class extending the base class, four steps should be taken, as you see in the following excerpt from
AppAccountAuthenticator extending
AbstractAccountAuthenticator:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
@Override public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException { AuthenticatorManager authenticatorManager = AuthenticatorManager.authenticatorManager; Bundle result; AccountManager accountManager = AccountManager.get(context); // case 1: access token is available result = authenticatorManager.getAccessTokenFromCache(account, authTokenType, accountManager); if (result != null) { return result; } final String refreshToken = accountManager.getPassword(account); // case 2: access token is not available but refresh token is if (refreshToken != null) { result = authenticatorManager.makeResultBundle(account, refreshToken, null); return result; } // case 3: neither tokens is available but the account exists if (isAccountAvailable(account, accountManager)) { result = authenticatorManager.makeResultBundle(account, null, null); return result; } // case 4: account does not exist return new Bundle(); } |
The first part of the code checks whether a previous access token exists using getAccessTokenFromCache method from AccountManager. If none exists, it instead looks for any refresh token saved as password using accountManager.getPassword(account) and after returning, the accountManagerCallback contacts the server to get access token from it. The third possibility –no access or refresh token is available yet the account exists– means the refresh token is either expired or reset from the server and new sign-in is required to get new ones from the server, which is dealt with in accountManagerCallback. If neither happens, it implies that there is no such account available and the user should be informed to add one first in accountManagerCallback. Thus, the callback is coded as:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
private AccountManagerCallback getAccessTokenCallBack(final String authTokenType, final Bundle options, final Account account) { AccountManagerCallback accountManagerCallback = new AccountManagerCallback() { @Override public void run(AccountManagerFuture future) { try { Bundle bundle = (Bundle) future.getResult(); String accountName = bundle.getString(AccountManager.KEY_ACCOUNT_NAME, null); String refreshToken = bundle.getString(AccountManager.KEY_PASSWORD, null); String authToken = bundle.getString(AccountManager.KEY_AUTHTOKEN, null); if (authToken != null) { signInAndDoAfter(account, authTokenType, authToken, options); } else { if (refreshToken != null) { signUpAndDoAfter(account, authTokenType, refreshToken, options); } else { if (accountName != null) { addAccount(authTokenType, null, options); } else { Toast.makeText(context, context.getString(R.string.auth_msg_account_does_not_exist), Toast.LENGTH_SHORT).show(); } } } } catch (OperationCanceledException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (AuthenticatorException e) { e.printStackTrace(); } } }; return accountManagerCallback; } |
The complete code for this class is included in my GitHub library.
As described in the previous part,
AccountManager gets informed about the class which extends the
AbstractAccountAuthenticator class (
AppAccountAuthenticator here) through a bound
Service. Invoking
accountManager.getAuthToken results in a system broadcast with the action name
android.accounts.AccountAuthenticator which triggers the service instantiating the
AppAccountAuthenticator. The concluding section of part two gives more details about this.
This wraps it up for the account manager and account authentication in android, which is the basis of my GitHub library for account authentication. In the last part of this series, I am going to demonstrate the application of this library for account authentication by providing the complete code of a sample app.