Make Web API Calls from ADFS Secured SharePoint

Facebooktwittergoogle_pluslinkedin

Making JavaScript web service calls from SharePoint when both SharePoint and the web service are secured with NTLM just works.  But what happens when both the web service and SharePoint are secured with ADFS?  This get a little more complicated, especially when SharePoint and the web service are different relying parties in ADFS.  Basically, the web service call will fail because JavaScript cannot easily handle the 302 redirect to the ADFS server.

There is a simple way to resolve this.  Although the solution I’m going to describe is a bit cheesy, it works.  So what is the solution?  Basically, we need to ensure the SAML token for the web service is issued to the users web browser before the JavaScript web service call is made.  Since we cannot expect the user to open the web service site first, we need to do it for them.  This can be accomplished by first creating a basic web page on the web service site and then configuring this page to require ADFS authentication.  Then an iframe is embedded inside a SharePoint page that uses the web service site page as it’s source.  When this iframe is loaded, the web browser will make a call to that page which results in a call to ADFS to get the SAML token.  The entire solution can be handled by some JavaScript.  The JavaScript will embed the iframe and make the call to the web service page.  The script can then make the actual web service call after the SAML token is received.  The following diagram steps through the authentication cycle for both SharePoint and the web service.  It seems like there are a lot of steps, but this entire process executes extremely fast.

ADFS Web API Process Flow
ADFS Web API Process Flow

Assumption: Client has not signed in via ADFS. No SSO Token from ADFS has been issued.

  • Client request SharePoint home page.
  • No STS Token is present so SharePoint redirects client to ADFS to login. Once authenticated, ADFS will issue a SSO Token and SAML Token.
  • ADFS will redirect client back to SharePoint site. SharePoint will issue the STS token. SharePoint content is loaded in the browser including the JavaScript to call the web service.
  • Note: CORS must be enabled on the WEB API Service
  • JavaScript will dynamically create an iframe. The iframe element’s content source is a page located on the same web site as the Web API. Note: The content source and web API must be in the same domain.
  • The Web API site will redirect the client (iframe) to ADFS to get a SAML token. ADFS will issue the token without requiring user login since a valid SSO token already exists.
  • ADFS will redirect the client (iframe) back to the Web API page.
  • Once the iframe load is complete, the JavaScript will proceed with the Web API call to the web service.
  • The Web API will return results to JavaScript.

The JavaScript below is just an example and isn’t complete, but it should help get you started.


var webAPIurl = "https://webapi.contoso.com/api/dosomething"
var testPage = "https://webapi.contoso.com/testpage"
var errorLoopCount = 0;

function authenticate() {
    return $.Deferred(function (d) {
        var iFrame = $("<iframe></iframe>");
	
        iFrame.hide();
        iFrame.appendTo("body");
        iFrame.attr('src', testPage);

        iFrame.load(function () {
            iFrame.remove();
            d.resolve();
        });
    });
};

function makeCall() {
    return $.ajax({
        type: 'GET',
        dataType: 'json',
        url: webAPIurl,
        xhrFields: {
            'withCredentials': true
        },
        crossDomain: true
    }).success(function(data) {
        // Add your logic to respond to the Web API call

        return $.Deferred(function(d) { d.resolve(data); });
    }).error(function(xhr, status, error) {
        if (xhr.status == 401) {
            if (errorLoopCount < 5) {
                errorLoopCount++;
                return authenticate().then(function() {
                    //Making the call again
                    return makeCall();
                });
            } else {
                return $.Deferred(function(d) {
                    d.reject(error);
                });
            }
        } else {
            return $.Deferred(function(d) {
                d.reject(error);
            });
        }
    })
}

$(document).ready(function() {
    makeCall();	
}) 
References:
Facebooktwittergoogle_pluslinkedin