Updating Trust Between OnPrem Farms and ACS for Apps When Your SharePoint STS Token Signing Certificate Expires

For those of you who are "in the app way" with SharePoint 2013 (no, not a lot different from being "in the pregnant way", as they say), you'll reach that point sooner or later where the token signing certificate for your SharePoint STS expires if you are using low trust apps on premises. Why is that? Because in order to establish the trust with ACS that is necessary to use low trust apps on prem, you had to replace the token signing certificate that comes out of the box. Most folks simply use a self-signed certificate, which when you get it from the IIS Manager snap-in is good for one year. Even if you get it from a public root authority, they are typically good for a year or two, although there are exceptions.

All that being said, when it comes time to change your STS token signing cert then you will need to re-establish that trust between your on premises farm and ACS. Having just beat my head against the wall for the better part of a day to get this to work I thought I'd share some tips that I used to get me through it. Here are some things you should be looking at:

  1. Get the latest version of the PowerShell modules for MSOL. For some reason this is always harder than it should be, so again, to save you some time, here's where I went to get the latest versions:  https://msdn.microsoft.com/en-us/library/azure/jj151815.aspx. This is the page that describes managing Azure Active Directory using PowerShell and it seems to consistently have the "right" links. On this page you will find a link to the Microsoft Online Services Sign-On Assistant, which you must get and install first. Right below that you will find a link for the 32-bit and 64-bit version of the PowerShell module for Azure Active Directory. I use this one because it has all the MSOL goodness we require. One other thing to note - you must first uninstall your existing version, and it will tell you that you need to reboot after doing so. Yeah, you need to do this. You may or may not get prompted to reboot again after installing it. The net of all this is that I tried to skate by and skip the reboots a couple times and then the Azure AD module just flat out wouldn't install. One reboot later everything installed just fine and using the new modules cleared the first set of roadblocks I had been hitting.
  2. Start with the scripts for establishing trust that we previously published here: https://msdn.microsoft.com/en-us/library/dn155905.aspx.
  3. There are two modifications to the scripts you will need to make; without them you will get errors and stuff will still be broken. Here are the changes:
    1. Find this line of in the main script: [Guid[]] $ExistingKeyIds = Get-MsolServicePrincipal -AppPrincipalId $SP_APPPRINCIPALID | Get-MsolServicePrincipalCredential | % KeyId. Comment it out (by putting a # sign in front of it) as well as the line that follows it: Remove-MsolServicePrincipalCredential -AppPrincipalId $SP_APPPRINCIPALID -KeyIds $ExistingKeyIds. At some point you may end up with key ID that is null, and that causes this part of the script to break. I fixed it with some other PowerShell I wrote up, so that chunk of the script now looks like this:

#Remove existing connection to an Office 365 SharePoint site if the switch is specified.
    if ($RemoveExistingAADCredentials.IsPresent -and $RemoveExistingAADCredentials -eq $true) {
        #[Guid[]] $ExistingKeyIds = Get-MsolServicePrincipal -AppPrincipalId $SP_APPPRINCIPALID | Get-MsolServicePrincipalCredential | % KeyId
        #Remove-MsolServicePrincipalCredential -AppPrincipalId $SP_APPPRINCIPALID -KeyIds $ExistingKeyIds

    [string[]] $ExistingKeyIds = get-MsolServicePrincipalCredential -ReturnKeyValues $returnIds -AppPrincipalID $SP_APPPRINCIPALID | % KeyId

    foreach($id in $ExistingKeyIds)
    {
    If ($id)
    {
        Remove-MsolServicePrincipalCredential -AppPrincipalId $SP_APPPRINCIPALID -KeyIds $id
    }
    }
    }

b. There is a second script that enumerates all of your web apps and adds each one as a ServicePrincipalName (SPN) to the AppPrincipal. If you've already added those SPNs before though, then it causes all sorts of problems and you will end up getting 400 Bad Request errors anyways (as I described here: https://blogs.technet.com/b/speschka/archive/2013/07/29/security-in-sharepoint-apps-part-3.aspx). To fix it you just need to add one line of code to the PowerShell so it will remove the SPN before adding it. So if the SPN already exists it will be removed before adding, and if it doesn't exist it just gets added. Again, the net is you end up with a working script and one SPN for each web app in your farm. Here is the section of that script with the new line of code:

Write-Host "Adding SPN for" $NewSPN

#here's the new line of code
$SPAppPrincipal.ServicePrincipalNames.Remove($NewSPN)

$SPAppPrincipal.ServicePrincipalNames.Add($NewSPN)
Set-MsolServicePrincipal -AppPrincipalId $SPAppPrincipal.AppPrincipalId -ServicePrincipalNames $SPAppPrincipal.ServicePrincipalNames

After doing ALL of these things, my low trust apps on premise are working again. Hope your sailing goes a little smoother.

A Few Additional Notes

You may still experience some issues after using this code. Here are a few suggestions:

  1. Try using the /_layouts/15/appregnew.aspx page to create a new app principal. If things are messed up then you will see an error there, like invalid JWT token.
  2. If appregnew.aspx is not working look in the ULS logs. You should be able to track down the invalid JWT token, and you may see a subsequent error along the lines of request aborted. I call this part of the error out specifically because if you are just trying to deploy a new app from Visual Studio you will also see this error in there - request aborted, request was cancelled, something like that (sorry I don't have the exact wording handy).
  3. If you are getting the invalid JWT tokens I recommend you do this:Also, I recommend when you run the script to create the trust you include the -SharePointWeb parameter. That parameter should be the Url to a web app on your on prem farm. If you don't include it then you will get one randomly selected and I actually ran into a problem with that scenario in one of the farms where I was doing this fix up.
    1. Manually remove the SPServiceApplication "ACS"; use this PowerShell script to do it:  $p = Get-SPServiceApplicationProxy | ? DisplayName -EQ "ACS", then Remove-SPServiceApplicationProxy $p
    2. Manually remove the SPServiceApplication "ACS-STS"; the PowerShell script is the same as the previous step, but use -EQ "ACS-STS".
    3. Rerun the script to create the trust but DO NOT include any of the -Remove* parameters
  4. When you run the script to create the trust include the -SharePointWeb parameter. This should be the Url to a web application on your local farm. If you don't include this parameter then you will have a web app randomly selected, and this scenario actually caused another issue in one of the farms where I was applying these fixes.