SharePoint Low-Trust Apps for On-Premises Deployments


PowerShell Functions and Scripts Post

Note: I’ve been wanting to share the details on setting up an OnPrem SharePoint environment to support low-trust apps for a while now. I had some help from the Product Group to get things working, and there were several catches and ‘Aha!’ moments involved which many of you are I’m sure stumbling over. So I dug in today and hammered out this post and the associated post of PowerShell helper functions to get this info out there. Let me know if (and when!) you find details that need clarification or might need correction. Enjoy!

A quick summary of SharePoint apps

SharePoint apps can be hosted inside SharePoint or outside of it. Apps hosted outside of SharePoint can be manually provisioned to an IIS server, an Apache server, an Azure Compute Services role, or any other host; or can be automatically provisioned and hosted in Azure Web Sites. Externally-hosted apps can call into SharePoint to request data on behalf of the user using the App Authentication model, an implementation of OAuth. Of the several OAuth profiles available, SharePoint apps implement and extend two of them under the names “High-Trust” and “Low-Trust.” In this post, we’ll discuss the protocol flow for SharePoint low-trust apps and how to configure an On-Prem SharePoint farm to support it.

A brief history of OAuth

OAuth version 1 was prepared by some of the biggest names of Web 2.0 – Twitter, Google, Facebook, Flickr. It provides a simple means for users to delegate limited authorization to their data to applications and services without sharing master credentials (e.g. username and password). For example, by using OAuth Facebook doesn’t need to ask you to share your Google password to get your Google contacts; instead you delegate to Facebook specific authorization to read your Google contacts list. In the canonical example (from the RFC), you can grant a photo printing service authorization to read your photos from a photo storage service without sharing your password with the printing service or granting that service read/write privileges to your photos and other account data at the photo storage service provider.

OAuth version 2 has been under discussion for several years now and RFC 6749 is the first of several RFCs documenting it. Significant (and controversial) additions to OAuth 2 include the JavaScript Web Token (JWT, pronounced ‘jot’) profile and the OAuth Assertion Framework, which together have made it easier to use OAuth for authentication as well as delegated authorization. Microsoft’s implementations of OAuth make heavy use of JWTs, and you’ll find the JWT Bearer Token profile to be the foundation for Microsoft’s High-Trust, Server-to-Server authentication schemes. For low-trust authentication, SharePoint uses a version of the classic authorization code flow discussed in the previous paragraph.

OAuth in SharePoint low-trust apps

SharePoint low-trust apps rely on the OAuth authorization code flow (“grant type”) to delegate limited rights to apps to act as users. For this to work, both SharePoint and the Client application (the SharePoint app) must trust and communicate with an Authentication Provider; SharePoint relies on Azure Active Directory. Azure AD, in turn, must be aware of SharePoint and the Client app in order to grant them the necessary codes and tokens to work together.

Let’s orient ourselves by presenting the overall protocol flow:

SharePoint 2013 OAuth

In this picture, the ‘S’ icon represents SharePoint and the globe represents an externally-hosted SharePoint app. The floating blue window represents Azure Active Directory, as indicated. In step 1, a user requests a page or an app directly from SharePoint. The app could be hosted directly within a SharePoint page in an IFrame, or might be on its own page. In either case, SharePoint will need to send the user’s browser to the external host to retrieve the app page, either via the location tag of the IFrame element or a 302 Redirect HTTP status; it will do so in step 3. First, though, SharePoint gets an OAuth authorization code to share with the app; the app will use this code to ultimately communicate back with SharePoint acting as the user. In step 2, SharePoint asks Azure Active Directory Access Control Service for an authorization code for the app. Now, in step 3, SharePoint redirects the user’s browser to the app, including the authz code with the redirected request (as the SPAppToken form parameter).

The app doesn’t use the authorization code directly to authenticate to SharePoint. Instead, in step 4 it calls Azure AD ACS and exchanges the authorization code it received from SharePoint for a proper Access Token. In step 5 the app uses this access token to authenticate to and access data from SharePoint, and finally in step 6 it uses this data to present a page to the user.

Setup SharePoint for low-trust apps

There’s lots to say about the various steps in this flow, but our focus here will be on setting up an On-Premises SharePoint 2013 farm to participate in it via a connection to Azure Active Directory. Most of this post will focus on enabling step 2 above – the connection between SharePoint and Azure Active Directory. The three steps you’ll need to follow to setup a SharePoint farm for low-trust apps are as follows:

  1. General SharePoint setup for apps (e.g. App Management Service Application, app isolation).
  2. Connect SharePoint to Azure Active Directory.
  3. Create an App Principal in AAD and SharePoint.

Step 1 is already well-documented on TechNet and other Web sites, so we’ll start at step 2.

Connect SharePoint to Azure Active Directory

If you haven’t yet, follow the steps in my previous post to get the Azure Active Directory module for PowerShell set up. You’ll need an AAD tenant and AAD/MSOL PowerShell for the next steps, and you’ll follow the discussion better after you’ve experimented a bit with AAD.

To completely configure a connection between your OnPrem SharePoint farm and Azure AD, you’ll need to complete the following steps:

  1. Replace the local STS signing certificate with one Azure AD can trust. I use a self-signed certificate, though a certificate from a trusted global authority should also work.
  2. Associate this certificate with the SharePoint principal in Azure Active Directory.
  3. Create an SPN for the OnPrem SharePoint farm and add it to the SharePoint principal in Azure Active Directory.
  4. Configure the authentication realm for the local SharePoint farm to match the AAD realm.
  5. Create an Azure Access Control Service application proxy in SharePoint.
  6. Create a Trusted Security Token Service for Azure ACS in SharePoint.

Remember that in step 2 above SharePoint calls in to AAD to request an authorization code, which requires authentication. The SharePoint username for this authentication is a well-known GUID representing SharePoint in Azure AD and O365 (00000003-0000-0ff1-ce00-000000000000). The credential used is an X.509 certificate, specifically SharePoint’s local STS signing certificate. Worth stating again: the same certificate used by SharePoint’s local STS to sign security tokens is used by SharePoint to authenticate to Azure Active Directory. That’s why the first step above is to replace the local signing certificate with one that can be used for authentication to AAD

I’ve created PowerShell functions which take care of the above steps and which you can review for the technical nitty-gritty; they’re named Replace-SPSTSSigningCertificate and Connect-SPFarmToAAD. You can access them individually at this post’s sister post, or download the whole package of scripts related to this post here. For further details, refer to the sister post and to the scripts themselves.

If you’ve saved all the files to the same directory, you can navigate there and use the following commands to set up your connection, replacing the AADDomain name and SharePoint Web URL appropriately. In the zip file, a similar script is available named ‘_ConnectSharePointToAAD.ps1’.

$FolderPath = if ($psISE) {Split-Path -Path $psISE.CurrentFile.FullPath -Parent} elseif ($PSScriptRoot) {$PSScriptRoot} else {“.”}
Import-Module -Name (‘{0}\{1}’ -f $FolderPath,’New-SelfSignedCertificate.ps1′)
Import-Module -Name (‘{0}\{1}’ -f $FolderPath,’Grant-CertificateAccess.ps1′)
Import-Module -Name (‘{0}\{1}’ -f $FolderPath,’Replace-SPSTSSigningCertificate.ps1′)
Import-Module -Name (‘{0}\{1}’ -f $FolderPath,’Connect-SPFarmToAAD.ps1′)

Add-PSSnapin Microsoft.SharePoint.PowerShell
Import-Module -Name MSOnline

Replace-SPSTSSigningCertificate `
  
-SPSTSCertName ‘CN=SharePoint Security Token Service, OU=SharePoint, O=Custom, C=US’ `
  
-RootAuthorityName ‘Self-Signed STS Signing Certificate’ `
  
-CerPath ‘C:\temp\SPSTS.cer’ `
  
-PfxPath ‘C:\temp\SPSTS.pfx’ `
  
-CleanSTSSigningCerts `
  
-Verbose

Write-Warning ‘The STS Signing Certificate has been replaced. It is recommended that you restart your system before continuing.’

Connect-SPFarmToAAD `
  
-AADDomain ‘joshgav1.onmicrosoft.com’ `
  
-SharePointWebUrl https://intranet.joshgav.com `
  
-RemoveExistingACS `
  
-RemoveExistingSTS `
  
-RemoveExistingAADCredentials `
  
-Verbose

Create App Principals

We’ve connected SharePoint to AAD, but to actually utilize this connection, we need to create AppPrincipals in Azure Active Directory and SharePoint. These AppPrincipals represent the remote-hosted Web Applications which will connect to SharePoint acting as users. Azure AD needs to be aware of these principals to be able to issue authorization codes for them (step 2) and access tokens to them (step 4). SharePoint needs to be aware of them to allow them access on behalf of users (step 5).

As things currently stand, you must create principals separately in SharePoint and in AAD. Work is underway to allow SharePoint to create a service principal in AAD automatically. In the meantime, though, we’ll use two separate functions to create an AppPrincipal in AAD and SharePoint. Again, refer to the sister post for details on these two functions, Add-ServicePrincipalToAAD and Register-SPAppPrincipalEx. Note that you’ll need the ClientID and Client Secret you use here when you create your SharePoint app, so don’t lose them!

An example, included in the zip as ‘_NewAppPrincipal.ps1,’ follows:

$FolderPath = if ($psISE) {Split-Path -Path $psISE.CurrentFile.FullPath -Parent} elseif ($PSScriptRoot) {$PSScriptRoot} else {“.”}
Import-Module -Name (‘{0}\{1}’ -f $FolderPath,’Register-SPAppPrincipalEx.ps1′)
Import-Module -Name (‘{0}\{1}’ -f $FolderPath,’Add-ServicePrincipalToAAD.ps1′)

$Credential = Get-Credential -Message ‘O365 Credential’

Add-PSSnapin Microsoft.SharePoint.PowerShell
Import-Module MSOnline

$ClientInfo = Add-ServicePrincipalToAAD `
  
-DisplayName ‘OnPremApp1’ `
  
-RedirectUri ‘https://localhost:44300’ `
  
-HostUri ‘localhost:44300’ `
   –
O365Credentials $Credential `
  
-RemoveExistingWithSameDisplayName

$AppPrincipal = Register-SPAppPrincipalEx `
  
-Web https://intranet.joshgav.com `
  
-ClientId $ClientInfo.ClientId `
  
-ClientSecret $ClientInfo.ClientSecret `
  
-HostUri ‘localhost:44300’ `
  
-RedirectUri ‘https://localhost:44300’ `
  
-DisplayName $ClientInfo.ClientDisplayName

$ClientInfo

Create a Remote-Hosted App

At this point, your environment is set. You can create a provider-hosted app in Visual Studio and specify the ClientID and ClientSecret created here in the web.config file. The MSDN samples have a Hello World app to get you started. Don’t forget to change the ClientID and ClientSecret in web.config.

Comments (26)

  1. Will this work with Foundation 2013?

  2. I believe so, but I haven't tested it.

  3. Great article and very useful! Thanks!

  4. Nigel Armstrong says:

    Hi Josh

    Well scratch my last comment.

    When I uploaded the app, everything worked perfectly. So now I'm just wondering why debugging wasn't working…

    Nigel

  5. TomResing says:

    Great info and explanations.

    Will this work without an internet connection? You state that it requires an Azure AD tenant. Does that mean we can't doe low trust apps in secure networks without wide area network access?

    Thanks,

    Tom

  6. Hi Tom,

    Yep, this definitely requires an Internet connection. It falls under a "Hybrid" SharePoint OnPrem/Online topology.

    Best,

    Josh

  7. Rajan says:

    Hi josh,

    Information you have posted on this blog is awesome. Can you suggest me a way to establish a trust between SharePoint Server 2013 on premise and Exchange Online (Office 365). I need to configure a feature in SharePoint, 'Sync to Outlook', which requires the OAuth connectivity between two servers.

    Any help is appreciated.

    Thanks,

    Rajan

  8. Hi Rajan – That would be something with Outlook and Exchange Online. Normally clicking "Sync to Outlook" establishes a connection from the Outlook client to the SharePoint list using regular user authentication, but perhaps the Outlook client behaves differently when connected to Exchange Online.

    Hope that helps a bit,

    Josh

  9. I have to say if your using Azure its not On-Prem

  10. But then thanks for the great guide on configuring a hybrid.  

  11. Jiniv says:

    Hi Josh,

    I am trying to develop a provider hosted app in Sharepoint 2013,can u please help me with that ?

    I would be thankful if you can provide me with something that you have developed 🙂

    I have tried out everything on MSDN links 🙁

    Thanks !!

  12. Besides working with the REST/oData API on Premise for Deskop or Mobile Applications is also not possible? Because you can't get an AccessToken neccessary for getting RequestDigest. Or am I missing something?

    Kind Regards

    Rafael

  13. Rafael – You can get the AccessToken by using the TokenHelper.cs code provided in the SharePoint app VS template, as you would with a CSOM app. You add the token to a header in the REST request: 'Authorization: Bearer <AccessToken>'.

    Hope that helps,

    Josh

  14. Yvan says:

    hello Josh,

    this is a great article and I followed it, but I get an error when I deploy the app from Visual Studio:

    In SharePoint logs I see this:

    Error when try to get access token when deploy AppInstanceId cae1b457-ad95-4947-9693-3181da8eeaee with AppPrincipalId i:0i.t|ms.sp.ext|6d610d19-ebd9-43e5-a8bb-a2b35a482fc2@0fa04868-d5f3-4de1-8dfe-2d1f50832006. Exception=System.InvalidOperationException: An unexpected error was returned from the delegation service.  Error Code: 'invalid_client' Error Description: 'ACS65003: The clientId '6d610d19-ebd9-43e5-a8bb-a2b35a482fc2/localhost:44313@0fa04868-d5f3-4de1-8dfe-2d1f50832006' is not a valid service identity

    and in the output of Visual Studio, just after app starts to install in SharePoint, I see this message:

    Web.config updated with Client ID 6d610d19-ebd9-43e5-a8bb-a2b35a482fc2

    So I guess that somehow I do now correctly register the app in SharePoint and despite I updated ClientId/ClientSecret before installing the app, SP considers it as a different one (since it is assigning a new ClientID to the app).

    So my question is how does SharePoint identifies an app during its installation? there must be something else than the ClientId, otherwise SP would not update it. Or maybe I completely misunderstood something!

    thanks!

  15. Gavin Barron MVP says:

    Is there any plans to develop a low trust mechanism for non-connected scenarios?

    i.e. low trust provider hosted apps with no external connection, like say for a Bank who lock down external access

  16. Yvan says:

    Hello,

    I successfully created a low trust app following your post, but now I cannot deploy any high trust apps anymore.

    Acording to SharePoint logs, SharePoint tries to validate them against ACS.

    Is there a way to prevent this?

    cheers,

    Yvan

  17. Toni Börner says:

    Hi Josh,

    How can I see that the configuration of this Low Trust Szenario is successfull ?

    Can I see this without an App?  I have read that I can see an ACS configuration in the Authentication Provider view in the Central Administration, but I see nothing after I used the scriptes.

    Can you or someone else help me here ?

    Best regards

     Toni

  18. Marco says:

    Yvan, I encountered exactly the same error: "ACS65003: The clientId … is not a valid service identity". Can you explain how did you manage to make it finally work? I'm fighting with it for a veery long time by now and I almost lost the rest of my hair. Any help will be more than apriciated.

  19. fastMotion says:

    Yvan why would you only post that you solved the problem without going into details? I have been struggling with this problem myself and your post is literally the only hit on google.

  20. Arthur Graus says:

    I am building a Provider Hosted app for a client of mine that is targeted to be put in the Dutch App Store.

    My question is, can this app also be used in on premise installations by following these steps:

    1. Register custom X.509 Certificate as STS certificate

    2. Configure SharePoint 2013 Server to use ACS

    3. Register Service Principal Name's of Local Web Application

    4. Enable Feature on Local Web Applications: " Apps that require accessible internet facing endpoints "

    5. Create Reversed Proxy with Access to WebApplication (On Premise)

    6.     Deploy Remote Web to the Cloud

    7. Publish App via Seller Dashboard

    8. Install App via App Store

  21. Krunal Trivedi says:

    Thanks a lot Josh….Awesome Article.

  22. prasad says:

    this is from 2012. the new implementation of low trust apps requires Azure ACS. I would like to see if there are any updated articles that implement low trust with azure ACS..

  23. Sean Hester says:

    Josh, thanks for blazing this trail for us!

    Similar to @prasad above, I'm wondering about where things stand with using Azure Active Directory as the token broker without having an O365 tenancy. From this article, and everything else I've seen on MSDN, it looks like an O365 tenancy is currently required. Could you confirm that? And if it is required, are their plans to remove that requirement in the future?

  24. Kartik Sharma says:

    I am facing the issue similar to that of Yvan, where on deploying the app through visual studio, it takes long time and finally shows that operation timed out.  ULS logs say that: The ClientID '…' is not a valid service identity. Can anyone help on that?

  25. Vinzenz says:

    I'm having the exact same scenario that Karik Sharma has. The local deployment times out and the Correlation ID logs show that its trying endlessly to obtain a access token! The ClientID … is not a valid service identiy.

    Is there any fix for this problem?

  26. Baso says:

    Hi Josh,

    this a great article and it helps me setup Low-Trust Apps model in my On-Prem,

    Although, I have faced one issue with Apps Model, actually I have another High_Trust Apps model running on the same SPFarm in a different SpWeb application, and I figure out that all my apps that was already running using High Trust Model is not running anymore properly and deploying them again is also failing on the same Machine.

    Is it possible that a conflict with STS prevent the high-trust apps authenticated , when we "replace the local signing certificate with one that can be used for authentication to AAD".

    I really appreciate if you advice how to remove SPN and ACS between OnPrem and Azure AD.