Udostępnij za pośrednictwem


SharePoint 2013 App Only Policy Made Easy

This post demonstrates the app only policy for SharePoint 2013 app development and show how to create SharePoint 2013 apps using OAuth tokens with either a trust to ACS as well as S2S.  I show how to request the AllowAppOnlyPolicy permission and how to execute actions that the current user is not authorized to perform directly.

Background

As developers start working with SharePoint 2013 apps, you quickly realize how permissions with apps work when performing work on behalf of a user.  I can log in as the site collection administrator, and execute an app that writes to a list and see the action fail because permission was denied.  The reason it was denied is because the app did not request write permissions to the list it was trying to write to.  Similarly, I can log in as a user who only has read permission to a list and invoke an app that has full control permission and see an attempt to write to a list fail because permission was denied.  The reason this time is that even though the app had permissions, the user did not.

This is explained in the following diagram.  The app is making a call into SharePoint, and it does so by providing the OAuth access token.  SharePoint sees that no user credentials were provided in the request and an OAuth access token is present.  When acting on behalf of a user, the token will include user information, so the context will be the App+User context.

image

This is exactly the scenario I just described.  The App+User policy is applied, and both the user and the app must have sufficient permission to write to the list.

Can I Elevate Privileges?

When presenting this material to developers, this is usually right about the time someone asks me about the RunWithElevatedPrivileges API.  It should be renamed to “RunWithScissors”, because so many developers use it but don’t quite understand what the API is doing, and it is this confusion that usually generates support calls on how to properly use the API in a predictable manner.  The new app model does away with this impersonation confusion and makes things quite a bit more straightforward because there is no impersonation capability in the API.

And when I say this to developers looking at apps for the first time, they usually want to throw things at me.  How the heck am I supposed to get things done?!?  Give users permissions to everything?!?  

Let’s analyze the requirement instead of focusing on how we arrive at the solution.  We need to perform an action that the user is not entitled to.  OK, now that we agree on semantics, how, then, do I use the new app model to perform actions when the user doesn’t have permission?  In the scenario presented earlier, how can I create an app that has privileges to do something such as writing to a list even though the current user does not have permissions to write to the list?  The answer is the App Only Policy. 

Don’t think of this as impersonation or elevation of the current user’s privileges, it’s not.  In fact, we are not going to use the permissions of the current user at all.

The App Only Policy

In the previous diagram, we highlighted a flow where an app is calling into SharePoint using the CSOM.  Our new scenario of an app performing work that the user does not have permission to means we will use the app only policy.  Following the flow diagram from the Start node, there is no user credential provided, an OAuth access token is present, and this time the token does not contain user information.  In this case, we are solely evaluating permissions based on the permission of the app and not on the user. 

Doing this is actually pretty straightforward.  The first step is to request permission to use the app only policy in your app manifest by adding the AllowAppOnlyPolicy attribute to the AppPermissionRequests node with a value of true.

image

When your app requests this permission, a subtle change is reflected in the trust dialog.  You now see the text “Let it share its permissions with other users.”. 

image

This text is letting you know that the app is requesting permission to perform actions that the user may not have permission to.  In this dialog, the app will be granted Write permission (which means read, create, edit, or delete items) in the Announcements list and will be able to do so whether the user has permission or not.

This capability is only available to provider-hosted and Azure auto-hosted apps.  It is not available to SharePoint-hosted apps.  In a SharePoint-hosted app, there is necessarily always an app+user context. 

App Only Context with S2S

Creating a provider-hosted app that leverages only policy using S2S is actually very easy.  The first step is to make sure that you add the AllowAppOnlyPolicy attribute in the app manifest.  The next step is to use the TokenHelper::GetS2SAccessTokenWithWindowsIdentity method, passing a null for the WindowsIdentity parameter.

 string appOnlyAccessToken = 
   TokenHelper.GetS2SAccessTokenWithWindowsIdentity(_hostWeb, null);

If the app manifest has the AllowAppOnlyPolicy attribute set to true, this call will succeed and return a valid OAuth access token that does not contain user information.  Once you have the access token, then you can use the TokenHelper::GetClientContextWithAccessToken method to obtain a CSOM client context.

 using (ClientContext clientContext = 
    TokenHelper.GetClientContextWithAccessToken(_hostWeb.ToString(), appOnlyAccessToken))
{
    List list = clientContext.Web.Lists.GetByTitle("Announcements");
    ListItemCreationInformation info = new ListItemCreationInformation();
    Microsoft.SharePoint.Client.ListItem item = list.AddItem(info);
    item["Title"] = "Created from CSOM";
    item["Body"] = "Created from CSOM " + DateTime.Now.ToLongTimeString();

    item.Update();
    clientContext.Load(item);
    clientContext.ExecuteQuery();
}

To test this, deploy the app and then log on as a user that only has read permission to the list.  Execute the code, and a new item is created even though the user does not have permission to create items in the list.  The Created By and Modified By fields in the list will reflect that it was only the app’s permissions that were used to create the item.

image

If we had included a user identity (as the code generated by Visual Studio does by default), then the created by and modified by fields would look a little different, showing that code was executed on behalf of an individual.

image

 

App Only Context with ACS

Creating an app-only context with a provider-hosted app using a trust to ACS is just slightly more involved.  A trust to ACS is automatically established when you create your O365 tenant.  If you are self-hosting your SharePoint farm, then a trust to ACS can be established between your SharePoint farm and ACS.

We use a similar approach as before, but this time we need to extract the context token string and validate the token in order to retrieve information such as the authentication realm.  Provided this information, we can now request an app-only token using TokenHelper::GetAppOnlyAccessToken.  Once we have the access token, we can now obtain the client context using TokenHelper::GetClientContextWithAccessToken.

 string _hostWeb = new Uri(Request.QueryString["SPHostUrl"]);
string _contextTokenString = TokenHelper.GetContextTokenFromRequest(Page.Request);

//Get context token.
SharePointContextToken contextToken =
    TokenHelper.ReadAndValidateContextToken_contextTokenString, Request.Url.Authority);

//Get app only access token.
string appOnlyAccessToken = 
    TokenHelper.GetAppOnlyAccessToken(contextToken.TargetPrincipalName, 
            _hostWeb.Authority, contextToken.Realm).AccessToken;

Response.Write("<h2>Valid app-only access token retrieved</h2>");
Response.Write("<p>" + appOnlyAccessToken + "</p>");
Response.Flush();

using (ClientContext clientContext = 
    TokenHelper.GetClientContextWithAccessToken(_hostWeb.ToString(), appOnlyAccessToken))
{
    List list = clientContext.Web.Lists.GetByTitle("Announcements");
    ListItemCreationInformation info = new ListItemCreationInformation();
    Microsoft.SharePoint.Client.ListItem item = list.AddItem(info);
    item["Title"] = "Created from CSOM";
    item["Body"] = "Created from CSOM " + DateTime.Now.ToLongTimeString();

    item.Update();
    clientContext.Load(item);
    clientContext.ExecuteQuery();
}

The result is the same as before… a user that does not have write permission to a list is able to execute the app, which has been granted the AllowAppOnlyPolicy in the permission requests in the app manifest, and the write to list operation now succeeds.

 

For More Information

App authorization policy types in SharePoint 2013

SharePoint Low-Trust Apps for On-Premises Deployments

Comments

  • Anonymous
    July 15, 2014
    Kirk, excellent article! I have a question: on the SharePoint side, is it possible somehow to track the user that originates the request when using the AppOnlyPolicy?

  • Anonymous
    July 15, 2014
    The comment has been removed

  • Anonymous
    July 15, 2014
    As expected :) Anyway, I'm asking this from a Microsoft license compliance perspective. Let's say that some internal users access SharePoint through a provider-hosted app with the AppOnlyPolicy enabled. I wonder if there's any impact on the license metering process from Microsoft's point of view. Maybe using a device CAL for the remote server? Any good resource on this?

  • Anonymous
    July 15, 2014
    I have no idea about licensing :)  Contact a licensing specialist?  Apologies, I have no idea how to find those, either.

  • Anonymous
    August 27, 2014
    The comment has been removed

  • Anonymous
    August 27, 2014
    The comment has been removed

  • Anonymous
    January 18, 2015
    Hi Kirk,I've a question about "AllowAppOnlyPolicy" and the current user Information. I've a provider-hosted App. The remote web allows anonymous Access (hosted in IIS). The App UI is embedded within a public Website (full SharePoint on-premise Installation). The App is used by anonymous AND authenticated SharePoint user. How can I read the current user Information if available - means not anonymous? The AppOnlyContext tells me everytime only "SharePoint/App". Due to the fact that the remote web allows anonymous access, I cannot use the "normal" SharePointContext -> user identity is not set. Because I need the current user to realize a Single-Sign-On (SSO) mechanism.

  • Anonymous
    March 09, 2015
    Thanks Kirk, this article is great.I have a question that it is possible and how to apply User-only policy to SharePoint 2013 app?

  • Anonymous
    March 09, 2015
    @Adam - no such thing as "user-only policy".  If you mean you want to use CSOM without OAuth and without an app context, then just use some JavaScript that calls CSOM in a SharePoint page.  

  • Anonymous
    April 17, 2015
    I was wondering how you would call the depreciated lists.asmx using App Only Context. I was attempting to use WebRequest, but I can't figure out how to include the credential of the App Only context...Is this possible? I ask because I want to retrieve recurring events from a calendar in my provider hosted app, but I can't seem to get recurring events via REST / CSOM calls..Thanks for any assistance!!!

  • Anonymous
    April 17, 2015
    @Mike - the ASMX services do not support OAuth.  You would need to instead use CSOM or REST API with OAuth.  See:msdn.microsoft.com/.../fp179912(v=office.15).aspx

  • Anonymous
    May 12, 2015
    Is there a way to use App+user policy in the remote IIS website?   I need to determine the user has full control on the SharePoint site to even allow him/her to access my remote IIS website. Related link: sharepoint.stackexchange.com/.../how-to-manage-security-for-on-prem-provider-hosted-apps-that-are-published-to-sh

  • Anonymous
    May 12, 2015
    Rohan - answered on StackExchange.  

  • Anonymous
    May 31, 2015
    Hi Kirk, I have created a Azure web jobs to provision the site collection remotely. I have registered the app in SPO with Tenant, Taxonomy, Site Collection & Site with Full control privilege. My Azure job is creating the site collection successfully but am unable to create a Term in Term store. I am getting an error - "Access denied. you dont have permission to access or read this resource" :( I am using Term store for Custom Gobal Navigation for across site collections. Can you please help me on this?

  • Anonymous
    June 09, 2015
    Hi Shankar, We have similar situation. Can you please share the resolution?

  • Anonymous
    June 16, 2015
    We have the same problem and need a solution, please give feedback on this one!

  • Anonymous
    July 29, 2015
    Excellent article! :) I have 2 questions

  1. Behind the scenes, what is the account that is used for running this app (For creating ITEM in this case). For farm solution, with runWIthElevatePrev , we used to have System account (App pool account)
  2. I don't understand much about S2S and ACS, can you please provide some links where this is explained. I tried to search but couldnt find anything solid
  • Anonymous
    August 27, 2015
    Its a great blog. Great writings. I have a question, if we don't have the App Isolation set up in the SharePoint server, is there any possibility that AppOnlyPolicy will not work on the testing(localhost) app machine i.e., I have just created a test app in Visual Studio, and set up the certificate thing and hit F5. The app is working fine with Use token, but when I try to run it with App Only, its throwing 401: Unauthorized. Looking for you reply on this. Thanks, Mohan mohanudupa@gmail.com

  • Anonymous
    August 31, 2015
    The comment has been removed

  • Anonymous
    August 31, 2015
    @Mohan - you need app isolation for this to work.

  • Anonymous
    January 27, 2016
    Kirk,Great article. One thing though, this appears to not work with SharePoint Online. Is this correct? If so, is there a way to make AppOnly work with SharePoint Online? We have an Azure hosted web app that we'd like to have AppOnly access with various SharePoint Online instances that are not linked in any way to our ACS. Is this possible? I thought it was until now. Any feedback would be greatly appreciated, thanks.

  • Anonymous
    March 31, 2016
    Hi Kirk,Thanks for a great post. I have an issue where I want to allow the app to create terms without giving the user term store permissions. The app has "app-only calls" enabled and Taxonomy-write permissions.However, I get "Access denied. You do not have permission to perform this action or access this resource." when I run the code. Just for testing, I added "everyone" as contributor to the term store and then it works. It seems like I need to give the app permission insite the term store as well? Is this possible?Thanks,Joakim