Question

I am using JSOM to get the list contents of a list which is located in another site collection than my app. For example, my app is hosted here: portal.contoso.com/sites/myapp and the list is on top level: portal.contoso.com/myList.

I get an 403 Forbidden error if I try to access the list with JSOM. The app has Tenant-administrator permissions.

Here is my code:

var serverUrl: string = "https://portal.contoso.com/";

try {
    var clientContext: SP.ClientContext = new SP.ClientContext(serverUrl);
    var rootWeb = clientContext.get_web();

    clientContext.load(rootWeb);

    //Here it gets the SharePoint list e.g. portal.contoso.com/mylist
    var oList: SP.List = this.getAssetsList(clientContext);

    if (oList != null) {
        var camlQuery = new SP.CamlQuery();
        camlQuery.set_viewXml("<View><Query><Where><Geq><FieldRef Name=\'ID\'/>'" +
        "<Value Type=\'Number\'>1</Value></Geq></Where></Query><RowLimit>10</RowLimit></View>");
        this.sharePointCollectionListItems = oList.getItems(camlQuery);

        clientContext.load(this.sharePointCollectionListItems);

        clientContext.executeQueryAsync(
            Function.createDelegate(this, this.onQuerySucceeded),
            Function.createDelegate(this, this.onQueryFailed));
        }
    } catch (e) {
        console.log(e);
    }

How do I get this working?

Was it helpful?

Solution

To access information in the host web using the Javascript Object Model you need to use an SP.AppContextSite object in conjunction with your SP.ClientContext

I see you're using TypeScript, which is awesome, so I'll post my answer with both TypeScript and the working Javascript; my comments will have some additional information and you can paste the JS directly into your browser dev tools:

TypeScript

/// <reference path="microsoft.ajax.d.ts" />
/// <reference path="SharePoint.d.ts" />

var appweburl: string,
    hostweburl: string,
    context: SP.ClientContext,
    appContextSite:  SP.AppContextSite;

JSRequest.EnsureSetup(); // this is an easy method to retrieve URL parameters...
appweburl = decodeURIComponent(JSRequest.QueryString["SPAppWebUrl"]);
hostweburl = decodeURIComponent(JSRequest.QueryString["SPHostUrl"]);
// the context needs to be for the app web, so we can use get_current
context = SP.ClientContext.get_current();
// the appContextSite is for the hostweb,
// so we provide the hostweburl as the second parameter
appContextSite = new SP.AppContextSite(context, hostweburl);

// now we reference objects using the appContextSite
// but load and query using the context...
var list = appContextSite.get_web().get_lists().getByTitle("Site Assets");
var items = list.getItems(SP.CamlQuery.createAllItemsQuery());
context.load(items);

context.executeQueryAsync(function(sender, args) {
    // success
    console.log(items.get_count());
}, function(sender, args) {
    //failure
    console.log(args.get_message());
});

JavaScript (just excludes type annotations for those of who like to copy/paste and see some results!)

var appweburl, hostweburl, context, appContextSite;
JSRequest.EnsureSetup(); // this is an easy method to retrieve URL parameters...
appweburl = decodeURIComponent(JSRequest.QueryString["SPAppWebUrl"]);
hostweburl = decodeURIComponent(JSRequest.QueryString["SPHostUrl"]);
// the context needs to be for the app web, so we can use get_current
context = SP.ClientContext.get_current();
// the appContextSite is for the hostweb,
// so we provide the hostweburl as the second parameter
appContextSite = new SP.AppContextSite(context, hostweburl);
// now we reference objects using the appContextSite
// but load and query using the context...
var list = appContextSite.get_web().get_lists().getByTitle("Site Assets");
var items = list.getItems(SP.CamlQuery.createAllItemsQuery());
context.load(items);
context.executeQueryAsync(function (sender, args) {
    // success
    console.log(items.get_count());
}, function (sender, args) {
    //failure
    console.log(args.get_message());
});

You can find more information on this technique in the MSDN Article: Complete basic operations using JavaScript library code in SharePoint 2013, the host web stuff you're looking for is down at the bottom in the section titled "Access Objects in the Host Web".

Astute readers will notice that I do not initialize a SP.ProxyWebRequestExecutorFactory nor do I attach that factory to the context: while this methodology appears in most of the MSDN samples I've read, I have not found a single case where it is actually necessary. My guess (note the emphasis) is that you would need to use that pattern in the C# version of the Client Object Model, and since the Javascript Object Model is transpiled from the C# version the technique is likely carried over regardless.

Licensed under: CC-BY-SA with attribution
Not affiliated with sharepoint.stackexchange
scroll top