During an implementation of android account authentication I came along this sample(which is very helpful if anyone wants to learn the process).
Here is some of the relevant code and I have 2 questions at the end.
public class MainActivity extends Activity {
private static final int REQ_SIGNUP = 1;
private AccountManager mAccountManager;
private AuthPreferences mAuthPreferences;
private String authToken;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
authToken = null;
mAuthPreferences = new AuthPreferences(this);
mAccountManager = AccountManager.get(this);
// Ask for an auth token
mAccountManager.getAuthTokenByFeatures(AccountUtils.ACCOUNT_TYPE, AccountUtils.AUTH_TOKEN_TYPE, null, this, null, null, new GetAuthTokenCallback(), null);
}
private class GetAuthTokenCallback implements AccountManagerCallback<Bundle> {
@Override
public void run(AccountManagerFuture<Bundle> result) {
Bundle bundle;
try {
bundle = result.getResult();
final Intent intent = (Intent) bundle.get(AccountManager.KEY_INTENT);
if (null != intent) {
startActivityForResult(intent, REQ_SIGNUP);
} else {
authToken = bundle.getString(AccountManager.KEY_AUTHTOKEN);
final String accountName = bundle.getString(AccountManager.KEY_ACCOUNT_NAME);
// Save session username & auth token
mAuthPreferences.setAuthToken(authToken);
mAuthPreferences.setUsername(accountName);
// If the logged account didn't exist, we need to create it on the device
Account account = AccountUtils.getAccount(MainActivity.this, accountName);
if (null == account) {
account = new Account(accountName, AccountUtils.ACCOUNT_TYPE);
mAccountManager.addAccountExplicitly(account, bundle.getString(LoginActivity.PARAM_USER_PASSWORD), null);
mAccountManager.setAuthToken(account, AccountUtils.AUTH_TOKEN_TYPE, authToken);
}
}
} catch(OperationCanceledException e) {
// If signup was cancelled, force activity termination
finish();
} catch(Exception e) {
e.printStackTrace();
}
}
}
.
.
.
}
This is the code for AccountAuthenticator:
public class AccountAuthenticator extends AbstractAccountAuthenticator {
private final Context mContext;
public AccountAuthenticator(Context context) {
super(context);
mContext = context;
}
@Override
public Bundle addAccount(
AccountAuthenticatorResponse response,
String accountType,
String authTokenType,
String[] requiredFeatures,
Bundle options)
throws NetworkErrorException
{
Bundle reply = new Bundle();
Intent intent = new Intent(mContext, LoginActivity.class);
intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
intent.putExtra(LoginActivity.ARG_ACCOUNT_TYPE, accountType);
intent.putExtra(LoginActivity.ARG_AUTH_TOKEN_TYPE, authTokenType);
intent.putExtra(LoginActivity.ARG_IS_ADDING_NEW_ACCOUNT, true);
// return our AccountAuthenticatorActivity
reply.putParcelable(AccountManager.KEY_INTENT, intent);
return reply;
}
@Override
public Bundle confirmCredentials(AccountAuthenticatorResponse arg0,
Account arg1, Bundle arg2) throws NetworkErrorException {
return null;
}
@Override
public Bundle editProperties(AccountAuthenticatorResponse arg0, String arg1) {
return null;
}
@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
// the server for an appropriate AuthToken.
final AccountManager am = AccountManager.get(mContext);
String authToken = am.peekAuthToken(account, authTokenType);
// Lets give another try to authenticate the user
if (null != authToken) {
if (authToken.isEmpty()) {
final String password = am.getPassword(account);
if (password != null) {
authToken = AccountUtils.mServerAuthenticator.signIn(account.name, password);
}
}
}
// If we get an authToken - we return it
if (null != authToken) {
if (!authToken.isEmpty()) {
final Bundle result = new Bundle();
result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
result.putString(AccountManager.KEY_AUTHTOKEN, authToken);
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);
intent.putExtra(LoginActivity.ARG_ACCOUNT_TYPE, account.type);
intent.putExtra(LoginActivity.ARG_AUTH_TOKEN_TYPE, authTokenType);
// This is for the case multiple accounts are stored on the device
// and the AccountPicker dialog chooses an account without auth token.
// We can pass out the account name chosen to the user of write it
// again in the Login activity intent returned.
if (null != account) {
intent.putExtra(AccountManager.KEY_ACCOUNT_NAME, account.name);
}
final Bundle bundle = new Bundle();
bundle.putParcelable(AccountManager.KEY_INTENT, intent);
return bundle;
}
@Override
public String getAuthTokenLabel(String arg0) {
return null;
}
@Override
public Bundle hasFeatures(AccountAuthenticatorResponse arg0, Account arg1,
String[] arg2) throws NetworkErrorException {
return null;
}
@Override
public Bundle updateCredentials(AccountAuthenticatorResponse arg0,
Account arg1, String arg2, Bundle arg3)
throws NetworkErrorException {
return null;
}
}
And this is the AccountAuthenticatorActivity:
public class LoginActivity extends AccountAuthenticatorActivity {
public static final String ARG_ACCOUNT_TYPE = "accountType";
public static final String ARG_AUTH_TOKEN_TYPE = "authTokenType";
public static final String ARG_IS_ADDING_NEW_ACCOUNT = "isAddingNewAccount";
public static final String PARAM_USER_PASSWORD = "password";
private AccountManager mAccountManager;
.
.
.
Intent res = new Intent
res.putExtra(AccountManager.KEY_ACCOUNT_NAME, mEmail);
res.putExtra(AccountManager.KEY_ACCOUNT_TYPE, AccountUtils.ACCOUNT_TYPE);
res.putExtra(AccountManager.KEY_AUTHTOKEN, authToken);
res.putExtra(PARAM_USER_PASSWORD, mPassword);
finishLogin(res);
.
.
.
private void finishLogin(Intent intent) {
final String accountName = intent.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
final String accountPassword = intent.getStringExtra(PARAM_USER_PASSWORD);
final Account account = new Account(accountName, intent.getStringExtra(AccountManager.KEY_ACCOUNT_TYPE));
String authToken = intent.getStringExtra(AccountManager.KEY_AUTHTOKEN);
if (getIntent().getBooleanExtra(ARG_IS_ADDING_NEW_ACCOUNT, false)) {
// Creating the account on the device and setting the auth token we got
// (Not setting the auth token will cause another call to the server to authenticate the user)
mAccountManager.addAccountExplicitly(account, accountPassword, null);
mAccountManager.setAuthToken(account, AccountUtils.AUTH_TOKEN_TYPE, authToken);
} else {
mAccountManager.setPassword(account, accountPassword);
}
setAccountAuthenticatorResult(intent.getExtras());
setResult(AccountAuthenticatorActivity.RESULT_OK, intent);
finish();
}
}
}
I have two questions:
There is a comment in the MainActivity: "If the logged account didn't exist, we need to create it on the device". How this scenario can happen that the account from which we just got the auth token does not exist and we need to add it by the data we just got from it?
In this question the username(email here) has been saved as the account_name. I suppose that's just for test implementation, but in general if I want to save some Data about my user that I want to save in the account, like username, etc., How can I do that? Lets say In contrast with adding an account password which is like this:
mAccountManager.addAccountExplicitly(account, accountPassword, null);