Show full screen Access Request dialog instead of notification when using getAuthToken (...)

I built an AccountAuthenticator for a web service that I would like to use in other applications with different signatures. I would like to immediately show a full-screen request dialog (this one: http://i.imgur.com/gcndGZs.png ) instead of a notification so that I can be sure that I have access to my account before setting up SyncAdapter for automatic synchronization .

So far I have tried getAuthToken, one of which shows a notification:

manager.getAuthToken(account, "full", null, true, callback, new Handler());

The other throws an exception:

manager.getAuthToken(account, "full", null, activity, callback, new Handler());

 java.lang.SecurityException: Activity to be started with KEY_INTENT must share Authenticator signatures at com.android.server.accounts.AccountManagerService$Session.onResult(AccountManagerService.java:2206) at com.android.server.accounts.AccountManagerService$6.onResult(AccountManagerService.java:1411) at com.android.server.accounts.AccountManagerService$6.onResult(AccountManagerService.java:1386) at android.accounts.IAccountAuthenticatorResponse$Stub.onTransact(IAccountAuthenticatorResponse.java:59) at android.os.Binder.execTransact(Binder.java:404) at dalvik.system.NativeStart.run(Native Method)` 

Is it possible to display the access request dialog without notification from an application that was signed with a different key?

+3
source share
3 answers

This seems to be a bug (or security feature) in KitKat, preventing token sharing. I recommend that you implement token exchanges using your own user intentions and security checks, rather than relying on the Android API.

+2
source

There is no good tutorial in this workflow, but I was finally able to get it to work in my application. I am adding this answer for posterity, for someone else who is faced with this particular use case.

Background task

 manager.getAuthToken(account, "full", null, true, callback, new Handler()); 

The above expects you to work in some background task where you do not need to immediately display the interface. This explains the java document of this method. Unfortunately, if you use the false flag, you will not receive the callback that occurred in the SecurityException.

Foreground launch

 manager.getAuthToken(account, "full", null, activity, callback, new Handler()); 

This expects the application to call this method to share the same authenticator signing certificate. Here I and many people hanged themselves. I tried several versions of Android from 4.0.1 to 5.0 and this seems to be the intended behavior, not a bug in their code.

Decision

Use another tool to get an authorization token. in particular, confirm the credentials of the required account.

 mgr.confirmCredentials(account, null, activity, callback, null); 

By supporting Confirm Credentials, you can send an intent to run AccountAuthenticatorActivity through Authenticator and avoid a security exception. You have full control over the user interface here, so although you can usually request a new password for the account, instead you can show the page with a button to confirm that the account should be used in the calling application. Then all you have to do is send the authentication token to the AccountManagerCallback package, returning to the calling application.

Callback Method Example

 new AccountManagerCallback<Bundle>() { @Override public void run(AccountManagerFuture<Bundle> bundle) { boolean success = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT); if (success) { final String token = result.getString(AuthenticationHelper.ACCESS_TOKEN); //cache token somewhere local } } } 
0
source

You are using the wrong method. See the documentation:
- getAuthToken growing notification:
- getAuthToken asks the user for credentials if necessary

An example of the method you are looking for:

 Bundle options = new Bundle(); accountManager.getAccountManager().getAuthToken(account, SyncUtils.AUTH_TOKEN_TYPE, options, Accounts.this, new AccountManagerCallback<Bundle>() { @Override public void run(AccountManagerFuture<Bundle> future) { Bundle bundle = bundle = future.getResult(); if(bundle == null) return; if (bundle.containsKey(AccountManager.KEY_INTENT)) { Intent intent = bundle.getParcelable(AccountManager.KEY_INTENT); intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_NEW_TASK); startActivityForResult(intent, SyncUtils.REQUEST_AUTHENTICATE); } else if (bundle.containsKey(AccountManager.KEY_AUTHTOKEN)) { ... credential.setAccessToken(bundle.getString(AccountManager.KEY_AUTHTOKEN)); setUI(); } } }, null); 
-1
source

All Articles