That’s all very well and good, but it’s really only half of the story. The second and equally as important part of if SharePoint 2010’s new authentication capabilities is the ability to delegate an individual users’ identity to systems outside of SharePoint.
Identity delegation is the process of passing an individual’s credentials to services and applications hosted on different computers. Typically, this is accomplished using Kerberos, an authentication protocol designed to handle this sort of thing. However, there are a couple of important issues associated with it. For starters, it’s notoriously difficult to configure and debug. And while there is a lot of documentation on the subject, most of it is incomplete, inaccurate, or just poorly written. Setup involves dependencies like SPNs, which require the involvement of the Active Directory team. And lastly, Kerberos isn’t extensible. You can’t supplement user identities with roles and arbitrary claims. And I can say from my own experience that Kerberos authentication tends to be an afterthought. Something to be avoided. Last on the list of options.
Enter Windows Identity Foundation (WIF), the platform on which SharePoint 2010 claims authentication is based. WIF, which is fully supported in SharePoint 2010, ADFS 2.0, ASP.NET, Windows Communication Foundation (WCF), and any other .NET application you care to develop, provides the infrastructure necessary to generate, transmit, and process claims-based identity in a simple and straightforward manner. It removes the roadblocks imposed by legacy authentication schemes like NTLM and Kerberos and puts control directly into the hands of developers, users, and IT security professionals. In short, it’s a framework written to help solve identity issues common in the of cloud computing and service-oriented architecture.
As a demonstration of these capabilities, I’ll show how SharePoint 2010, WCF,and WIF can be put together to solve the identity delegation problem. In this blog, we’ll start with a web service that is front-ending line-of-business information stored in a SQL database. Then, we’ll configure it to use WIF to request the calling user’s claims from SharePoint and process the token so that authorization decisions can be made. In Part 2 of this series, we’ll surface this information in SharePoint 2010 as an External Content Type using Business Connectivity Services (BCS).
The Setup: Part 1 and Part 2 of this walkthrough is implemented on a single computer running Windows 7. This computer has IIS, SQL 2008, and Visual Studio 2010 installed. In IIS, I have a dedicated web site created with the host header of “pbdev.com” and a self-signed SSL certificate is bound to it over port 443. I have the backconnectionhostnames registry entry configured to ensure there are no issues with local authentication. In addition, I have the most recent version of the Windows Identity Foundation SDK installed. This adds some functionality to Visual Studio 2010 for creating (among other things) claims-aware ASP.NET sites and web services.
The Database: You’ll need to download and install the standard AdventureWorks database, not the SQL 2008 version.
The Web Service: I’ll be working with a modified version of the web service included in the Business Connectivity Services samples section of the SharePoint 2010 SDK. This web service is actually an XML web service (.asmx), which won’t allow us to add the bindings we need for this demo. I converted it to a standard WCF web service.
You can download the completed web service it here.
After downloading and extracting the .zip file, add the web service as an application under a site in IIS. Remember, this site should be enabled for SSL.
Open web.config and make sure the connection string is correct for your environment.
- <addname=“AdventureWorksConnectionString“connectionString=“Data Source=localhost;Initial Catalog=AdventureWorks;Integrated Security=True“providerName=“System.Data.SqlClient“ />
Next, you’ll need to update your endpoint address (line 130) and DNS value (line132) to match your environment. The following example is configured for “pbdev.com”.
- <dnsvalue=“pbdev.com“ />
You should now be able to browse to the service: http://[your_host_header]/WCFService/service.svc and see the wsdl.
The web service I provide is pretty much complete and ready to go. It just requires some additional configuration. Some background information on how I got to this point and the remaining configuration steps are covered in the remainder of this post.
Add Windows Identity Foundation
I initially created this web service using the standard WCF Service template in Visual Studio. After integrating Microsoft’s Adventureworks code, I added WIF functionality using the Federation Utility (FedUtil). This utility is installed as part of the Windows Identity Foundation SDK. The process is initiated by right-clicking the site within the Solution Explorer and selecting “Add STS Reference…”
This will launch the federation wizard. I accepted the defaults and selected “No STS” at the end.
This accomplished a couple of things:
- Added a section name for “microsoft.identityModel” within <configSections>
- Registered the Microsoft.IdentittyModel assembly in the <assemblies> section
- Defined a behavior extension named “federatedServiceHostConfiguration”
- Added the behavior defined in the previous step to <serviceBehaviors>
- Created and populated the <microsoft.identityModel> section that was defined in step1 (above)
After completing this step, I was ready to start customizing the web service the to require tokens.
I updated the endpoint address to use SSL and changed the binding from wsHttpBinding to ws2007FederationHttpBinding. I also added a bindingConfiguration setting to reference the name of the binding configuration section found later in web.config. I also changed the MEX endpoint from mexHttpBinding to mexHttpsBinding.
IMPORTANT: When you look to set up this web service in your environment, be sure to update the host header values for your endpoint addresses and DNS values to match your environment.
- <!– Service Endpoints –>
- <dnsvalue=“pbdev.com“ />
- <endpointaddress=“mex“binding=“mexHttpsBinding“contract=“IMetadataExchange“ />
The IT Leader's Guide to Multicloud Readiness
This guide provides practical key insights and important factors to consider to make informed decisions in your multicloud journey.
Download the Guide
Next, I linked the custom behavior to reference the service name defined within the <microsoft.identitymodel> section found at the bottom of web.config. This is seen on Line 4 in the following code section.
- <!– To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment –>
- <serviceMetadatahttpGetEnabled=“true“ />
- <!– To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information –>
- <serviceDebugincludeExceptionDetailInFaults=“false“ />
Add the ws2007FederationHttpBinding Section
This step involved adding a lot of new settings into web.config. Specifically the the ws2007FederationHttpBinding. This is the section that specifies the claim requirements for our web service and pretty much drives the delegated authentication process. You can drop in this entire section after the <extensions> section.
- <issuerMetadataaddress=“https://fabrikam/_vti_bin/sts/spsecuritytokenservice.svc?wsdl“ />
- <issueraddress=“https://fabrikam/_vti_bin/sts/spsecuritytokenservice.svc/actas“ />
- <!–Add or uncomment claims that you require by your application and then update the federation metadata of this application.–>
- <!–<add claimType=”http://schemas.microsoft.com/ws/2008/06/identity/claims/role” isOptional=”true” /> –>
- <!–<add claimType=”http://schemas.xmlsoap.org/ws/2008/06/identity/claims/role” isOptional=”true” />–>
- <!– <add claimType=”http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress” isOptional=”true” />–>
- <!–<add claimType=”http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname” isOptional=”true” />–>
- <addclaimType=“http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn“isOptional=“true“ />
- <!–<add claimType=”http://schemas.xmlsoap.org/claims/CommonName” isOptional=”true” />–>
- <!–<add claimType=”http://schemas.xmlsoap.org/claims/EmailAddress” isOptional=”true” />–>
- <!–<add claimType=”http://schemas.xmlsoap.org/claims/Group” isOptional=”true” />–>
- <!–<add claimType=”http://schemas.xmlsoap.org/claims/UPN” isOptional=”true” />–>
- <!–<add claimType=”http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname” isOptional=”true” />–>
- <!–<add claimType=”http://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifier” isOptional=”true” />–>
- <!–<add claimType=”http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier” isOptional=”true” />–>
- <!–<add claimType=”http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationinstant” isOptional=”true” />–>
- <!–<add claimType=”http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod” isOptional=”true” />–>
- <!–<add claimType=”http://schemas.xmlsoap.org/ws/2005/05/identity/claims/denyonlysid” isOptional=”true” />–>
- <!–<add claimType=”http://schemas.microsoft.com/ws/2008/06/identity/claims/denyonlyprimarysid” isOptional=”true” />–>
- <!–<add claimType=”http://schemas.microsoft.com/ws/2008/06/identity/claims/denyonlyprimarygroupsid” isOptional=”true” />–>
- <!–<add claimType=”http://schemas.microsoft.com/ws/2008/06/identity/claims/groupsid” isOptional=”true” />–>
- <addclaimType=“http://schemas.microsoft.com/ws/2008/06/identity/claims/primarygroupsid“isOptional=“true“ />
- <addclaimType=“http://schemas.microsoft.com/ws/2008/06/identity/claims/primarysid“isOptional=“true“ />
- <!–<add claimType=”http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname” isOptional=”true” />–>
- <addclaimType=“http://schemas.microsoft.com/sharepoint/2009/08/claims/userlogonname“isOptional=“true“ />
- <addclaimType=“http://schemas.microsoft.com/sharepoint/2009/08/claims/userid“isOptional=“true“ />
- <addclaimType=“http://schemas.microsoft.com/sharepoint/2009/08/claims/identityprovider“isOptional=“true“ />
- <addclaimType=“http://sharepoint.microsoft.com/claims/2009/08/isauthenticated“isOptional=“true“ />
- <addclaimType=“http://schemas.microsoft.com/sharepoint/2009/08/claims/farmid“isOptional=“true“ />
- <addclaimType=“http://sharepoint.microsoft.com/claims/2009/08/tokenreference“isOptional=“true“ />
- <addclaimType=“http://schemas.microsoft.com/sharepoint/2009/08/claims/SidCompressed“isOptional=“true“ />
IMPORTANT: Be sure to note the host names on lines 6 and 7, which reference the SharePoint 2010 STS. Line 7 is particularly important since it specifies the requirement for an “ActAs” token. This how the identity delegation process is initiated. Also note we are requesting a SAML 1.1 token on line 5. You will need to update these host names to match your local SharePoint implementation.
Configure the Windows Identity Foundation Section
The last thing I did was configure the <microsoft.identityModel> section at the bottom of the web.config. This involved five basic steps:
- Adding a custom claimsAuthorizationManager. This class can be used to determined if the user should have access to the data or not. It’s optional but I decided to put it into this example.
- Adding values to the <audienceUris> section. This specifies the label for acceptable incoming SAML tokens. For SharePoint 2010, we set this to: “urn:schemas-microsoft-com:sharepoint:service”
- Adding certificates as trusted issuers. SAML 1.1 tokens are digitally signed so we need to tell our web service what signatures are valid. This can be done by adding certificate thumbprints within <trustedIssuers> inside the <issuerNameRegistry> section.
- Specifying what kind of client certificate validation is necessary. For this demonstration, I’m setting it to “none”.
- Adding a token handler. WIF can do all the SAML 1.1 token decryption and inspection for you. That’s a Good Thing and we need to enable that.
The end result for the <microsoft.identityModel> section looks like this:
- <addvalue = “urn:schemas-microsoft-com:sharepoint:service“/>
- <issuerNameRegistrytype=“Microsoft.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry, Microsoft.IdentityModel, Version=22.214.171.124, Culture=neutral, PublicKeyToken=31bf3856ad364e35“>
- <addthumbprint=“e9e4f8121e2503d2e0ea7b32b91af3ef92767e6e“name=“SharePoint Security Token Service Encryption“ />
- <addthumbprint=“b05832ec28fb137b33cfbfeee8793c41b1a93dc8“name=“SharePoint Security Token Service“/>
- <certificateValidationcertificateValidationMode=“None“ />
- <removetype=“Microsoft.IdentityModel.Tokens.Saml11.Saml11SecurityTokenHandler, Microsoft.IdentityModel, Version=126.96.36.199, Culture=neutral, PublicKeyToken=31bf3856ad364e35“/>
- <addtype=“Microsoft.IdentityModel.Tokens.Saml11.Saml11SecurityTokenHandler, Microsoft.IdentityModel, Version=188.8.131.52, Culture=neutral, PublicKeyToken=31bf3856ad364e35“/>
IMPORTANT: You will need to get actual certificate thumbprints from your SharePoint 2010 installation. One of the ways to get this information is to use the Certificates snap-in on the machine running SharePoint. Be sure to use these values within <trustedIssuers> and *not* the ones from this post.
Now when you look at the WSDL, you should see a policy that requires an ActAs token from SharePoint 2010 to connect to the web service.
This may have seemed like a lot of work, but once you go through it once or twice I hope you’ll find it’s not too bad. At this point, we’re ready to integrate this web service (and the data behind it) to SharePoint 2010.
One Last Step…. Configure SharePoint 2010 to Trust Your SSL Certificate
Recall that I mentioned the WCF service is installed in a web site that has a self-signed SSL certificate bound to it. This is necessary because we’re going to need to secure the communication between SharePoint and the web service. One of the nuances of SharePoint 2010 is that it’s built-in Security Token Service (STS) does *not* use the operating system’s certificate trust repository to define what certificate root authorities are to be trusted. Therefore, we’ll need to manually add our certificate to SharePoint’s trusted root authority list via PowerShell. Here is the command to do that.
$cert = Get-PfxCertificate “C:SSL_CERT_FILENAME.cer”
New-SPTrustedRootAuthority “Adventureworks WCF SSL Cert” -Certificate $cert
Now we should have a properly configured web service that is ready to accept SAML 1.1 tokens that represent end-users who are attempting to consume it’s services. We can now move on to configure an example of this using Business Connectivity Services in part 2 of this series.