Accessing SharePoint Data from Provider-Hosted Apps: Use the Right Context

I have spent the better part of the last two years working with the new app development model for SharePoint 2013. Recently I have focused on building apps that access, manipulate, and interact with data stored in SharePoint Online with Office 365. If you have done any development using the client-side object model (CSOM) for SharePoint, you understand the importance of instantiating the proper ClientContext object to access data in a particular SharePoint site. The ClientContext constructor takes as an argument the URL of a SharePoint site and allows you to access data stored in the Lists collection of the Web associated with it. In this post, I will discuss the various context objects you should use in your provider-hosted app depending on where the data your app needs to access resides and if the user’s permissions need to be considered. If you have been developing apps for SharePoint for awhile now (and even if you haven’t), I strongly encourage you to use Visual Studio 2013 and the Office Developer Tools for Visual Studio 2013 – March 2014 Update.

Host webs and app webs

When dealing with apps for SharePoint, you will become familiar with host webs and app webs:

  • Host web – the SharePoint site to which an app is installed
  • App web – the special isolated site (a unique app web is provisioned for each installation of the app) where the app for SharePoint’s internal components and content, such as lists, content types, workflows, and pages, are deployed

Note that a provider-hosted app is not required to have an app web, and in fact may not need one depending on your business requirements.

Your app will always have Full Control permissions to its app web. However, your app will need to request (and be granted) permissions by the user installing your app in order to access data in the host web. This is handled through the app manifest.

If your app needs to access data in the SharePoint site where it is being installed, you will be working with a host web context of some sort. As you will see, there are actually two different host web context objects, depending on the app authorization policy you choose.

Life made easy, thanks to SharePointContext.cs

When you create a new provider-hosted app in Visual Studio 2013, you have the option to create a new ASP.NET Web Forms or MVC application to serve as your app’s remote web application. If you are using the Office Developer Tools for Visual Studio 2013 – March 2014 Update, you also have the option to convert an existing ASP.NET web application to an app for SharePoint project (really cool!) In either case, you will notice that SharePointContext.cs is added to the remote web application project. This file contains class definitions for SharePointAcsContext and SharePointHighTrustContext, which allow you to create host web and app web context objects based on whether your trust broker is ACS (which it is with Office 365) or if you are on premises. I won’t delve into too much more detail in this post, but Kirk Evans has an outstanding writeup about these classes.

Accessing data in the app web

To access data in the SharePoint app web from your app, use the following pattern:

CSOM (C#)

var spContext = SharePointContextProvider.Current.GetSharePointContext(Context);

using (var clientContext = spContext.CreateUserClientContextForSPAppWeb())
{
    Web web = clientContext.Web;
    clientContext.Load(web);
    clientContext.ExecuteQuery();

    ListCollection lists = web.Lists;
    clientContext.Load<ListCollection>(lists);
    clientContext.ExecuteQuery();
}

REST (C#)

var spContext = SharePointContextProvider.Current.GetSharePointContext(Context);
HttpWebRequest listRequest = (HttpWebRequest)HttpWebRequest.Create(spContext.SPAppWebUrl + "/_api/web/lists");
listRequest.Method = "GET";
listRequest.Accept = "application/atom+xml";
listRequest.ContentType = "application/atom+xml;type=entry";
listRequest.Headers.Add("Authorization", "Bearer " + spContext.UserAccessTokenForSPAppWeb);

JSOM

var appweburl = decodeURIComponent(getQueryStringParameter("SPAppWebUrl"));
var clientContext = new SP.ClientContext(appweburl);
var appWeb = clientContext.get_web();
var appWebListColl = appWeb.get_lists();
clientContext.load(appWebListColl);
clientContext.executeQueryAsync(onAppWebGetListSuccess, onError);

Accessing data in the host web

To access data in the SharePoint host web (the SharePoint site where your app is installed) from your app, use the following pattern:

CSOM (C#)

var spContext = SharePointContextProvider.Current.GetSharePointContext(Context);

using (var clientContext = spContext.CreateUserClientContextForSPHost())
{
    Web web = clientContext.Web;
    clientContext.Load(web);
    clientContext.ExecuteQuery();

    ListCollection lists = web.Lists;
    clientContext.Load<ListCollection>(lists);
    clientContext.ExecuteQuery();
}

REST (C#)

var spContext = SharePointContextProvider.Current.GetSharePointContext(Context);
HttpWebRequest listRequest = (HttpWebRequest)HttpWebRequest.Create(spContext.SPAppWebUrl + "/_api/web/lists");
listRequest.Method = "GET";
listRequest.Accept = "application/atom+xml";
listRequest.ContentType = "application/atom+xml;type=entry";
listRequest.Headers.Add("Authorization", "Bearer " + spContext.UserAccessTokenForSPHost);

JSOM

var appweburl = decodeURIComponent(getQueryStringParameter("SPAppWebUrl"));
var hostweburl = decodeURIComponent(getQueryStringParameter("SPHostUrl"));
var clientContext = new SP.ClientContext(appweburl);
var factory = new SP.ProxyWebRequestExecutorFactory(appweburl);
clientContext.set_webRequestExecutorFactory(factory);
var appContextSite = new SP.AppContextSite(clientContext, hostweburl);
var hostWeb = appContextSite.get_web();
hostWebListColl = hostWeb.get_lists();
clientContext.load(hostWebListColl);
clientContext.executeQueryAsync(onHostWebGetListSuccess, onJSOMError);

Note that using JSOM, we still need to construct a ClientContext for the app web before we generate an AppContextSite for the host web, made possible through the SP.ProxyWebRequestExecutorFactory.

A note about the app-only authorization policy

By default, authorization checks in the host web succeed only if both the current user and the app have sufficient permissions to perform the action in question, such as reading from or writing to a list. We are reminded that the user’s permissions are taken into account based on the names of the context and access token objects we use in these scenarios: for instance, CreateUserClientContextForSPHost and UserAccessTokenForSPHost. However, your app has the ability to do something akin to running with elevated privileges using the app-only policy for authorization. Also controlled through the app manifest, the app-only policy is useful when an app doesn’t need or want to consider the permissions of the current user. In Visual Studio 2013, you can specify that your app would like to have the ability to use the app-only policy by checking this box in the AppManifest.xml editor, on the Permissions tab:

apponly

That being said, just because your app is granted this permission does not mean that you can use the same host web context or access token as before to automatically leverage it. To access data from the SharePoint host web (taking only your app’s permissions into account and ignoring the current user’s permissions) from your app, use the following pattern:

CSOM (C#)

var spContext = SharePointContextProvider.Current.GetSharePointContext(Context);

using (var clientContext = spContext.CreateAppOnlyClientContextForSPHost())
{
    Web web = clientContext.Web;
    clientContext.Load(web);
    clientContext.ExecuteQuery();

    ListCollection lists = web.Lists;
    clientContext.Load<ListCollection>(lists);
    clientContext.ExecuteQuery();
}

REST (C#)

var spContext = SharePointContextProvider.Current.GetSharePointContext(Context);
HttpWebRequest listRequest = (HttpWebRequest)HttpWebRequest.Create(spContext.SPAppWebUrl + "/_api/web/lists");
listRequest.Method = "GET";
listRequest.Accept = "application/atom+xml";
listRequest.ContentType = "application/atom+xml;type=entry";
listRequest.Headers.Add("Authorization", "Bearer " + spContext.AppOnlyAccessTokenForSPHost);

Remember that in order to use the app-only policy, your app must request and be granted this permission by the site owner who installs your app. Also note that there is no JSOM example using the app-only policy because apps that do not make OAuth authenticated calls (such as apps that are only JavaScript running in the app web) cannot use the app-only policy.

As you can see, the code you write in each of the above scenarios (accessing data in the app web, host web, or using the app-only authorization policy) is identical except for the method or property you use from the SharePointContext class to get the appropriate context or access token. Understanding these subtle differences is vitally important when making sure your app has the ability to access and manipulate the SharePoint data it needs.

  • Gaytri Nagpal

    Hi Danny,
    Very Nice Article. Thank you so much for posting this. I am facing a permission issue.Please help me in that. I am creating a sharepoint hosted app for attaching document library doc as an attachment in outlook. I am using javascript csom (JSOM) to achieve the same. Following code works ok with sharepoint on premises :-
    outlook = new ActiveXObject(‘Outlook.Application’);
    otem = outlook.CreateItem(0);
    otem.Attachments.Add(DocUrl);
    However, sharepoint online throws “you dont have permisssion to perform this operation” on third line of code i.e. otem.Attachments.Add(DocUrl); where docurl is sharepoint online url.
    Could you please suggest what setting I will have to do in order to make this work.

    • I am able to reproduce your problem, even with my SharePoint Online site URL in my IE Trusted Sites zone and with all my ActiveX security options set as permissively as possible. With that in mind, I’m not sure if there are any other settings you could adjust to fix this error.

      I would encourage you to reach out to the Office 365 Technical Community on Yammer (everyone is welcome to join) at https://www.yammer.com/itpronetwork. Many members of the Office 365 team at Microsoft are very active in that group. Someone there should be able to help you further. Best of luck!

  • yang

    Hi Danny,
    Great Article.

    I am facing a problem.I create a solution contains two project.A sharepoint app and a azure web site.The sharepoint app hosted on azure and created some lists.I installed the sharepoint app on sharepoint collection.I used the following code.

    var spctx = SharePointContextProvider.Current.GetSharePointContext(Context);

    using (var ctx = spctx.CreateUserClientContextForSPAppWeb()){}

    I got the ctx was null sometimes:(. I did not know why?

    Please help me in that.Thanks…

    yang

  • TrollPatrol

    Hi Danny and thanks for the clear informations. I need your expertise on answering a simple question (which is for me unsolvable at the moment) :
    I need to do a SharePoint app, viewed through a Webpart on a SP local site. This app will have to use a SP List data from the SP local site. Agreed that the app is not hosted on the same domain, but the AppWeb is. My question is :

    Where do I have to put my code if I want to make it works ? I have 2 projects in the solution (the app and the app web). In the App I’ve got the webpart. I would like to do CSOM or REST API call in order to get information from the list, and then templating these data into an already nice and clean HTML/CSS design.

    I’m currently trying to do an app.js file to process the data into the App Web project, and display the results into Default.aspx of the App Web. This side is a bit unclear to me, could someone take 2 minuts to help me (maybe we could discuss around a corner with a cofee !) thanks

    • You should be able to accomplish all you need in a simple SharePoint-hosted app (which does not require a separate web application project; that’s only needed for provider-hosted apps where you need to run server-side code). In your App.js, you can use either the JavaScript client object model or the REST APIs to request list data from the host web (obviously your app will need to request and be granted that permission first). For a very thorough write-up of your options, check out Chris O’Brien’s post here: http://www.sharepointnutsandbolts.com/2012/11/access-end-user-data-in-host-web-from.html

      • TrollPatrol

        Thanks for your answer ! I already tried a SharePoint-hosted app, but I’ve faced multiple issues :
        – as I’ve got a self-signed certificate, the app won’t be trusted by Sharepoint and prompt the user to be careful : by doing provider hosted app, i can directly use my site certificate to dispay over SSL the app in a webpart (it does a redirect).
        – I never managed to be able to view my app in a Sharepoint page : worked fine when displaying the app from its location (“spapp-c47550980302b1.mydomain.local”), but I’ve got some trouble with DNS when i’m on my SP site (https://srv-myspsite.mydomain.local/sites/mysite) and I want to access to a “view” of the app content.

        Don’t know if I’m clear enough, maybe I don’t use the right vocabulary. Anyway thanks for the link i’m gonna check out.

        • Makes sense. You should be able to use the same strategies in a provider-hosted app as well. Good luck!

          • TrollPatrol

            Seems I can’t do anything into my App Web with js. Still got a null context whether I’m constructing it from hostWebUrl or appWebUrl … Ok thanks I will need luck for sure πŸ™‚

          • TrollPatrol

            Hi again ! I managed to do it ! (YES!)
            I rebuilt an entire new provider-hosted solution, and I tried another approach : the connection via the RESTful SharePoint API.

            As CSOM wasn’t working (maybe I would have make it works if dive deeper into the issue) I tried a simple ajax request to get a list content and eureka ! I’ll provide my tips & tricks learnt on my blog (I love knowledge sharing). Now begin my real work : code πŸ™‚

  • Jer Santos

    Hi Danny,

    This is a great write. I have yet to try it out. But I hope you can indulge me with my question if my case applies with the above. Just a hard time interpreting it against my case.

    I am creating a web service deployed in Azure reading data from a Sharepoint Online list. Well short of saying i’m stuck. πŸ™‚

    During debug, it seems not able to access the Sharepoint site at ExecuteQuery.

    using (ClientContext clientContext = new ClientContext(Globals.siteURL))

    {

    Web web = clientContext.Web;

    clientContext.AuthenticationMode = ClientAuthenticationMode.FormsAuthentication;

    FormsAuthenticationLoginInfo formsAuthInfo = new FormsAuthenticationLoginInfo(“xxx@xxxx.onmicrosoft.com”, “xxxxx”);

    Uri uri = new Uri(Globals.siteURL);

    formsAuthInfo.AuthenticationServiceUrl = uri;

    clientContext.FormsAuthenticationLoginInfo = formsAuthInfo;

    SP.List proceduresList = clientContext.Web.Lists.GetByTitle(“Procedures”);

    SP.CamlQuery query = new SP.CamlQuery();

    query.ViewXml = “”;

    ListItemCollection collListItem = proceduresList.GetItems(query);
    clientContext.Load(

    collListItem,

    items => items.Include(

    item => item[“Procedure Code”],

    item => item[“Display Desc”]));

    clientContext.ExecuteQuery();

    Is it a requirement that in the server explorer that I define connection to Sharepoint? But it only applies to on-premises Sharepoint right?

    • Jer, in your case I believe the issue may be how you are providing your authentication credentials when creating the ClientContext. There is a class called SharePointOnlineCredentials included in Microsoft.SharePoint.Client.dll that you should use instead of specifying FormsAuthentication credentials when authenticating to SharePoint Online. For more details, see this blog post:
      http://www.vrdmn.com/2013/01/authenticating-net-client-object-model.html

      • Jer Santos

        Thanks a lot!!!! It seems to work. The error is now 403. I will try to look. But feel free to comment!

  • Zach Tyler

    Very useful post. However, I would’ve liked to see some examples of JS REST calls using executeAsync, especially since it was mentioned that REST calls are the easier route to take.