I have developed a Provider Hosted MVC App for SP2013. Everything works in my development environment; however, I am struggling getting it to work in our production (on-premises) environment.
Anything with the [SharePointContextFilter] attribute will not load (i.e. Home/Index aka start page), but About or Contact pages will as they do not have the attribute. The homepage redirects to my 'Error' page; however, other actions give the unexpected error page: "An unexpected error has occurred. Please try again by launching the app installed on your site."
When navigating to the app I see, via Fiddler, the following error: SPAppToken=&SPSiteUrl=http%3A%2F%2Ftest.thrive&SPSiteTitle=Test+Thrive&SPSiteLogoUrl=&SPSiteLanguage=en-US&SPSiteCulture=en-US&SPRedirectMessage=EndpointAuthorityMatches&SPErrorCorrelationId=b237159f-1886-7076-971c-96c26a739eb2&SPErrorInfo=The+Azure+Access+Control+service+is+unavailable.
Which appears to be a red-hearing and seems exactly like this post: SharePoint 2013 Provider-Hosted App "Azure Access Control Service is unavailable"
I have looked at the suggested blog posts by Russ (Part 1 & Part 2).
I have my provider hosted web hosted on a seperate server from the SP ones which is accessible via https://apps.mycompany.on.ca/appname and is using our company wildcard cert issued by GoDaddy.
Because of this on the SharePoint side I have added 4 Trusted Root Authorities to SharePoint as my understanding is the entire certificate chain needs to be a trusted authority:
- Company Wildcard cert
- GoDaddy CA - G2
- GoDaddy Root CA
- GoDaddy Class 2 CA
I have also registered the company wildcard as a trusted token issuer:
$certPath = "C:\wildcard\wc.cer"
$certificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($certPath)
$realm = Get-SPAuthenticationRealm
$specificIssuerId = [guid]::NewGuid() # Make sure to take note of this
$fullIssuerIdentifier = $($specificIssuerId.toString() + '@' + $realm)
New-SPTrustedSecurityTokenIssuer -Name "Company High Trust Apps Cert" -Certificate $certificate -RegisteredIssuerName $fullIssuerIdentifier -IsTrustBroker
I thought this would have allowed things to work properly. It is worth noting that our web app (HNSCs) is not SSL, nor is our add-in domain (however, other apps are working, i.e. ones bought from the store).
I have also seen mention of modifying the TokenHelper.cs file which I have done to the following:
#if DEBUG
private static readonly string ClientSigningCertificatePath = WebConfigurationManager.AppSettings.Get("ClientSigningCertificatePath");
private static readonly string ClientSigningCertificatePassword = WebConfigurationManager.AppSettings.Get("ClientSigningCertificatePassword");
private static readonly X509Certificate2 ClientCertificate = (string.IsNullOrEmpty(ClientSigningCertificatePath) || string.IsNullOrEmpty(ClientSigningCertificatePassword)) ? null : new X509Certificate2(ClientSigningCertificatePath, ClientSigningCertificatePassword);
private static readonly X509SigningCredentials SigningCredentials = (ClientCertificate == null) ? null : new X509SigningCredentials(ClientCertificate, SecurityAlgorithms.RsaSha256Signature, SecurityAlgorithms.Sha256Digest);
#else
private static readonly string ClientSigningCertificateSerialNumber = WebConfigurationManager.AppSettings.Get("ClientSigningCertificateSerialNumber");
private static readonly X509SigningCredentials SigningCredentials = GetSigningCredentials(GetCertificateFromStore());
#endif
#if !DEBUG
private static X509SigningCredentials GetSigningCredentials(X509Certificate2 cert)
{
return (cert == null) ? null
: new X509SigningCredentials(cert,
SecurityAlgorithms.RsaSha256Signature,
SecurityAlgorithms.Sha256Digest);
}
private static X509Certificate2 GetCertificateFromStore()
{
if (string.IsNullOrEmpty(ClientSigningCertificateSerialNumber))
{
return null;
}
// Get the machine's personal store
X509Certificate2 storedCert;
X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
try
{
// Open for read-only access
store.Open(OpenFlags.ReadOnly);
// Find the cert
storedCert = store.Certificates.Find(X509FindType.FindBySerialNumber,
ClientSigningCertificateSerialNumber,
true)
.OfType<X509Certificate2>().SingleOrDefault();
}
finally
{
store.Close();
}
return storedCert;
}
#endif
And in the Web project Web.config I added the following app settings:
<add key="ClientID" value="12035ef7-6e7f-4d9f-9d6b-2f974fef09c4" />
<add key="ClientSigningCertificateSerialNumber" value="<Company Wildcard Serial Number>" />
<add key="IssuerId" value="de9920bd-b2b8-42a8-b441-cb1e7b7f3636" />
In the AppManifest.xml I have tried hardcoding the ClientId into the file... but thought this unecessary when it should be done via the publishing process? Same ClientId as in the Web.config file.
I do not understand what I am missing to get this to all come together into a working production app (everything works in my development environment via Visual Studio).
I have also checked out this Step by Step Installation Guide – SharePoint 2013 On-Premises Provider Hosted High Trust Configuration for guidance.
Any help/suggestions would be greatly appreciated. Thanks.