使用getAuthToken(...)时显示全屏“访问请求”对话框而不是通知

时间:2013-12-11 18:50:39

标签: android

我为Web服务构建了一个AccountAuthenticator,我想在其他具有不同签名的应用程序中使用它。我想立即显示全屏访问请求对话框(这一个:http://i.imgur.com/gcndGZs.png)而不是通知,以便我确定在将SyncAdapter设置为自动同步之前我可以访问该帐户。

到目前为止,我已经尝试了getAuthToken,其中一个显示了通知:

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

另一个抛出异常:

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

java.lang.SecurityException: Activity to be started with KEY_INTENT must share Authenticator's 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)`

是否可以在没有来自使用其他密钥签名的应用的通知的情况下显示访问请求对话框?

3 个答案:

答案 0 :(得分:2)

这似乎是KitKat中的一个错误(或安全功能),阻止了跨应用程序令牌共享。我建议您使用自己的自定义意图和安全验证来实现令牌共享,而不是依赖于Android的API。

答案 1 :(得分:0)

这个工作流程没有很好的教程,但我终于能够在我的应用程序中使用它了。对于遇到这个特定用例的其他人,我正在为后代添加这个答案。

作为后台任务运行

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

以上期望您在某些后台任务中运行,您无需立即显示UI。这个方法的java文档解释了这一点。遗憾的是,如果使用“false”标志,则不会收到SecurityException发生的回调。

在前景中运行

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

这要求调用此方法的应用程序共享验证者的相同签名证书。这是我和许多人被挂断的地方。我尝试了从4.0.1开始到5.0的几个版本的android,它似乎是预期的行为,而不是他们代码中的错误。

解决方案

使用不同的方式获取身份验证令牌。具体确认您需要的帐户凭据。

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

通过支持Confirm Credentials,您可以通过Authenticator发送启动AccountAuthenticatorActivity的意图,并避免安全例外。您可以在此处完全控制用户界面,因此通常您可以为该帐户申请新密码,您可以显示一个带有按钮的页面,以确认该帐户应该在调用应用程序中使用。然后,您所要做的就是将AccountManagerCallback包中的auth令牌发送回调用应用程序。

示例回调方法

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
        }
    }
}

答案 2 :(得分:-1)

你使用的是错误的方法。看看文档:
- getAuthToken rising notification:
- getAuthToken prompting the user for credentials if necessary

您正在寻找的方法示例:

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