Android 的 AzureAD 抛出 ADALError.AUTH_REFRESH_FAILED_PROMPT_NOT_ALLOWED

AzureAD for Android throws ADALError.AUTH_REFRESH_FAILED_PROMPT_NOT_ALLOWED

我有一个应用程序,用户在 Office365 中使用 AzureAD library for Android 进行身份验证。

它运行良好,用户可以验证并使用该应用程序。不幸的是,过了一会儿,他们开始使用 ADALError.AUTH_REFRESH_FAILED_PROMPT_NOT_ALLOWED 作为错误代码来攻击 AuthenticationException

我查看了 AzurelAD 的 source code。唯一解决这个问题的地方是 acquireTokenAfterValidation() 方法:

private AuthenticationResult acquireTokenAfterValidation(CallbackHandler callbackHandle,
        final IWindowComponent activity, final boolean useDialog,
        final AuthenticationRequest request) {
    Logger.v(TAG, "Token request started");

    // BROKER flow intercepts here
    // cache and refresh call happens through the authenticator service
    if (mBrokerProxy.canSwitchToBroker()
            && mBrokerProxy.verifyUser(request.getLoginHint(),
                    request.getUserId())) {
        .......
        Logger.v(TAG, "Token is not returned from backgroud call");
        if (!request.isSilent() && callbackHandle.callback != null && activity != null) {
            ....
        } else {
            // User does not want to launch activity
            String msg = "Prompt is not allowed and failed to get token:";
            Logger.e(TAG, msg, "", ADALError.AUTH_REFRESH_FAILED_PROMPT_NOT_ALLOWED);
            callbackHandle.onError(new AuthenticationException(
                    ADALError.AUTH_REFRESH_FAILED_PROMPT_NOT_ALLOWED, msg));
        }

        // It will start activity if callback is provided. Return null here.
        return null;
    } else {
        return localFlow(callbackHandle, activity, useDialog, request);
    }
}

我的源代码:

authenticator.getAccessTokenSilentSync(getMailService());

public class Authenticator {
    ..............
    public String getAccessTokenSilentSync(ServiceInfo serviceInfo) {
        throwIfNotInitialized();
        return getAuthenticationResultSilentSync(serviceInfo).getAccessToken();
    }

    private AuthenticationResult getAuthenticationResultSilentSync(ServiceInfo serviceInfo) {
        try {
            return authenticationContext.acquireTokenSilentSync(
                    serviceInfo.ServiceResourceId,
                    Client.ID,
                    userIdentity.getAdUserId());
        } catch (AuthenticationException ex) {
            // HERE THE EXCEPTION IS HANDLED.
        }
    }
    ..............
} 

我得到的堆栈跟踪:

    <package name>.data_access.error_handler.AuthenticationExceptionWithServiceInfo: Refresh token is failed and prompt is not allowed
    at com.microsoft.aad.adal.AuthenticationContext.localFlow(AuthenticationContext.java:1294)
    at com.microsoft.aad.adal.AuthenticationContext.acquireTokenAfterValidation(AuthenticationContext.java:1229)
    at com.microsoft.aad.adal.AuthenticationContext.acquireTokenLocalCall(AuthenticationContext.java:1123)
    at com.microsoft.aad.adal.AuthenticationContext.refreshToken(AuthenticationContext.java:1609)
    at com.microsoft.aad.adal.AuthenticationContext.localFlow(AuthenticationContext.java:1261)
    at com.microsoft.aad.adal.AuthenticationContext.acquireTokenAfterValidation(AuthenticationContext.java:1229)
    at com.microsoft.aad.adal.AuthenticationContext.acquireTokenLocalCall(AuthenticationContext.java:1123)
    at com.microsoft.aad.adal.AuthenticationContext.refreshToken(AuthenticationContext.java:1609)
    at com.microsoft.aad.adal.AuthenticationContext.localFlow(AuthenticationContext.java:1261)
    at com.microsoft.aad.adal.AuthenticationContext.acquireTokenAfterValidation(AuthenticationContext.java:1229)
    at com.microsoft.aad.adal.AuthenticationContext.acquireTokenLocalCall(AuthenticationContext.java:1123)
    at com.microsoft.aad.adal.AuthenticationContext.access0(AuthenticationContext.java:58)
    at com.microsoft.aad.adal.AuthenticationContext.call(AuthenticationContext.java:1072)
    at com.microsoft.aad.adal.AuthenticationContext.call(AuthenticationContext.java:1067)
    at java.util.concurrent.FutureTask.run(FutureTask.java:237)

我正在使用的 AzureAD 库版本:1.1.7(为了防止指责太旧的版本 - 我已经检查了从 1.1.7 到 1.1.11 的变更列表,但没有发现任何与问题相关的内容)

问题: 现在,我正在处理这个错误,作为通过用户进入登录屏幕的信号。在我看来,这会给用户带来糟糕的体验。事实上,它 非常 经常发生并影响许多用户,这使情况变得更糟。

问题:我能做些什么来避免这种情况AuthenticationException或以某种方式解决它(即避免用户再次输入凭据)。

这是因为您需要刷新您的令牌并在您的代码中实现它,这样用户就不会在每次访问令牌过期时都提示登录。请在此处查看如何实现刷新令牌:

https://msdn.microsoft.com/en-us/library/azure/dn645538.aspx

希望对您有所帮助。

您是否已确认 AuthenticationContext.acquireTokenSilentSync() 确实是您希望调用的方法?

文档表明此方法将明确不显示提示。来自文档:

This is sync function. It will first look at the cache and automatically checks for the token expiration. Additionally, if no suitable access token is found in the cache, but refresh token is available, the function will use the refresh token automatically. This method will not show UI for the user. If prompt is needed, the method will return an exception.

您获得的刷新令牌应持续两周 AAD book。刷新令牌过期后,用户需要重新进行身份验证。你能用 Fiddler 或 Charles 检查网络流量并检查令牌的到期时间吗?如果您可以验证令牌在到期前未能刷新,则可能表明 AD 库中存在错误。

澄清 AuthenticationContext 上方法的区别 - 方法分为两类:"silent" 方法(如果用户需要重新验证,则不会向用户显示对话框),和 non-silent。 Non-silent 方法将在需要用户重新验证(或同意)的情况下启动包含 AAD 登录名的新 Activity。此时身份验证流程将重新启动。

此外,如果您更改应用程序在 Azure 中的注册,例如添加新的权限范围,您的用户将需要 re-grant 同意应用程序继续处理他们的数据。