Cross Site Scripting with SharePoint 2013 REST calls

Today I had to figure out how to query a SharePoint 2013 REST service from another domain.

It took a while to find the correct settings. 😉

There was no list on the internet so I want to post it here as reference. – If you have additions to it please post them in the comments.

My test bed:

  1. I created two web applications
  2. At the root of both web apps I created a “Team Site” site collection.
  3. I uploaded a copy of jQuery to the masterpage catalog of http://fromhere.kc-dev.com .
  4. Also to the masterpage catalog of this site I uploaded a script file named “crosssitescripting.js” containing the REST call to http://tohere.kc-dev.com/_api .
  5. On the homepage of the root site collection of http://fromhere.kc-dev.com I added some script tags to load the script files jQuery.js and crosssitescripting.js. And a div tag for the sub web list.

clip_image001

I opened the homepage of http://fromhere.kc-dev.com in the browser and got an error in the F12 dev tools of the Internet Explorer. As expected.

clip_image002

Now I added some web.config modifications using PowerShell to enable cross site scripting. (Some years ago I wrote a note on that topic: https://blog.kenaro.com/2010/09/02/add-web-config-modification-with-powershell-spwebconfigmodification)

After reloading the site I could see the sub web list:

clip_image003

Here is the content of crosssitescripting.js

$(document).ready(function(){
    $.support.cors = true;
    $.ajax({
        url: "http://tohere.kc-dev.com/_api/Web/Webs",
        type: "GET",
        crossDomain: true,
        dataType: "json",
        headers: { "Accept": "application/json; odata=verbose" },
        xhrFields: { withCredentials: true },
        success: function (response) {
            var ul = $("#weblist").append("<ul/>");
            $(response.d.results).each(function(){
                $("<li>"+this.Url+"</li>").appendTo(ul);
            });
        },
        error: function (xhr, status) {
            debugger;
        }
    });
});

Here is the PowerShell script to add the web.config modifications:

Add-PSSnapin Microsoft.SharePoint.PowerShell -EA 0

$localFarm = Get-SPFarm

$webapp = Get-SPWebApplication "http://tohere.kc-dev.com"

# Remove old web.config modifications of MyAuthenticationProvider
$oldMods = @();
$webapp.WebConfigModifications | ? { $_.Owner -eq "CrossSiteScripting" } | % { 
    $oldMods = $oldMods + $_
}

$oldMods | % { 
    $webapp.WebConfigModifications.Remove($_) 
}

# update the Web Application and apply all existing web.config modifications - this executes the "remove" actions from above
$webapp.Update()
[Microsoft.SharePoint.Administration.SPWebService]::ContentService.ApplyWebConfigModifications()

#Wait until web.config modifications finished by timer job
while( (Get-SPTimerJob | ? { $_.Name -eq "job-webconfig-modification"}) -ne $null ) {
    Write-Host "." -NoNewline
    Start-Sleep 1
}

# New web.config modifications for MyAuthenticationProvider
$myModification1 = new-object Microsoft.SharePoint.Administration.SPWebConfigModification
$myModification1.Path = "configuration/system.webServer/httpProtocol/customHeaders"
$myModification1.Name = "add[@name='Access-Control-Allow-Origin'][@value='http://fromhere.kc-dev.com']"
$myModification1.Sequence = 0
$myModification1.Owner = "CrossSiteScripting"
#0 = for the enum value "SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode"
$myModification1.Type = 0
$myModification1.Value = "<add name='Access-Control-Allow-Origin' value='http://fromhere.kc-dev.com' />"
$webapp.WebConfigModifications.Add($myModification1)

$myModification1 = new-object Microsoft.SharePoint.Administration.SPWebConfigModification
$myModification1.Path = "configuration/system.webServer/httpProtocol/customHeaders"
$myModification1.Name = "add[@name='Access-Control-Request-Method'][@value='GET,POST,HEAD,OPTIONS']"
$myModification1.Sequence = 0
$myModification1.Owner = "CrossSiteScripting"
$myModification1.Type = 0
$myModification1.Value = "<add name='Access-Control-Request-Method' value='GET,POST,HEAD,OPTIONS' />"
$webapp.WebConfigModifications.Add($myModification1)

$myModification1 = new-object Microsoft.SharePoint.Administration.SPWebConfigModification
$myModification1.Path = "configuration/system.webServer/httpProtocol/customHeaders"
$myModification1.Name = "add[@name='Access-Control-Request-Headers'][@value='Content-Type,Authorization']"
$myModification1.Sequence = 0
$myModification1.Owner = "CrossSiteScripting"
$myModification1.Type = 0
$myModification1.Value = "<add name='Access-Control-Request-Headers' value='Content-Type,Authorization' />"
$webapp.WebConfigModifications.Add($myModification1)

$myModification1 = new-objectMicrosoft.SharePoint.Administration.SPWebConfigModification
$myModification1.Path = "configuration/system.webServer/httpProtocol/customHeaders"
$myModification1.Name = "add[@name='Access-Control-Allow-Credentials'][@value='true']"
$myModification1.Sequence = 0
$myModification1.Owner = "CrossSiteScripting"
$myModification1.Type = 0
$myModification1.Value = "<add name='Access-Control-Allow-Credentials' value='true' />"
$webapp.WebConfigModifications.Add($myModification1)

$webapp.Update()
[Microsoft.SharePoint.Administration.SPWebService]::ContentService.ApplyWebConfigModifications()

#Wait until web.config modifications finished by timer job
while( (Get-SPTimerJob | ? { $_.Name -eq "job-webconfig-modification"}) -ne $null ) {
    Write-Host "." -NoNewline
    Start-Sleep 1
}