Android : All app can access my password in AccountManager

Viewed 1971

here's my problem :

I have a programme that need a password to work so I thought I could make an AccountManager. I took the code from this tutorial and it works just fine : I have a new account in setting->accounts !

BUT to test if it was safe I did another programme (lets call it Prog2 and the firt one Prog1) with the same code and supprise, I have full access to the password of the account I created with Prog2 with Prog1.

I know that it's possible to secure the account cause I tried with google and facebook accounts and I could not have access to their password.

Here's my code :

PS : if at the end of a String there is a '1' it's because I changed it in Prog1 and not in Prog2 to test if these variable had some effect

Authenticator

public class Authenticator extends AbstractAccountAuthenticator {

private String TAG = "CoderzHeavenAuthenticator";
private final Context mContext;

public Authenticator(Context context) {
    super(context);

    // I hate you! Google - set mContext as protected!
    this.mContext = context;
}

@Override
public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) throws NetworkErrorException {
    Log.d("CoderzHeaven", TAG + "> addAccount");

    final Intent intent = new Intent(mContext, AuthenticatorActivity.class);
    intent.putExtra(AuthenticatorActivity.ARG_ACCOUNT_TYPE, accountType);
    intent.putExtra(AuthenticatorActivity.ARG_AUTH_TYPE, authTokenType);
    intent.putExtra(AuthenticatorActivity.ARG_IS_ADDING_NEW_ACCOUNT, true);
    intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);

    final Bundle bundle = new Bundle();
    bundle.putParcelable(AccountManager.KEY_INTENT, intent);
    return bundle;
}

@Override
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {

    Log.d("CoderzHeaven", TAG + "> getAuthToken");

    // If the caller requested an authToken type we don't support, then
    // return an error
    if (!authTokenType.equals(AccountGeneral.AUTHTOKEN_TYPE_READ_ONLY) && !authTokenType.equals(AUTHTOKEN_TYPE_FULL_ACCESS)) {
        final Bundle result = new Bundle();
        result.putString(AccountManager.KEY_ERROR_MESSAGE, "invalid authTokenType");
        return result;
    }

    // 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);

    Log.d("CoderzHeaven", TAG + "> peekAuthToken returned - " + authToken);

    // Lets give another try to authenticate the user
    if (TextUtils.isEmpty(authToken)) {
        final String password = am.getPassword(account);
        if (password != null) {
            try {
                Log.d("CoderzHeaven", TAG + "> re-authenticating with the existing password");
                //authToken = sServerAuthenticate.userSignIn(account.name, password, authTokenType);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    // If we get an authToken - we return it
    if (!TextUtils.isEmpty(authToken)) {
        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, AuthenticatorActivity.class);
    intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
    intent.putExtra(AuthenticatorActivity.ARG_ACCOUNT_TYPE, account.type);
    intent.putExtra(AuthenticatorActivity.ARG_AUTH_TYPE, authTokenType);
    intent.putExtra(AuthenticatorActivity.ARG_ACCOUNT_NAME, account.name);
    final Bundle bundle = new Bundle();
    bundle.putParcelable(AccountManager.KEY_INTENT, intent);
    return bundle;
}


@Override
public String getAuthTokenLabel(String authTokenType) {
    if (AUTHTOKEN_TYPE_FULL_ACCESS.equals(authTokenType))
        return AUTHTOKEN_TYPE_FULL_ACCESS_LABEL;
    else if (AUTHTOKEN_TYPE_READ_ONLY.equals(authTokenType))
        return AUTHTOKEN_TYPE_READ_ONLY_LABEL;
    else
        return authTokenType + " (Label)";
}

@Override
public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) throws NetworkErrorException {
    final Bundle result = new Bundle();
    result.putBoolean(KEY_BOOLEAN_RESULT, false);
    return result;
}

@Override
public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
    return null;
}

@Override
public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options) throws NetworkErrorException {
    return null;
}

@Override
public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
    return null;
}
}

AuthenticatorService

public class AuthenticatorService extends Service {

private Authenticator authenticator;

public AuthenticatorService() {
    super();
}

public IBinder onBind(Intent intent) {
    IBinder ret = null;
    if (intent.getAction().equals(android.accounts.AccountManager.ACTION_AUTHENTICATOR_INTENT))
        ret = getAuthenticator().getIBinder();
    return ret;
}

private Authenticator getAuthenticator() {
    if (authenticator == null)
        authenticator = new Authenticator(this);
    return authenticator;
}

}

AuthenticatorActivity

public class AuthenticatorActivity extends AccountAuthenticatorActivity implements OnClickListener{

public final static String ARG_ACCOUNT_TYPE = "ACCOUNT_TYPE1";
public final static String ARG_AUTH_TYPE = "AUTH_TYPE1";
public final static String ARG_ACCOUNT_NAME = "ACCOUNT_NAME1";
public final static String ARG_IS_ADDING_NEW_ACCOUNT = "IS_ADDING_ACCOUNT1";

public static final String KEY_ERROR_MESSAGE = "ERR_MSG1";

public final static String PARAM_USER_PASS = "USER_PASS1";

private final String TAG = this.getClass().getSimpleName();

private AccountManager mAccountManager;
private String mAuthTokenType;
String authtoken = "12345678910"; // this
String password = "1234510";

String accountName;

public Account findAccount(String accountName) {
    for (Account account : mAccountManager.getAccounts())
        if (TextUtils.equals(account.name, accountName) && TextUtils.equals(account.type, getString(R.string.auth_type))) {
            System.out.println("FOUND");
            return account;
        }
    return null;
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.act_login);

    Log.d(TAG, "onCreate");

    mAccountManager = AccountManager.get(getBaseContext());

    // If this is a first time adding, then this will be null
    accountName = getIntent().getStringExtra(ARG_ACCOUNT_NAME);
    mAuthTokenType = getIntent().getStringExtra(ARG_AUTH_TYPE);

    if (mAuthTokenType == null)
        mAuthTokenType = getString(R.string.auth_type);

    findAccount(accountName);

    System.out.println(mAuthTokenType + ", accountName : " + accountName);

    ((Button)findViewById(R.id.submit)).setOnClickListener(this);
}

void userSignIn() {

    // You should probably call your server with user credentials and get
    // the authentication token here.
    // For demo, I have hard-coded it.
    authtoken = "12345678910";

    accountName = ((EditText) findViewById(R.id.accountName)).getText().toString().trim();
    password = ((EditText) findViewById(R.id.accountPassword)).getText().toString().trim();

    if (accountName.length() > 0) {
        Bundle data = new Bundle();
        data.putString(AccountManager.KEY_ACCOUNT_NAME, accountName);
        data.putString(AccountManager.KEY_ACCOUNT_TYPE, mAuthTokenType);
        data.putString(AccountManager.KEY_AUTHTOKEN, authtoken);
        data.putString(PARAM_USER_PASS, password);

        // Some extra data about the user
        Bundle userData = new Bundle();
        userData.putString("UserID", "25");
        data.putBundle(AccountManager.KEY_USERDATA, userData);

        //Make it an intent to be passed back to the Android Authenticator
        final Intent res = new Intent();
        res.putExtras(data);

        //Create the new account with Account Name and TYPE
        final Account account = new Account(accountName, mAuthTokenType);

        //Add the account to the Android System
        if (mAccountManager.addAccountExplicitly(account, password, userData)) {
            // worked
            Log.d(TAG, "Account added");
            mAccountManager.setAuthToken(account, mAuthTokenType, authtoken);
            setAccountAuthenticatorResult(data);
            setResult(RESULT_OK, res);
            finish();
        } else {
            // guess not
            Log.d(TAG, "Account NOT added");
        }

    }
}

@Override
public void onClick(View v) {
    userSignIn();       
}

}

AccountGeneral

public class AccountGeneral {

/**
 * Account name
 */
public static final String ACCOUNT_NAME = "CoderzHeaven1";

/**
 * Auth token types
 */
public static final String AUTHTOKEN_TYPE_READ_ONLY = "Read only1";
public static final String AUTHTOKEN_TYPE_READ_ONLY_LABEL = "Read only access to an CoderzHeaven account1";

public static final String AUTHTOKEN_TYPE_FULL_ACCESS = "Full access1";
public static final String AUTHTOKEN_TYPE_FULL_ACCESS_LABEL = "Full access to an CoderzHeaven account1";

}
1 Answers
Related