SharePoint 2013: Using the App Only policy and App Principals instead of username and password combos

Tags: SharePoint 2013, Apps

Introduction

One of the blog post I receive the most e-mails about is the How to do active authentication to Office 365 and SharePoint Online. Some of the feedback I get is “thank you” etc. and some of them are people that are modifying it for all kind of things, especially when trying to do “background jobs” or integrations using user credentials. And this is what this post is all about. My standard answer to that question is that you do not want to use the methods in that post for those scenarios, nowadays, since you can actually build an App for that. And even better you can build an App with a so called App Only policy – which means that we do not need to have any user credentials or anything like that. So in order to save me some time in replying that and then another reply on the “how do I do that?” question I can from now on only link to this post :-)

Scenario

Before we dig down to the actual nuts and bolts, let’s have a scenario in front of us. Assume we have a SharePoint site containing a product list (or similar). We would like an application/app/service/background job (you name it) to read and potentially write to this list. This is a very common scenario and traditionally this was built as timer jobs in SharePoint, or scheduled tasks running as specific users or even with hardcoded username and passwords (yikes!). In this sample we will instead use an App Principal, this is very much like a user principal which you can give permissions to in SharePoint.

In this simple example I will use a site containing a list of products (two columns; Title and Price) and then an ASP.NET MVC web site that will show that list of products. An no, I will not build a SharePoint App, but instead show how you can take advantage of App Principals to get the products from SharePoint without using any user credentials whatsoever.

Create the App Principal

Register an App The first thing we would like to do once we have created our site and the list of products is to generate an App Principal with access to read from this list of products. This is done by navigating to your site and then appending /_layouts/15/AppRegNew.aspx to the URL so that you end up on the “Register an App” page. On that page click on the two Generate buttons to generate an App Id and an App Secret, then provide a Title for your App and the domain (localhost in this demo) for your app.

Once that is done you will get a confirmation and a summary of the app identifier created. Screenshot this and/or copy and paste it so that you have the details, you will need them later.

Give the app some permissonsNext we need to grant permissions to this app. This is done by navigating to /_layouts/15/AppInv.aspx. On that page paste your App Id (generated previously) into the App Id text box and click on the Lookup button, this will retrieve the details of the app and give us the opportunity to give the app some permissions.

Giving the app permissions is not for the faint hearted and the UI is horrible (just like basically any admin UI in SharePoint). You need to write a valid app permission request XML. In this case I only want read rights to a specific list, nothing more, and I also want the app to be able to retrieve this data without any user credentials (App Only). The resulting XML looks like below, and the key thing is the AllowAppOnlyPolicy attribute:

<AppPermissionRequests AllowAppOnlyPolicy="true">
  <AppPermissionRequest
    Scope="http://sharepoint/content/sitecollection/web/list"
    Right="Read"/>
</AppPermissionRequests>

When you click the Create button you will be taken to the “Do you trust” page, where you should trust your app. Since we requested to be able read from a list, we must also choose which list that we should read from. Once we’ve done that we click “Trust it”. One crappy thing is that if you would like to modify the permissions later, you need to re-write the XML completely since the UI does not retrieve the current requested permissions, so you need to document and save your XML.

Trust the app!

If you don’t want to study the app permission request schema I recommend you to look at this MSDN page which will give you some more insights.

(Optional) High-trust apps for on-premises usage

If you’re now doing this for on-premises deployments and do not have configured your farm for low-trust apps (integration with ACS), then you also need to register a high-trust app. To not repeat myself, you can use the same set of PowerShell that I used in the SharePoint 2013 with SAML Claims and Provider Hosted Apps blog post. Just make sure that you generate a new Issuer Id (which is not the same as the Client Id you got above when registering the app.

Building the ASP.NET MVC site

Now it is time to start building the consumer of this products list, in this case an ASP.NET MVC site (but it could be basically anything). Fire up Visual Studio 2013, create a new ASP.NET Web Application, then choose MVC and configure it as you like.

App for SharePoint Web Toolkit NuGet packageThe first thing we need to do is to download a very sweet NuGet package. It is called “App for SharePoint Web Toolkit”. This “little” package will install the SharePoint 2013 client assemblies and also add the TokenHelper.cs and SharePointContext.cs files that you should be familiar with from a normal Provider Hosted SharePoint App.

Update the web.config

Before we start writing the code to retrieve the products from the SharePoint list we need to add some plumbing into the web.config. Depending on if you’re using ACS or using the High-Trust approach you need to add different configuration. For a low-trust approach you only need the first two lines (ClientId and ClientSecret that you got from AppRegNew.aspx) and for the high-trust approach you need all settings below, except the ClientSecret, and you also need to have the pfx file of the certificate used for the high-trust configuration.

<add key="ClientId" value="d0ee6f6d-1c39-44b2-a284-f3143e820ec2"/>
<add key="ClientSecret" value="11Flt0LME9mnToNhRN5nmgeynAXW3nMBvJBrIBGlT/o="/>

<add key="ClientSigningCertificatePath" value="C:\temp\AppOnlySite.pfx" />
<add key="ClientSigningCertificatePassword" value="Pass@word1" />
<add key="IssuerId" value="93d08816-eb35-4ed9-be90-ea352cc81130" />

Modifying the Home Controller

To read the stuff from our SharePoint list I modify the Index action of the Home Controller so that it looks like this:

public async Task<ActionResult> Index()
{            
    Uri site = new Uri("https://intranet.sp01.contoso.com/sites/productlist");
    string realm = TokenHelper.GetRealmFromTargetUrl(site);

    // For use with ACS/SPO
    //var tokenResponse = TokenHelper.GetAppOnlyAccessToken(TokenHelper.SharePointPrincipal, site.Authority, realm);
    //var accessToken = tokenResponse.AccessToken;
            
    // High Trust
    var accessToken = TokenHelper.GetS2SAccessTokenWithWindowsIdentity(site, null);
            
    UriBuilder builder = new UriBuilder(site);
    builder.Path += "/_api/lists/GetByTitle('Products')/Items";
    builder.Query = "$select=Title,Price";

    HttpClient client = new HttpClient();
    client.DefaultRequestHeaders.Add("Accept", "application/json;odata=verbose");
    client.DefaultRequestHeaders.Add("Authorization", "Bearer " + accessToken);

    var response = await client.GetAsync(builder.Uri);

    var result = JsonConvert.DeserializeObject<ProductResponse>(await response.Content.ReadAsStringAsync());

    return View(result.Data.Results);
}

The first thing I do is to modify the method signature (#1) and declare it with the async keyword, so that I later can use the await keyword for the asynchronous operations. Then I use the TokenHelper class to retrieve the realm (#4)and the access token (#8 || #11). As you can see there are two different ways to retrieve the access token for an App Only principal, one when using ACS and one when using the High-Trust approach.

Once I have the access token I build the REST URL (#13-15) and then create the HttpClient that I will use to invoke the REST end-point. On line #18 I specify that I want the data back in JSON format and on line #19 I pass the access token for authorization.

Once that is all done we only need to invoke our HTTP client and retrieve the result. Once we have the result I use the JsonConverter to deserialize the data into a nice object structure (ProductResponse). In order to more easily work with the data returned I created these POCOs:

public class ProductResponse
{
    [JsonProperty("d")]
    public ProductResult Data { get; set; }
}
public class ProductResult
{
    [JsonProperty("results")]
    public IEnumerable<Product> Results { get; set; }
}
public class Product
{
    [JsonProperty("Title")]
    public string Title { get; set; }
    [JsonProperty("Price")]
    public float Price { get; set; }
}

The JsonConverter converts my JSON data into this object structure and I can easily then retrieve the actual Product results and return it my View.

Modifying the View

To display the set of products I use the built-in WebGrid in my Index View:

<div class="row">
    <div class="col-md-4">
    @{
        WebGrid grid = new WebGrid(this.Model);
        @grid.GetHtml();
    }  
    </div>    
</div>

This will result in a grid view of my SharePoint products list like this:

Wow, this dude has some serious UI skillz

There you have it! This example can easily be extended with write operations or other stuff you would like to do, as long as you can express the requested permissions using the permission request XML.

Summary

In this post you have seen how we can leverage SharePoint data in other applications without using any user credentials and/or saved passwords. It requires minimal effort, especially when you’re using SharePoint Online (or ACS on-premises) since you only require Site Collection permissions to create the App Principal. All this thanks to the App Only policy.

5 Comments

  • Fabian Williams said

    Wictor, heck of a post. Mark Q is one of my colleagues at Planet, he posed this question (and yes it was for an on prem env) which can yield itself to other solutions, but I love what you have done here. way to go mate.. see u in November at the mothership.

  • Jeremy Thake said

    Nice write up wictor, as usual mate ;-)

    As well as doing this with Azure ACS, you can also do this with Azure Active Directory. This is the future direction of authentication with SharePoint. We actually posted two angularJS samples today that use this approach http://blogs.office.com/2014/08/12/office-365-apis-using-angularjs-standalone-websites-samples-shipped/

  • Wictor said

    @Jeremy - thanks. And those are some great samples. Problem with AAD is that as a site collection admin you cannot this easily generate you clientid and secret (mayhaps in the future though :-)

Comments have been disabled for this content.

About Wictor...

Wictor Wilén is the Nordic Digital Workplace Lead working at Avanade. Wictor has achieved the Microsoft Certified Architect (MCA) - SharePoint 2010, Microsoft Certified Solutions Master (MCSM) - SharePoint  and Microsoft Certified Master (MCM) - SharePoint 2010 certifications. He has also been awarded Microsoft Most Valuable Professional (MVP) for seven consecutive years.

And a word from our sponsors...