2

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";

}
Kolopox
  • 276
  • 1
  • 4
  • 17

1 Answers1

1

As the Google documentation tell,AccountManager is not an encryption service. See here

It's betcome an isue with rooted device. Or "you should store a cryptographically secure token that would be of limited use to an attacker" (from Google documentation)

Read this too

EDIT :

If you have access with your Program2 it's because you use (or not at all) the same keystore to sign your apk. An application with the same signature can access to the accountManager field

Timo
  • 497
  • 10
  • 21
  • For the moment I don't care if it's encrypted or not, I just want to know why linux thinks my 2 différents app have the same UID. With the methode getPassword(Account) I can access my second app password with my first one, at least that should be protected no ? – Kolopox May 31 '17 at 14:05
  • Dummmy question did your two project share the same package structure (com.example) ? Did you try on an Android Rooted device or AVD ? – Timo Jun 01 '17 at 06:12
  • **Package 1** : com.example.mysociety.testaccountmanager **Package 2** : com.example.mysociety.testvoila. Does that mean that if someone create a project with package com.example.mysociety he can access all my password ? And I tried it on my phone that is not rooted. – Kolopox Jun 01 '17 at 07:29
  • This is the package name you write in your manifest ? I know on Play store you can't have two application with the same package name. Have you try Package 2 : me.example.test (with distinct package names). And the main purpose of AccountManager is to share credential with other application (Maybe I'm wrong) like facebook. You said you try with facebook what did you get from AccountManager I'm curious – Timo Jun 01 '17 at 07:44
  • I don't think you wrong and I think the AccountManager isn't really good for what I'm trying to do but my "coworker" leave me no choice... And I tried to change the package as your proposed but it's the same, I can read the password of the account I created with Prog2 with Prog1 – Kolopox Jun 01 '17 at 07:59
  • If you want keep a secret you could use a file which will be only readble by the application. And keep using the accountManager to interface it with other services. Like I said you should store only hash of the password in the password field. Could you take a look of what is store in the facebook account ? or Google – Timo Jun 01 '17 at 08:26
  • ^^ my goal is not to share it with other service so AccountManager is totaly useless on my situation I guess... And no facebook or google account can't be access by my app, that's why I want to do the same but I keep getting access to it don't know why – Kolopox Jun 01 '17 at 09:05
  • No you misunderstood, I can see them (name and type) whitout problem but I can't have access to the password of the account. They succeeded at secure it (I tried to change my package to com.google but I still can't access it). I want to do the same, have an app in account where people can see name and type but not the password I enter – Kolopox Jun 01 '17 at 09:32
  • oh ! Yeah I don't use a keystore I think, do you have a code example to show me how I can correct my programme ? – Kolopox Jun 01 '17 at 09:57
  • You don't have to fix just sign tour apk – Timo Jun 01 '17 at 10:19
  • oh ! You mean sign it when I post it on google play ? I can't test right now but I will and edit my answer if it works. Thanks a lot for helping me, I still don't really understand how to use AccountManager well (with credential, etc) but now I think I can understand it better. Thanks again – Kolopox Jun 01 '17 at 10:35