Managing Identity and Context in Low-Trust Hybrid SharePoint 2013 Apps

SharePoint 2013 Apps written for Office 365 and on-premises installations using Windows Azure Active Directory rely on OAuth for authentication of the app and users of the app against SharePoint. Conceptually, the way this works is fairly simple, but in practice it can be complex when you are starting from scratch because there is some infrastructure and related code that you will almost certainly need to build to manage the various pieces of the puzzle. The SharePoint App project templates in Visual Studio include a file named TokenHelper.cs which contains essential functionality for processing tokens and creating authenticated connects to SharePoint, but there are no out-of-box components for storing and managing the tokens with which TokenHelper helps.

This is a very high-level post that you can use as a basis for your solution to this problem. I included links to Microsoft’s documentation if you need more detailed information.

Initial Authentication Flow

Step 1 – App Redirect

When a user launches an app in SharePoint, the first step is an HTTP GET of SharePoint page named appredirect.aspx, _layouts/15/appredirect.aspx. The GET includes a single parameter, instanceId. AppRedirect uses the instanceId to get information from the app’s manifest that the page uses to build the URL and form POST for the next step in the process. This information includes the app’s ClientId, the app’s start page URL, and the query string parameters that should be passed to the start page.

ClientId is a GUID that represents the app’s identity. It is used by tokenhelper.cs along with the ClientSecret to generate the tokens the app will require to talk to SharePoint. These values are created when the app is registered with SharePoint.

Step 2 – App Start Page

The app redirect page issues a POST to your app’s start page. In a hybrid app, this page is on your application server and it redirects to the App Web once it is finished processing and storing the authentication data. The POST contains quite a bit of important information and (for the most part) this is your only chance to grab it!

If your manifest specifies the Visual Studio default tokens, {StandardTokens}, the query string will contain two critical pieces of information: SPHostUrl and SPAppWebUrl.

  • SPHostUrl is the url of the web where the app is installed.
  • SPAppWebUrl is the url of the app web assuming there is one. If this parameter is not present, the app does not include an app web.

The POST’s form body contains the rest of the information. The most important parameter is SPAppToken. SPAppToken is specific to low-trust configurations. If you are using a high-trust configuration the SPAppToken token is missing. This fact causes a great deal of confusion because all of the original SDK samples assume low-trust and break when they attempt to get this value in a high-trust configuration.

SPAppToken is an encrypted string that contains many more parameters with which we are concerned. TokenHelper.cs uses the Microsoft.Identity classes to decrypt SPAppToken with the ClientSecret. The decrypted and parsed SPAppToken contains several important pieces of information:

  • CacheKey – The cache key is an opaque (non-readable) string that is unique to the combination of user, app, and tenant: CacheKey = UserNameId + "," + UserNameIdIssuer + "," + ApplicationId + "," + Realm
  • AccessToken – The token required to create an authenticated connection to SharePoint, it expires in 12 hours.
  • RefreshToken – The token used to request a new AccessToken or RefreshToken, it expires in 6 months.

If you have these values, you can talk to SharePoint on behalf of the user.

Values you need to Persist and Why

In practice you will always need a mechanism to persist these key values. I use a combination of Windows Azure SQL Database for the tokens, the App Web’s property bag for the host url, and session cookies for my version of the CacheKey. This allows me to accommodate the following scenarios:

  1. Deep links – If a user enters my app via a link instead of from the app’s icon in the host site the traversal of appredirect and the app start page won’t happen. If there is a session cookie for the CacheKey everything is fine because the code can get the tokens from persisted storage which can be used to create a full context. However, if the cookie does not exist the app needs to redirect the user to appredirect to authenticate the app for the user. For the redirect to work the code needs the SPHostUrl and the instance id.
  2. Background processes – I use Azure worker roles for long running processes. To create a CSOM client context, the input to the job must include a valid cache key.
  3. Authentication to the provider hosted web site and services – My apps don’t actually store the cache key from SharePoint. They add additional information to the key and encrypt the result using a private certificate. The creation of this value requires the user to have successfully launched the app from SharePoint and the user can be considered authenticated. By storing this value in the database and in the session key I add a layer of protection that I build on as part of my services infrastructure.
  4. Privacy – My apps never store any information that can be used to identify an individual user without help from the SharePoint site. If someone with malicious intent gets the content of this database it will do them no good without also breaking the encryption, knowing the app identity, client secret, and app web url.

Figuring this all out took me much time and effort. Hopefully this post can spare you from going through that, but if you would like some consulting or training on the subject I would love to hear from you!

Happy coding!
–Doug

Author: Doug Ware