1
votes

We have experienced the following problem: We had implemented Azure AD authenticated web API and we have been successfully had been making calls to it with ADAL 1.0.12 (we had our own wrapper around it for processing iframe for silent login).

Now we got rid all together of our wrapper and upgraded to the latest version and started to get the problem described here: token renewal operation timed out.

The Azure AD architecture behind the API is very simple: a registered web app with Read directory data and Sign in and read user profile permissions. We have another native app registered with permissions to the web app (Access to the Web APP NAME HERE). Both applications have oauth2AllowImplicitFlow set to true in their manifests.

Both apps have registered a wildcard redirect URIs for https://ourtenant.sharepoint.com/* (we are making the calls from SharePoint to the web API). In addition, the native client app has registered redirect URI to the unique URI (App ID URI) of the web API app.

Both apps have been granted admin consent before - they were working fine with the old version of ADAL.js

So, here is some code. Please, note that we had tried both without displayCall callback (with page refresh directly on the current page) and with page refresh (check the commented code in initLogin method). Also we had a switch for generating a popup or an iframe with a callback on successful login.

The problem is with authContext.acquireToken. Note that if we call OurNamespace.authContext.getCachedToken(OurNamespace.clientId) we get a stored token for the resource.

Note that also, we are calling properly handleWindowCallback after each page refresh / iframe / popup.

Happens regardless of the browser.

OurNamespace = {

serviceMainUrl: "https://localhost:44339",
webApiAppIdUri: "WEB-API-URI",
tenant: "TENANT",
clientId: "NATIVE-APP-GUID", // Native APP
adalEndPoints: {
    get: null
},
adal: null,
authContext: null,
dummyAuthPage: null,
usePopup: true,

init: function () {
    this.dummyAuthPage = "DummmyLogin.aspx";
    OurNamespace.adalEndPoints.get = OurNamespace.serviceMainUrl + "/api/values";
    OurNamespace.authContext = new AuthenticationContext({
        tenant: OurNamespace.tenant + '.onmicrosoft.com',
        clientId: OurNamespace.clientId,
        webApiAppIdUri: OurNamespace.webApiAppIdUri,
        endpoints: OurNamespace.adalEndPoints,
        popUp: false,
        postLogoutRedirectUri: window.location.origin,
        cacheLocation: "localStorage",
        displayCall: OurNamespace.displayCall,
        redirectUri: _spPageContextInfo.siteAbsoluteUrl + "/Pages/" + OurNamespace.dummyAuthPage
    });
    var user = OurNamespace.authContext.getCachedUser(); // OurNamespace.authContext.getCachedToken(OurNamespace.clientId)
    if (user) {
        OurNamespace.azureAdAcquireToken();
    } else {
        OurNamespace.initLogin();
    }
},

initLogin: function () {
    //OurNamespace.authContext.config.displayCall = null;
    //var isCallback = OurNamespace.authContext.isCallback(window.location.hash);
    //OurNamespace.authContext.handleWindowCallback();

    //if (isCallback && !OurNamespace.authContext.getLoginError()) {
    //    window.location.href = OurNamespace.authContext._getItem(OurNamespace.authContext.CONSTANTS.STORAGE.LOGIN_REQUEST);
    //}

    OurNamespace.authContext.login();
},

displayCall: function (url) {
    var iframeId = "azure-ad-tenant-login",
        popup = null;
    if (OurNamespace.usePopup) {
        popup = window.open(url, 'auth-popup', 'width=800,height=500');
    } else {
        var iframe = document.getElementById(iframeId);
        if (!iframe) {
            iframe = document.createElement("iframe");
            iframe.setAttribute('id', iframeId);
            document.body.appendChild(iframe);
        }
        iframe.style.visibility = 'hidden';
        iframe.style.position = 'absolute';
        iframe.src = url;
        popup = iframe.contentDocument;
    }
    var intervalId = window.setInterval(function () {
        try {
            // refresh the contnetDocument for iframe
            if (!OurNamespace.usePopup)
                popup = iframe.contentDocument;
            var isCallback = OurNamespace.authContext.isCallback(popup.location.hash);
            OurNamespace.authContext.handleWindowCallback(popup.location.hash);
            if (isCallback && !OurNamespace.authContext.getLoginError()) {
                popup.location.href = OurNamespace.authContext._getItem(OurNamespace.authContext.CONSTANTS.STORAGE.LOGIN_REQUEST);
                window.clearInterval(intervalId);
                if (OurNamespace.usePopup) {
                    popup.close();
                }
                var user = OurNamespace.authContext.getCachedUser();
                if (user) {
                    console.log(user);
                } else {
                    console.error(OurNamespace.authContext.getLoginError());
                }
            }
        } catch (e) { }
    }, 400);
},

azureAdAcquireToken: function () {
    OurNamespace.authContext.acquireToken(OurNamespace.adalEndPoints.get, function (error, token) {
        if (error || !token) {
            SP.UI.Status.addStatus("ERROR", ('acquireToken error occured: ' + error), true);
            return;
        } else {
            OurNamespace.processWebApiRequest(token);
        }
    });
},

processWebApiRequest: function (token) {
    // Alternatively, in MVC you can retrieve the logged in user in the web api with HttpContext.Current.User.Identity.Name
    $.ajax({
        type: "GET",
        url: OurNamespace.adalEndPoints.get,
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        data: {},
        headers: {
            'Authorization': 'Bearer ' + token
        },
        success: function (results) {
            var dataObject = JSON.parse(results);
            SP.UI.Status.addStatus("Info", "ADAL GET data success: " + dataObject.webApiUser);
            $(".grid-info").html("<h1 class=\"h1\">Current Web API authenticated user: " + dataObject.webApiUser + "</h1>");
        },
        error: function (xhr, errorThrown, textStatus) {
            console.error(xhr);
            SP.UI.Status.addStatus("ERROR", ('Service error occured: ' + textStatus), true);
        }
    });
}
}
2

2 Answers

0
votes

I am testing using the 1.0.15 adal.js and get the access token successfully by using the authContext.acquireToken which actually call the this._renewToken(resource, callback) to acquire the access token by the hide iframe. Here is the full test sample code for your reference:

<html>
<head>

<script src="/js/1.0.15/adal.js"></script>
<script>

var config = {
    tenant: 'adfei.onmicrosoft.com',
    clientId: '7e3b0f81-cf5c-4346-b3df-82638848104f',
    redirectUri: 'http://localhost/spa.html',
    navigateToLoginRequestUrl:false,

};

var authContext=new AuthenticationContext(config);

var hash = window.location.hash;

if(hash.length!=0){
    authContext.handleWindowCallback(hash);
    var user=authContext.getCachedUser();
}
    

function login(){
    authContext.login();
}

function acqureToken(){
    authContext.acquireToken("https://graph.microsoft.com", function(error, token, message){
        console.log(token)

    })
}

</script>
</head>

<body>
<button onclick="login()">Login</button>
<button onclick="acqureToken()">AcquireToken</button>

</body>
</html>

Is it helpful? Or would you mind sharing a run-able code sample for this issue?

0
votes

I'm not sure if what version of adal.js you are using. But there is a loadFrameTimeout setting for the config object that you can set in milliseconds. Check the top of your adal.js file and it should be there.