weixin_39919195
weixin_39919195
2020-12-30 03:43

Invalid State in Edge for subset of users

Here is some context:

ADAL.js Version = 1.0.13 (Have also tried with 1.0.15) Edge Version = 40.15063

In App.js config():


$locationProvider.html5Mode({
    enabled: true,
    requireBase: false
}).hashPrefix("!");

adalProvider.init({
    clientId: sessionStorage.getItem(Constant.adalHelper.AadAppId),
    endpoints: angular.fromJson(sessionStorage.getItem(Constant.adalHelper.AdalResourceMap)),
    extraQueryParameter: "nux=1",
    instance: sessionStorage.getItem(Constant.adalHelper.AadInstance),
    loginResource: sessionStorage.getItem(Constant.adalHelper.ValidAudience),
    popUp: false,
    redirectUri: window.location.origin + "/login",
    requireADLogin: true,
    tenant: sessionStorage.getItem(Constant.adalHelper.AadTenant)
}, $httpProvider);

Above we redirect to /login, we found that if we redirect to our main page the iFrames for token refreshes would essentially end up resolving and loading the entire app which caused odd problems. /login in this case is its own module:

LoginModule.js config():


$locationProvider.html5Mode({
    enabled: true,
    requireBase: false
}).hashPrefix("!");

adalProvider.init({
    clientId: sessionStorage.getItem(Constant.adalHelper.AadAppId),
    endpoints: angular.fromJson(sessionStorage.getItem(Constant.adalHelper.AdalResourceMap)),
    extraQueryParameter: "nux=1",
    instance: sessionStorage.getItem(Constant.adalHelper.AadInstance),
    loginResource: sessionStorage.getItem(Constant.adalHelper.ValidAudience),
    popUp: false,
    redirectUri: window.location.origin + "/login",
    requireADLogin: true,
    tenant: sessionStorage.getItem(Constant.adalHelper.AadTenant)
}, $httpProvider);

In LoginModule.js run():


var redirect = false;
var expirationTime = sessionStorage.getItem(loginConstant.ADALEXPIRATIONKEY + sessionStorage.getItem(loginConstant.AADAPPID));
if (expirationTime && expirationTime > Math.round(new Date().getTime())) {
    redirect = true;
}
$rootScope.$watch(function (scope) {
    return scope.userInfo.isAuthenticated;
}, function () {
    if (redirect) {
        var authRedirectUri = window.sessionStorage.getItem("authRedirectUri");
        window.sessionStorage.removeItem("authRedirectUri");
        if (authRedirectUri) {
            window.location.href = authRedirectUri;
         }
    } else {
           window.sessionStorage.removeItem("authRedirectUri");
    }
});

With the above in place, we now see the majority of users hitting our site, getting redirected to AAD to Auth, then coming back into /login and being redirected to the main site. iFrame token renewals are redirected to /login and stay there avoiding the odd behavior of having our site running in multiple iFrames.

For a subset of users this is not the case, they login and get redirected to /login as expected but we do not redirect them because ADAL comes back with an Invalid State error.

I know this error shows up when either AAD or Site is in Trusted sites but not the other but for these users they have neither in Trusted Sites. In fact their login seems sucessful you can see in the URL that they have /login#id_token= but in session storage ADAL is saying error with Invalid State.

For all users site works fine in other browsers, and for many also in Edge but it seems that whatever causing this issue is happening to more people. These are managed machines, so there could be some policy being rolled out that is causing this but outside of the Trusted Sites issue I'm not sure what it could be.

Logging from an affected user:


Thu, 10 Aug 2017 18:31:52 GMT:1.0.13-VERBOSE: Url: App/Views/Directives/SpinnerView.html maps to resource: <resourceid>
App.js (10,13)
Thu, 10 Aug 2017 18:31:52 GMT:1.0.13-WARNING: User login is required
App.js (10,13)
Thu, 10 Aug 2017 18:31:52 GMT:1.0.13-ERROR: Error when acquiring token for resource: <resourceid>
stack:
undefined
App.js (10,13)
Thu, 10 Aug 2017 18:31:52 GMT:1.0.13-INFO: Getting error in the response: {"method":"GET","transformRequest":[null],"transformResponse":[],"cache":{},"url":"App/Views/Directives/SpinnerView.html","headers":{"Accept":"application/json, text/plain, */*"},"data":"User login is required|login required|undefined"}
App.js (10,13)
Thu, 10 Aug 2017 18:31:53 GMT:1.0.13-VERBOSE: Location change event from https://localhost:44300/ to https://localhost:44300/
App.js (10,13)
Thu, 10 Aug 2017 18:31:53 GMT:1.0.13-INFO: State change event for:/
App.js (10,13)
Thu, 10 Aug 2017 18:31:53 GMT:1.0.13-INFO: Login event for:/
App.js (10,13)
Thu, 10 Aug 2017 18:31:53 GMT:1.0.13-INFO: Start login at:https://localhost:44300/
App.js (10,13)
Thu, 10 Aug 2017 18:31:53 GMT:1.0.13-VERBOSE: Expected state: 925ac088-e603-4da5-9931-c58da688e5b2 startPage:https://localhost:44300/
App.js (10,13)
Thu, 10 Aug 2017 18:31:53 GMT:1.0.13-INFO: Navigate url:https://login.windows.net/microsoft.onmicrosoft.com/oauth2/authorize?response_type=id_token&client_id=<clientid>&redirect_uri=https%3A%2F%2Flocalhost%3A44300%2Flogin&state=925ac088-e603-4da5-9931-c58da688e5b2&nux=1&client-request-id=<clientrequestid>&x-client-SKU=Js&x-client-Ver=1.0.13
App.js (10,13)
Thu, 10 Aug 2017 18:31:53 GMT:1.0.13-INFO: Navigate to:https://login.windows.net/microsoft.onmicrosoft.com/oauth2/authorize?response_type=id_token&client_id=1<clientid>&redirect_uri=https%3A%2F%2Flocalhost%3A44300%2Flogin&state=925ac088-e603-4da5-9931-c58da688e5b2&nux=1&client-request-id=<clientrequestid>&x-client-SKU=Js&x-client-Ver=1.0.13&nonce=391d02d9-53d0-4542-8b48-5ff8cf0c3b87
App.js (10,13)
Thu, 10 Aug 2017 18:31:55 GMT:1.0.13-VERBOSE: Url: /App/Views/Workspace/Desktop/WorkspaceView.html maps to resource: <resourceid>
App.js (10,13)
Thu, 10 Aug 2017 18:31:55 GMT:1.0.13-INFO: login is in progress.
App.js (10,13)
Thu, 10 Aug 2017 18:31:55 GMT:1.0.13-INFO: Getting error in the response: {"method":"GET","transformRequest":[null],"transformResponse":[null],"cache":{},"headers":{"Accept":"text/html"},"url":"/App/Views/Workspace/Desktop/WorkspaceView.html","data":"login in progress, cancelling the request for /App/Views/Workspace/Desktop/WorkspaceView.html"}
App.js (10,13)
Thu, 10 Aug 2017 18:31:55 GMT:1.0.13-VERBOSE: State change error occured. Error: {"method":"GET","transformRequest":[null],"transformResponse":[null],"cache":{},"headers":{"Accept":"text/html"},"url":"/App/Views/Workspace/Desktop/WorkspaceView.html","data":"login in progress, cancelling the request for /App/Views/Workspace/Desktop/WorkspaceView.html"}
App.js (10,13)
Thu, 10 Aug 2017 18:31:55 GMT:1.0.13-INFO: Setting defaultPrevented to true if state change error occured because adal rejected a request. Error: login in progress, cancelling the request for /App/Views/Workspace/Desktop/WorkspaceView.html
App.js (10,13)
</resourceid></clientrequestid></clientid></clientrequestid></clientid></resourceid></resourceid>

Let me know what other info I can provide to help resolve. I suppose the other "non-boilerplate" thing we are doing is having the LoginModule to handle the token refresh iFrames causing the entire site to load so if there are suggestions for that I can try and see if it helps.

该提问来源于开源项目:AzureAD/azure-activedirectory-library-for-js

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享
  • 邀请回答

11条回答

  • weixin_39919195 weixin_39919195 4月前

    Just seeing: https://github.com/AzureAD/azure-activedirectory-library-for-js/wiki/FAQs has a better approach to the iFrame app loading issue, will try that and see if it simplifies things.

    点赞 评论 复制链接分享
  • weixin_39919195 weixin_39919195 4月前

    So following the example https://gist.github.com/tushargupta51/5fa6d000357120adbc8fd1d5c68853c4 I simplified our approach, we were essentially already doing this, we had a separate /login page which booted its own app module as I outline above.

    Here is the simplified approach, our /login page now just does:

    
    var App = angular.module("App", ["AdalAngular"]);
    App.config(['$httpProvider', "$locationProvider", 'adalAuthenticationServiceProvider', function ($httpProvider, $locationProvider, adalProvider) {
    
        $locationProvider.html5Mode(true).hashPrefix("!");
    
        adalProvider.init(
            {
                clientId: sessionStorage.getItem(Constant.AADAPPID),
                extraQueryParameter: "nux=1",
                instance: sessionStorage.getItem(Constant.AADINSTANCE),
                loginResource: sessionStorage.getItem(Constant.VALIDAUDIENCE),
                popUp: false,
                redirectUri: window.location.origin + "/login",
                tenant: sessionStorage.getItem(Constant.AADTENANT)
            }, $httpProvider);
    }]);
    

    This is working but I have not been able to check for the users that are seeing this issue, I myself do not have the issue in Edge for our production site.

    Consistently for me though if I open our prod site in Edge InPrivate I get stuck at /login at that point in the same tab if I just navigate again to the home URL the site loads properly. This behavior is still the same after this above simplification so my guess is the affected users would still see this issue non InPrivate assuming the occurrences are related.

    点赞 评论 复制链接分享
  • weixin_39919195 weixin_39919195 4月前

    It seems maybe the occurrences are separate. I had one of our affected users completely reset Edge via:

    
    remove-item $env:localappdata\Packages\Microsoft.MicrosoftEdge_8wekyb3d8bbwe\* -recurse -Force 2>$null
    Get-AppXPackage -Name Microsoft.MicrosoftEdge | Foreach {Add-AppxPackage DisableDevelopmentMode -Register "$($_.InstallLocation)\AppXManifest.xml" -Verbose}
    

    Before running the above, he would attempt to hit our prod site as normal and would get stuck on /login with Invalid State. Now he no longer sees this behavior with Edge fully reinstalled.

    In InPrivate however he still sees the issue, getting stuck at /login with Invalid State, in this case though simply navigating again to the root URL causes the site to load correctly.

    So it seems maybe this is more of an Edge specific issue, the version that was reinstalled was the same as it was before, but maybe some corp policy is making additional changes. The InPrivate issue does still seem like it could be related to how I have configured ADAL.js though.

    点赞 评论 复制链接分享
  • weixin_39919195 weixin_39919195 4月前

    It does not appear that the hard Edge reset works for everyone. Just tried it on another affected user and was seeing the same Invalid State issue after the reset. Hoping I can get some pointers here for something to try. From what I can tell I'm following the samples around this so not sure where to start changing things.

    Outside of just adding Logging = ... is there maybe a more verbose way I can pull out all the logs around this, Edge seems particularly bad in its Console so I'm not sure I'm actually seeing everything.

    点赞 评论 复制链接分享
  • weixin_39933438 weixin_39933438 4月前

    do you know what is special for these users? a version of the OS? a version of Edge? would they be guest users of the Azure Active directory?

    点赞 评论 复制链接分享
  • weixin_39919195 weixin_39919195 4月前

    They have the same version of OS/Edge as users who work. They are not guest users.

    I pulled a full group policy report between an unaffected user and someone with the issue and could not find any relevant differences.

    I also exported and applied all "internet/edge/ie" registry keys that weren't users specific to my machine and could not replicate the issue.

    I agree is does appear very user specific but given the above I cannot see any differences. I'm not an AD admin though so there may be things I cannot see. What else can I do to discern possible issues with specific users?

    点赞 评论 复制链接分享
  • weixin_39919195 weixin_39919195 4月前

    Just had our AD look into this from their end for the affected users. They confirm that there are zero failed sign in attempts. This makes sense as the stuck users are redirected back and have #idtoken= in their URL. So this is something on the ADAL side.

    Is there anything in my config that could cause this? Is this some sort of ADAL bug specific to Edge? What other information can I provide from my end to help figure this out?

    点赞 评论 复制链接分享
  • weixin_39997795 weixin_39997795 4月前

    We would need a code repro for this issue to see whats going on. Can you provide us with that?

    点赞 评论 复制链接分享
  • weixin_39919195 weixin_39919195 4月前

    Was looking at this more today. Wanted some clarifications around the frameRedirect approach. On the FAQ here https://github.com/AzureAD/azure-activedirectory-library-for-js/wiki/FAQs its stated

    Set redirect_uri property on config to a simple page, that does not require authentication. You have to make sure that it matches with the redirect_uri registered in AAD portal. This will not affect user's login experience as Adal saves the start page when user begins the login process and redirects back to the exact location after login is completed.

    Where does Adal save the start page for user driven login flows? Looking at where the URL is serialized

    
    AuthenticationContext.prototype._serialize = function (responseType, obj, resource) {
            var str = [];
            if (obj !== null) {
                str.push('?response_type=' + responseType);
                str.push('client_id=' + encodeURIComponent(obj.clientId));
                if (resource) {
                    str.push('resource=' + encodeURIComponent(resource));
                }
    
                str.push('redirect_uri=' + encodeURIComponent(obj.redirectUri));
                str.push('state=' + encodeURIComponent(obj.state));
    
                if (obj.hasOwnProperty('slice')) {
                    str.push('slice=' + encodeURIComponent(obj.slice));
                }
    
                if (obj.hasOwnProperty('extraQueryParameter')) {
                    str.push(obj.extraQueryParameter);
                }
    
                var correlationId = obj.correlationId ? obj.correlationId : this._guid();
                str.push('client-request-id=' + encodeURIComponent(correlationId));
            }
    
            return str.join('&');
        };
    

    redirect_uri is always added to the URL from the config. In the frame redirect approach this is set to some /login page where the iFrames are supposed to land. Wouldn't the code need some sort of check like

    
    if(resourceType === "idToken") {
        str.push('redirect_uri=' + window.location.href);
    } else if(resourceType === "token") {
        str.push('redirect_uri=' + encodeURIComponent(obj.redirectUri));
    }
    

    Its not clear why the above would only affect certain users but since they are getting stuck at /login which would imply the process is assuming they are an iFrame, or am I missing something here.

    点赞 评论 复制链接分享
  • weixin_39919195 weixin_39919195 4月前

    Further testing has narrowed down this issue to a problem in Edge. For the users that get stuck at /login Edge has cleared local and session storage after the redirect from AAD. So they are unable to validate state, nonce, and redirect because the values no longer exist to check against.

    As a temporary workaround until Edge fixes this issue I have done the following. In get/save item in adal.js I have added a check just for the above keys to save them as session cookies

    
    if (key === this.CONSTANTS.STORAGE.STATE_LOGIN || key ===this.CONSTANTS.STORAGE.NONCE_IDTOKEN || key === this.CONSTANTS.STORAGE.LOGIN_REQUEST) {
        document.cookie = key + "=" + obj + ";";
        return true;
    }
    

    For whatever reason the cookies seem to be preserved when this issue happens, so it seems the only workaround is to store them here where we actually have them after we come back from AAD. Is there any concerns/issues with this as a workaround, it seems in general session cookies behave mostly the same way.

    点赞 评论 复制链接分享
  • weixin_39658900 weixin_39658900 4月前

    The above work around is being investigated and needs to go through security and threat model consideration. Closing for now.

    点赞 评论 复制链接分享

相关推荐