why we use Accountmanager.addAccount() when we can create account with all parameters with Accountmanager.addAccountExplicitly().
I googled and find out when we use Accountmanager.addAccount() it trigles AbstractAccountAuthenticator addAccount event but what is the point?
why we should using addAccount method?
UPDATED
we can create account in this way:
Account account = new Account(accountname, accountType);
mAccountManager.addAccountExplicitly(account, accountPassword, null);
I finally find out after many tries!
Accountmanager.addAccount()
and
Accountmanager.addAccountExplicitly() are very different methods!
when you call Accountmanager.addAccount() it's call a same method that in your AbstractAccountAuthenticator you can handle what happens. in other hand when user go to phone settings/account and select your custom account type and press "add an account" this method will call. so I handle account-type and many stuff and direct user to login/singup page.
public class MyAuthenticator extends AbstractAccountAuthenticator {
#Override
public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) throws NetworkErrorException {
final Intent intent = new Intent(mContext, DirectLogin.class);
intent.putExtra(Constants.ARG_ACCOUNT_TYPE, accountType);
intent.putExtra(Constants.ARG_AUTH_TYPE, authTokenType);
intent.putExtra(Constants.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;
}
then in my activiy user chose to create an account or sign in. after sing-in or sign-up user get tokens from server and action completes and finally I use Accountmanager.addAccountExplicitly() to add account.
String accountName = "name";
String accountPassword = "password";
final Account account = new Account(accountName, "account_type");
AccountManager mAccountManager = AccountManager.get(getBaseContext());
String authToken = intent.getStringExtra(AccountManager.KEY_AUTHTOKEN);
String refreshToken = intent.getStringExtra(AccountManager.KEY_USERDATA);
String authTokenType = AccountGeneral.ACCOUNT_TYPE;
mAccountManager.addAccountExplicitly(account, accountPassword, null);
mAccountManager.setAuthToken(account, authTokenType, authToken);
mAccountManager.setUserData(account, "refreshToken", refreshToken);
Accountmanager.addAccount() must be use to ask the user to create an account of some type. The user have to approve and interact with the device so that the account get indeed created. This method can be use to create an account of any type.
AccountManager.addAccountExplicitely() allows you to create an Account without user approval/interaction, but it is limited to account type for which the authenticator have the same signature as yours.
Related
i'm new with android and i need to do a connection with server with oauth 2.0 i looked in internet and found just example how to dowit with google or github but my need is to connect with my own server i have the clientId clientSecret and the scope all i need is to get the token correctly
i hope my question is clear
thank you
this what i have donne
AccountManager am = AccountManager.get(Authentification.this);
Bundle options = new Bundle();
options.putSerializable("numero", numero);
am.getAuthToken(
null,
"whrite",
options,
this,
new OnTokenAcquired(),
null);
private class OnTokenAcquired implements AccountManagerCallback<Bundle> {
#Override
public void run(AccountManagerFuture<Bundle> result) {
// Get the result of the operation from the AccountManagerFuture.
try{
Bundle bundle = result.getResult();
// The token is a named value in the bundle. The name of the value
// is stored in the constant AccountManager.KEY_AUTHTOKEN.
String token = bundle.getString(AccountManager.KEY_AUTHTOKEN);
System.out.println("================>>>>"+token);
}catch(Exception e){
}
}
}
I would start with the AppAuth code sample. My blog post
has step by step instructions on how to run it.
Once it is working reconfigure it to point to your own Authorization Server.
I'm implementing the android account authenticator, so far I can add accounts get a token etc.
The problem is when I change credential server side.
As I can't get notified from the server if credentials changed, my next API request will be denied as the token is not longer valid.
After getting a request denied there could be 2 reasons for it -> token expired or credentials no longer valid
When this happen I invalidate the token saved and call getAuthToken()
In my getAuthToken() I first attempt a request for a new token, if it gets denied means that credential are not ok anymore so I need to prompt login activity.
The problem is AccountAuthenticatorResponse.onError seems only able to log the error and that's it.
I tried to use AccountAuthenticatorResponse.onResult passing the bundle with the KEY_INTENT for login activity but it does't do anything.
Any thoughts?
#Override
public Bundle getAuthToken(final AccountAuthenticatorResponse authenticatorResponse, final Account account,
final String authTokenType, Bundle bundle) throws NetworkErrorException {
//Get the account manager to access the account details
final AccountManager accountManager = AccountManager.get(mContext);
String authToken = accountManager.peekAuthToken(account, authTokenType);
//If auth token is null then try to log in the user with the stored credentials
//It could be that previous token has expired
if (authToken == null) {
final String password = accountManager.getPassword(account);
final String clientID = accountManager.getUserData(account, CLIENT_ID_KEY);
final String apiSecret = accountManager.getUserData(account, API_SECRET_KEY);
final String serverUrl = accountManager.getUserData(account, SERVER_ADDRESS_KEY);
if (password != null && clientID != null && apiSecret != null && serverUrl != null) {
Logger.log(LOG_TAG, "Requesting new token...", Log.VERBOSE);
ApiRequestManager.getInstance(mContext)
.getToken(serverUrl, clientID, apiSecret, account.name, password,
new NetworkCallBack() {
#Override
public void tokenReceived(Token JsonToken) {
//Credentials still valid, token received
//Returning data back to the account authenticator
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, JsonToken.getAccess_token());
authenticatorResponse.onResult(result);
}
#Override
public void errorReceivingToken(VolleyError errorResponse) {
//If we are here with error 400 it only means credentials have changed
//I should prompt LogIn activity at this point
if (errorResponse.networkResponse.statusCode == 400) {
Bundle loginActivityBundle =
promptLoginActivity(authenticatorResponse, account.type, authTokenType, null);
// authenticatorResponse.onResult(loginActivityBundle);
authenticatorResponse.onError(errorResponse.networkResponse.statusCode, "error");
}
}
});
return null;
}
}
//If we got an authToken return the account and login info in a bundle
if (authToken != null) {
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 are here then means either we do not have an account signed
//or credentials are no longer valid -> prompt login procedure again
return promptLoginActivity(authenticatorResponse, account.type, authTokenType, null);
}
I found the solution.
When I invoke getAuthToken() i pass an accountManagerCallback which receives either success or fail, from here I can prompt logIn if it was a fail due to auth exception.
I use a SyncAdapter and an AccountAuthenticator in my app. When doing the sync stuff, I call AccountManager.blockingGetAuthToken to get an access token. I understand this method that way, that it starts my Log-in activity when it can not get a token (or in other words, when the getAuthToken methods returns an Intent to start the Activity).
But it just returns null, without launching the Activity.
This is the getAuthToken method from my authenticator.
#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 (TextUtils.isEmpty(authToken)) {
final String password = am.getPassword(account);
if (password != null) {
try {
authToken = APIHelper.getInstance().logIn(account.name, password);
} catch (IOException e) {
e.printStackTrace();
}
}
}
// If we get an authToken - we return it
if (!TextUtils.isEmpty(authToken)) {
// cache
am.setAuthToken(account, authTokenType, 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, AuthActivity.class);
intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
intent.putExtra(AuthActivity.ARG_ACCOUNT_TYPE, account.type);
intent.putExtra(AuthActivity.ARG_AUTH_TYPE, authTokenType);
final Bundle bundle = new Bundle();
bundle.putParcelable(AccountManager.KEY_INTENT, intent);
return bundle;
}
By the way, most of the code is from
this blog.
It seems the asker figured it out. For posterity, it works if you use this instead:
AccountManagerFuture<Bundle> resultFuture = accountManager.getAuthToken(
account,
AUTH_TOKEN_TYPE,
null,
activity,
null,
null
);
Bundle bundle = resultFuture.getResult();
return bundle.getString(AccountManager.KEY_AUTHTOKEN);
I guess blockingGetAuthToken() is unable to do this automatically because it lacks the activity parameter. And the documentation is incorrect.
I'm trying to add a new account (after a Facebook login + server
validation) in AccountManager. The flow for this case is like this:
User login with Facebook
I got the details after the login is done and I validate them against the data I have on my server
If everything is ok, the server send back an auth_token (JWT token)
Having user's details and the auth_token I'm creating an account via AccountManager and once it is created, I set the authToken for
it.
On next login when the user will re-open the app I call getAuthToken which first try to get the cached authToken by calling peekAuthToken().
The problem
At point 5, peekAuthToken returns null but it shouldn't because I already set the autToken for that account.
Code
public static Bundle handleUserLogin(Context context, User user) {
SharedPreferences mPrefs = context.getSharedPreferences(Constants.PREFS_NAME, Context.MODE_PRIVATE);
AccountManager am = AccountManager.get(context);
Account account = new Account(user.getEmail(), ACCOUNT_TYPE);
Account[] accounts = am.getAccountsByType(ACCOUNT_TYPE);
boolean isNewAccount = true;
for (int i = 0; i < accounts.length; i++) {
if (user.getEmail().equalsIgnoreCase(accounts[i].name) && ACCOUNT_TYPE.equalsIgnoreCase(accounts[i].type)) {
isNewAccount = false;
account = accounts[i];
break;
}
}
if (isNewAccount) {
am.addAccountExplicitly(account, user.getPassword(), null);
accounts = am.getAccountsByType(ACCOUNT_TYPE);
for (int i = 0; i < accounts.length; i++) {
if (user.getEmail().equalsIgnoreCase(accounts[i].name) && ACCOUNT_TYPE.equalsIgnoreCase(accounts[i].type)) {
account = accounts[i];
break;
}
}
}
if (null != user.getPassword()) {
am.setPassword(account, user.getPassword());
}
Cs.error(TAG, "account " + account + " token " + user.getToken());
am.setAuthToken(account, user.getToken(), Authenticator.AUTHTOKEN_TYPE_FULL_ACCESS);
setUserData(user, account, am);
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, user.getToken());
mPrefs.edit().putString(Constants.KEY_CURRENT_USER, account.name).commit();
return result;
}
First I thought that maybe the reference to my new account is not the correct one (ex the one from AccountManager) so I search for account again.
Could you give me some indications about what I'm doing wrong or how should I make sure the authToken will be set for an account?
Thank you
I would not rely on explicit setting authToken in place different than in AbstractThreadedAccountAuthenticator's getAuthToken() method.
My guess is that the authToken is not cached yet when you request it.
I suggest just creating account via AccountManager.addAccountExplicitly() and deferring accessing authToken once you really need it.
In AbstractThreadedAccountAuthenticator's getAuthToken() I suggest applying logic like in the project I created a while ago.
Request authToken via AccountManager.peekAuthToken()
If authToken is set in cache then return it.
If not then request it from your server then cache via AccountManager.setAuthToken() and finally return.
Link: https://github.com/dawidgdanski/AccountAuthenticatorExample/blob/master/app/src/main/java/com/authenticator/account/auth/SimpleAuthenticator.java
Hope this will help you out:
https://github.com/dawidgdanski/AccountAuthenticatorExample
Well i need to authorize Google Calendar's access for a user, first google Id works fine when i use
blockingGetAuthToken
and it gets a token, i usually log on this token.
So when i tried to use other accounts i got a null token.
I searched a lot and found out that using getAuthToken is preferred as it uses a context from the activity calling it.. then i converted the whole process to use it
private static final String AUTH_TOKEN_TYPE = "oauth2:https://www.googleapis.com/auth/calendar";
public static String authorize(AndroidtestActivity parent, Account account) {
AccountManager accountManager = AccountManager.get(parent);
Bundle options= new Bundle();
Log.d("MyAPP", "Get Authorization");
try {
AccountManagerFuture<Bundle> acc=accountManager.getAuthToken ( account, AUTH_TOKEN_TYPE, options, true, null, null);
Bundle authTokenBundle = acc.getResult();
String authToken = authTokenBundle.get(AccountManager.KEY_AUTHTOKEN).toString();
Log.d("MyAPP","Token= "+authToken);
return authToken;
} catch (Exception ex) {
Logger.getLogger(GoogleAuthorize.class.getName()).log(Level.SEVERE,
null, ex);
}
return null;
}
}
but still no accounts could get a valid token, they all get a null one
then i saw this answer https://stackoverflow.com/a/2021337/1280902 and followed using invalidateAuthToken
private static final String AUTH_TOKEN_TYPE = "oauth2:https://www.googleapis.com/auth/calendar";
public static String authorize(AndroidtestActivity parent, Account account) {
AccountManager accountManager = AccountManager.get(parent);
Bundle options= new Bundle();
Log.d("MyAPP", "Get Authorization");
try {
AccountManagerFuture<Bundle> acc=accountManager.getAuthToken ( account, AUTH_TOKEN_TYPE, options, true, null, null);
Bundle authTokenBundle = acc.getResult();
String authToken = authTokenBundle.get(AccountManager.KEY_AUTHTOKEN).toString();
accountManager.invalidateAuthToken("com.google",authToken);
acc=accountManager.getAuthToken ( account, AUTH_TOKEN_TYPE, options, true, null, null);
authTokenBundle = acc.getResult();
authToken = authTokenBundle.get(AccountManager.KEY_AUTHTOKEN).toString();
Log.d("MyAPP","Token= "+authToken);
return authToken;
} catch (Exception ex) {
Logger.getLogger(GoogleAuthorize.class.getName()).log(Level.SEVERE,
null, ex);
}
return null;
}
}
but i had the same problem on every account i use, even the one that used to work at the beginning with blockingGetAuthToken
So am i missing something?
Ok it works fine when i use
getAuthToken (Account account, String authTokenType, Bundle options, Activity activity, AccountManagerCallback<Bundle> callback, Handler handler)
The activity parameter solved the problem..