Introduction
The other week I posted an article about how to use SharePoint Hosted Apps when using SAML Claims, I did not expect that amount of feedback I had on that blog post, in e-mail, comments, tweets etc. Some of that feedback was how do you do it with Provider Hosted apps. Well you’re about to find out. It took me a while to get it properly done and there are some things that you should be aware of. In this post I will walk you through the simplest scenario and you will notice that there are a couple of moving parts. But, since I am such an influencer I thought I should make it easier for you. I will show you how to do this without the minimal changes to your current provider hosted apps – you only have to add an extension file to your solution, make a small modification to the helper files that Visual Studio gives you and a couple of web.config modifications! All the code you need will be published in a Github repository (https://github.com/wictorwilen/SharePointContextSaml) for you to consume and do all the fancy gitty stuff that you code dweebs out there like.
Let’s get started, bare with me this is a long post and for some of you the first parts might be just a repeat of what you’ve seen/done previously…
Creating a Provider Hosted High Trust App
The first thing we need is to create us a High Trust Provider Hosted App. That is an app that does not run in SharePoint and is using the Server to Server (S2S) protocol for authorization. In my case (and it should be yours as well if you want to use my helper code) is to use Visual Studio 2013 and create a SharePoint App, choose a provider hosted app using the ASP.NET MVC template. To create a High Trust App we then need to specify a certificate for the S2S trust. This is how you can create a cert using makecert.exe and some PowerShell. In the script below all you need to do is modify the four properties; domain, name and password of cert and the location where we will store the certificate files:
#ALIASES
set-alias makecert "C:\Program Files\Microsoft Office Servers\15.0\Tools\makecert.exe"
# CREATE SELF SIGNED DEV CERTS
$domain = "localhost"
$certName = "HightTrustSAMLDemo"
$password = "Pass@word1"
$dir = "c:\HighTrustCerts\"
New-Item $dir -ItemType Directory -Force -Confirm:$false | Out-Null
$publicPath = $dir + $certName + ".cer"
$privatePath = $dir + $certName + ".pfx"
makecert -r -pe -n "CN=$domain" -b 01/01/2012 -e 01/01/2022 -eku 1.3.6.1.5.5.7.3.1 -ss my -sr localMachine -sky exchange -sy 12 -sp "Microsoft RSA SChannel Cryptographic Provider" $publicPath
$publicCert = Get-PfxCertificate -FilePath $publicPath
$publicThumbprint = $publicCert.Thumbprint
Get-ChildItem Cert:\LocalMachine\My |
Where-Object {$_.Thumbprint -eq $publicThumbprint} |
ForEach-Object {
$_.Export("PFX",$password) |Set-Content $privatePath -Encoding Byte
}
Once we have the certificate we need to install the certificate in the SharePoint certificate store as well as register a Trusted Security Token Issuer. And to do this we need a Guid! You can use any preferred tool to generate your Guid or generate and register your very own unique Guid at www.vanityguid.com. Here’s a script the does the trick; give it your very own unique Guid/Issuer Id and a unique display name:
$issuerId = '17721805-67c2-46ba-92d1-fd1fdf91bb01' # MUST BE UNIQUE
$displayName = "High Trust SAML Demo" # MUST BE UNIQUE
Add-PSSnapin Microsoft.SharePoint.PowerShell -EA 0
$certificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($publicCert)
New-SPTrustedRootAuthority -Name $certName -Certificate $certificate
$realm = Get-SPAuthenticationRealm
$fullIssuerId = $issuerId + "@" + $realm
New-SPTrustedSecurityTokenIssuer -Name $displayName -Certificate $certificate -RegisteredIssuerName $fullIssuerId –IsTrustBroker
iisreset
Ok, this was standard High Trust App procedure. Copy and paste the issuer Id into the Visual Studio wizard and enter the certificate details to complete the app creation. What you have right now is a standard High Trust app ready to work with Windows (claims) users. Before you start hacking in some code into this app – just make sure that it properly deploys and executes (ie press F5), so that you haven’t messed up any Guids or anything else. This app should now run using IISExpress and it uses Windows Authentication (remove http(s)://localhost from Local Intranet sites in Internet Explorer if you want to test different users).
What about them SAML users?
So let’s see what happens if we log into our SharePoint Developer site, where we deployed our High Trust app, as a SAML user (using for instance AD FS 3.0). Of course you need to setup a trusted identity token issuer before doing that, how that’s done is out of scope of this post. When logged in as a SAML user and I click on the High Trust App I either get logged into the app as the currently logged on Windows user or I get the Windows login dialog (if http(s)://localhost was removed from Local Intranet sites). So, it doesn’t really work! We don’t even get the chance of authentication with our AD FS (or similar federation service).
Configuring the app for federated Authentication
What we need to do is modify our web application (the provider hosted web) to use federated authentication. Unfortunately we are SharePoint developers, and are not given all the love that ordinary ASP.NET developers are from the Visual Studio team. When creating a standard ASP.NET project we get the really awesome option of specifying which authentication method we would like to use and we can point out our federation service and Visual Studio fixes everything for us.
We can do this in two different ways. Either we add a new ASP.NET MVC project and remove the default web project and convert that ASP.NET MVC project into a SharePoint app project (which is the easiest way) or we start fiddling with web.config and configure it ourselves – which is exactly what we’re going to do here!
The first thing we need to do is to use NuGet and add the Microsoft ASP.NET Identity Core package and the Microsoft Token Validation Extension for .NET 4.5. These package will give us all the assemblies and references we need to run the app.
In your web application project open up web.config, now we’re going to do some copy-paste work! First of all we need to add a configSections element for the System.IdentityModel. Insert the snippet below directly after the configuration element:
<configSections>
<section name="system.identityModel"
type="System.IdentityModel.Configuration.SystemIdentityModelSection, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
<section name="system.identityModel.services"
type="System.IdentityModel.Services.Configuration.SystemIdentityModelServicesSection, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
</configSections>
Then head on over to the system.web element and below the authorization element add the following.
<authentication mode="None" />
This means that we’re not letting ASP.NET do the Authentication, but instead handling it over to a new module which we’re going to add. Directly after the system.web element copy and paste the following, which will add two new modules to the ASP.NET pipeline:
<system.webServer>
<modules>
<add name="WSFederationAuthenticationModule" type="System.IdentityModel.Services.WSFederationAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" preCondition="managedHandler" />
<add name="SessionAuthenticationModule" type="System.IdentityModel.Services.SessionAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" preCondition="managedHandler" />
</modules>
</system.webServer>
These two modules are called WSFAM and SAM (two more acronyms for you JB). WSFAM is responsible for the login/logout process and SAM takes care of session management by using cookies.
Then we need to add the configuration for our Relying Party. This is done by adding the system.identityModel element after the system.serviceModel element. It’s a handful of configuration and we need to modify it in a few places to match our app and our federation service.
<system.identityModel>
<identityConfiguration>
<audienceUris>
<add value="uri:SharePoint:App:SAML" />
</audienceUris>
<securityTokenHandlers>
<add type="System.IdentityModel.Services.Tokens.MachineKeySessionSecurityTokenHandler, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<remove type="System.IdentityModel.Tokens.SessionSecurityTokenHandler, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
</securityTokenHandlers>
<certificateValidation certificateValidationMode="None" />
<issuerNameRegistry type="System.IdentityModel.Tokens.ValidatingIssuerNameRegistry, System.IdentityModel.Tokens.ValidatingIssuerNameRegistry">
<authority name="fs.contoso.com">
<keys>
<add thumbprint="EF66CEF27067637D296C403691DBBE2A8B5C7BAA" />
</keys>
<validIssuers>
<add name="http://fs.contoso.com/adfs/services/trust" />
</validIssuers>
</authority>
</issuerNameRegistry>
</identityConfiguration>
</system.identityModel>
First of all on line #4 – that row must contain the relying party identifier for our app registered in AD FS. I prefer using a URI, since that makes it easier when I need to deploy this into production, but you could use the URL of your web application (https://localhost:44305/ for instance). I’ll show you in just a minute how to configure this in AD FS. Line 12 should contain the name of our authority and line 17 must have the Federation Service Identifier of AD FS (found under Federation Service Properties in the AD FS Console). On line 14 we need to specify the thumbprint of the AD FS Token-signing certificate (you’ll find that easy in the AD FS Console under Service/Certificates).
Ok, only one more thing to fiddle with in the web.config.
<system.identityModel.services>
<federationConfiguration>
<cookieHandler requireSsl="true" />
<wsFederation passiveRedirectEnabled="true"
issuer="https://fs.contoso.com/adfs/ls/"
realm="uri:SharePoint:App:SAML"
requireHttps="true" />
</federationConfiguration>
</system.identityModel.services>
Here there are two things we need to modify. First of all we need to point to the SAM/WS-Federation endpoint of AD FS, the login page, that Url is set in the issuer property of the wsFederation element. Secondly we must add the realm of our app – which should be the same as the audienceUri specified above. As I said earlier, I use a URI instead of the web application Url.
A quick note here as well, notice that I use HTTPS and SSL all the way. For my SharePoint web application and for my app web application. If you prefer to be sloppy you can still use HTTP but that requires some extra work in SharePoint and the app web application config.
Let’s take our High Trust App for a ride! Press F5 to get the app up and running. Doesn’t work…well some of it do. You should now be taken to the AD FS login page, but that one will just tell you an error occurred and the details doesn’t give much of a hint. You have to take a look in the AD FS server and the event log for AD FS. The error message is pretty clear on what we haven’t done yet:
MSIS7007: The requested relying party trust 'uri:SharePoint:App:SAML' is unspecified or unsupported.
If a relying party trust was specified, it is possible that you do not have permission to
access the trust relying party.
Contact your administrator for details.
Yes, we haven’t created our Relying Party Trust yet.
Creating the Relying Party Trust in AD FS
Now we need to create our Relying Part Trust in AD FS, it’s pretty straightforward, but comes with a couple of caveats. Add a new Relying Party Trust and choose to enter data manually, then give the trust a display name (I prefer to use the realm here). Continue by choosing to use the AD FS Profile, skip the certificate page in the wizard, and then select to enable support for the WS-Federation Passive protocol and enter the URL of your App. On the next page add a new identifier and now use the realm of your app, then just keep on clicking Next until you’re done. Once you’re done with creating the Relying Party Trust a new dialog will open up in AD FS in which you need to create Claims Rules for this new trust. This is a very important step, you need to get the rules correct and include the correct claims. In this case I will add two claims. One which augments the E-Mail claim from Active Directory (LDAP) for my Active Directory users and one which will just pass through the E-Mail claim from my third party Identity Provider (Thinktecture Identity Server). You have to pass either E-Mail, SIP address or UPN to get this to work. SharePoint needs at least one of those claims to be able to rehydrate the user from the User Profile Service Application.
Once that is done, go back to your App and start it again using F5. Now logon using one of the Active Directory, use some other account than the one you are currently logged on to your machine as (otherwise you’ll think you are home safe already). You should see the default ASP.NET MVC SharePoint App page which says “Welcome Someone”, where Someone is the name of the logged in user. But what! That’s not the name of the account you logged in using? You can easy check that you are actually properly logged in using AD FS by setting a break point in the HomeController.cs in the Index action and take a look at some watches. You can clearly see that we are authenticated through federation and you’ll see the e-mail claim for that user who authenticated through AD FS, but the identity shown is the wrong one!
Well, it turns out that the SharePointContext.cs and TokenHelper.cs classes that the Visual Studio and Office Dev team so kindly wrote for us only works with Windows Claims for High Trust apps. Bummer!
Steve Peschka to the rescue!
I barely gave up when I hit this, but my Bing-fu found a single blog post by Steve Peschka (who knew!) called “Using SharePoint Apps with SAML and FBA Sites in SharePoint 2013” who solved my problems, almost. This post of Steve was written some time ago when the TokenHelper.cs class looked very different and it forced you to make modifications to your app to support federated users. So I took his example code and modified it to fit the new helper classes provided by Visual Studio 2013 and made them in such way that you do not have to re-write your apps and packaged it into a single .cs file; SharePointContextSaml.cs. I will NOT post the entire thing here, since it is a lot of code. But if you’re like me you should read through it, it gives you tons of knowledge on how the app authorization actually works beneath the hood.
So you just download the .cs extension file and add to your app web application project and then you have to do a couple of tweaks to your app configuration and to the default TokenHelper.cs and SharePointContext.cs files. This is how you do it.
-
After you copied the SharePointContextSaml.cs file into your project, modify the namespace of that file to match the namespace of your project.
-
Modify the default TokenHelper.cs class declaration so that the TokenHelper class is a partial class declaration, we need this to be able to extend the TokenHelper class. (This declaration should have been there by default imho!)
public static partial class TokenHelper {...}
- In the SharePointContext.cs file locate the static constructor for the SharePointContextProvider class and modify it so it looks like below. This is needed so that we can use our custom context provider if the user is authenticated through federation.
static SharePointContextProvider()
{
if (!TokenHelper.IsHighTrustApp())
{
SharePointContextProvider.current = new SharePointAcsContextProvider();
}
else
{
if(HttpContext.Current.User.Identity.AuthenticationType == "Federation") {
SharePointContextProvider.current = new SharePointHighTrustSamlContextProvider();
} else {
SharePointContextProvider.current = new SharePointHighTrustContextProvider();
}
}
}
- Finally we need to add three entries to the appSettings in web.config:
<add key="spsaml:ClaimProviderType" value="SAML"/>
<add key="spsaml:TrustedProviderName" value="High Trust SAML Demo"/>
<add key="spsaml:IdentityClaimType" value="SMTP"/>
The first entry is used to tell our custom context provider that we use SAML claims (there is support for Forms based authentication, value = FBA, in the extensions that Steve originally wrote but I have not yet tested it at all in my edition, and personally I prefer using something like IdentityServer instead of FBA). The second entry must be the name of the Trusted Security Token Issuer that you created using PowerShell for your app. And finally we need to specify what claim we will extract and use as identity claim; possible values are SMTP (e-mail), SIP or UPN.
Let’s try to run the app once again. Most likely you will be able to logon but then you get an exception in the HomeController, an access denied. Then you need to check two things. First make sure that the user that you’re logging in with have permissions on your site. Since we’re talking about SAML claims here the people picker behaves a bit differently, enter the e-mail address of the user and make sure to choose the e-mail claim name (hover the mouse over the suggested options to see the claim type). Secondly you must make sure that the user has the WorkEmail attribute in the user profile store populated. This can be done either by using the User Profile Synchronization, if you’re authenticating with an Active Directory or LDAP source. But if you’re using some other IdP, for instance IdentityServer that I use here, you must add the WorkEmail manually through PowerShell or some other method. Once that is done you should see a nice and warm welcome to your SAML claims user!
That’s it! All you need to do to enable SAML Claims user for your SharePoint 2013 High Trust App.
Show me the codez!
I’ve created a Github repository to host the SharePointContextSaml.cs file, you can find it here: https://github.com/wictorwilen/SharePointContextSaml
I’d really like to get feedback in form of features, bugs, fixes etc. Please use the issue feature on Github, instead of e-mailing me…
Currently it is not documented at all, there are some comments; a mix of mine and Steves original ones.
I’d like to maintain it as long as possible, but my hope is that the Office Dev Team picks this up and do some proper testing and then merges this with the default Visual Studio helper files. Well, one can at least wish!
Summary
Yet another long blog post. I really hope I didn’t miss anything, there’s quite a few step once you do this the first time. I’ve shown you the complete example here; how to create and register a high trust SharePoint 2013 app, how to convert the default MVC project to a Claims based web application, registering a Trusted Relying Party in AD FS and then finally adding the new SharePointContextSaml.cs file and enabling you to use federated users in your Provider hosted app. Good luck to you all and thanks Steve for doing all the hard work!