Introduction
The purpose of this exercise is to achieve web Single Sign
On (web SSO) through the use of a third party authentication agent that issues
a security token to be shared between Sharepoint and its provider-hosted apps.
Therefore, both Sharepoint and its provider-hosted app will delegate user
authentication to a common identity server. Upon authentication, the security
token issued by the identity server is stored as browser cookie and is shared
among various apps including Sharepoint that use the same identity service for
user authentication. These apps sharing the same identity service are
referred to as relying parties.
Prerequisites
1.
On-premise Sharepoint 2013 configured to accept
high trust provider-hosted app with a Sharepoint web application configured for
claim-based authentication (This is the default OOTB authentication mode). More
details here.
2.
A high trust provider-hosted app (low trust with
Azure ACS will not work) configured to work with Sharepoint. More details here.
3.
Latest version of Thinktecture
Identity Server v2 installed on an instance of IIS as an IIS web site. More
details on its setup here.
Some abbreviations and conventions
·
“Identity Provider” and “Identity Server” are
used interchangeably.
·
IP-STS: Token Issuer/Service from Identity
Provider
·
RP-STS: Token Issuer/Service from Relying Party;
namely, Sharepoint.
·
PH-App: Provider-hosted Sharepoint App of high
trust variety.
·
WS-Fed: WS-Federation Passive Protocol.
Overview
A.
Create SSL certificate. This
is to be used by the IP-STS and RP-STS, and will be the “glue” for establishing
trust between these token services.
D.
Make PH-App claimed-aware.
This will allow the app to delegate its user authentication to the identity
server, and share its security token with Sharepoint.
A. Create SSL Certificate
For the purpose of this exercise, since we’re targeting a
development environment, we’re going to create a self-signed certificate. For
production setup, it’s recommended that a commercial SSL certificate be used.
A1. Configure a certificate
management console.
1.
From the machine that hosts the Identity Server,
navigate to the Run… applet by pressing the [WinKey]+R key combination. Type in
mmc to bring up the Windows Management Console. We’re going to create a custom
console to show certificates for later use.
2.
From the Console window, navigate to the top
menu item “Add/Remove Snap-in…”.
3.
From the Add or Remove Snap-ins dialog box, select
the Certificates item from the left pane and click “Add”.
4.
On the Certificate snap-in dialog, select the
Computer account radio button and click “Finish”.
5.
On the Select Computer dialog, click “Finish” to
accept the default values.
6.
You’re back on the Add or Remove Snap-ins dialog
box. Click “OK”.
7.
Expand the Personal certificate store from the
tree view on the left of the Console window as shown.
8.
Save this instance of the Console to desktop
from the File menu -> “Save” as Certificates.msc for easy access at a later time.
Do not close the console window at this time as we’re coming back to modify the
new certificate we’re creating in the next steps.
A2. Create a certificate
IIS 7 and above has built-in facilities to create
self-signed certificates but it’s not flexible enough for us to create a
certificate with a subject (common name) of our choice. So for this, we’ll use
a command line tool detailed below.
1.
Open Visual Studio 2012 Command Prompt from
Start menu -> All programs -> Microsoft Visual Studio 2012 -> Visual
Studio Tools -> Developer Command Prompt for VS2012. Note if you don’t have
Visual Studio installed on the same machine as the Identity Server, you can use
SelfSSL
from IIS
6 Resource Kit to create a self-signed certificate.
2.
From the Visual Studio command prompt, run the
following command to create a certificate. This command will create a
certificate in the personal certificate store we saw earlier on the console
window. It will also generate a .cer file that we will use later on for hooking
up PH-App and RP-STS for Sharepoint. Note that the common name (CN) will be
different on every machine and the CN can either be an IP address or a domain
name.
makecert -r -pe -n CN=10.7.8.59 -ss
my -sr localmachine -sky exchange -sp "Microsoft RSA SChannel
Cryptographic Provider" -sy 12 "c:\stscert.cer"
3.
Switch back to the Management Console window,
refresh the window view and you should see the newly-created certificate under
the personal certificate store. Right-click on the new certificate item on the
right pane and select Properties.
4.
On the Properties dialog box under the General
tab, specify a Friendly name (i.e. STS Cert), and Click “OK” to apply the
change.
A3. Export the certificate
1.
Open IIS Manager by typing in InetMgr from the
Run… applet (pressing [WinKey]+R key combinations).
2.
Select the local server node from the
connections pane on the left side of the screen, and double click the Server
Certificates icon on the main Feature View.
3.
Select the new certificate you created earlier
on the certificates list, right-click, and choose “Export…”.
4.
On the Export certificate dialog that appears,
choose a location for file export, specify a password, and click OK. Note you
will need this exported pfx file for your provider-hosted app configuration
later.
B. Configure Sharepoint for trust
between its RP-STS and IP-STS.
In order to establish trust between RP-STS and IP-STS, a
trusted identity token issuer needs to be created on Sharepoint. This trusted
token issuer will act on-behalf of Sharepoint to establish a trusted
authenticated connection, via the same certificate shared between RP-STS and
IP-STS.
B1. Create a Trusted Identity Token
Issuer
1.
Using Notepad or other text editor, save the
following powershell script in a file named createSTS.ps1 on Desktop.
## Sharepoint incoming WS-Fed endpoint ##
$realm = "http://10.7.8.59/_trust"
## Thinktecture Identity Server sign-in URI ##
$providerUri = "https://10.7.8.59/issue/wsfed"
## Create certificate object. Note the .cer file we saved
earlier. ##
$cert = New-Object
System.Security.Cryptography.X509Certificates.X509Certificate2("C:\stscert.cer")
## Create a new Root Certificate Authority (optional if a root
authority with the same cert exists) ##
New-SPTrustedRootAuthority -Name
"IP STS Auth" -Certificate $cert
## Create claims mappings ##
$map1 =
New-SPClaimTypeMapping -IncomingClaimType
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"
-IncomingClaimTypeDisplayName "EmailAddress" -SameAsIncoming
$map2 =
New-SPClaimTypeMapping -IncomingClaimType
"http://schemas.microsoft.com/ws/2008/06/identity/claims/role"
-IncomingClaimTypeDisplayName "Role" -SameAsIncoming
$map3 = New-SPClaimTypeMapping
-IncomingClaimType
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/webpage"
-IncomingClaimTypeDisplayName "Webpage" -SameAsIncoming
## Putting it all together as a Token Issuer ##
$ap =
New-SPTrustedIdentityTokenIssuer -Name "Identity Provider STS"
-Description "Identity Provider STS" -Realm $realm
-ImportTrustCertificate $cert -SignInUrl $providerUri -IdentifierClaim
$map1.InputClaimType -ClaimsMappings $map1,$map2,$map3
2.
Launch Sharepoint 2013 Management Shell as an
administrator, execute the following commands line by line to run the script
above:
cd Desktop
.\createSTS
3.
Ensure that the Identity Token Issuer was
created properly by running following command, and examine the output which
should look as follows.
Get-SPTrustedIdentityTokenIssuer |
where {$_.Name -eq "Identity Provider STS"}
B2. Configure a trusted identity
provider for a Sharepoint web application
For the purpose of this exercise, we’re going to add the new
STS (Identity Token Issuer) we created earlier to the default Sharepoint web
application we have on our test environment. However, this can just as well be
done on any web application within your Sharepoint farm.
1.
Browse to your Sharepoint Central Admin
Application Management section -> Manage web applications, select the web application
displayed on the list.
2.
Click on the “Authentication Providers” menu bar
item on top, select the default zone on the dialog screen.
3.
Check the “Trusted Identity Provider” checkbox
and the checkbox displayed for the identity provider. In this case it is the
“Identity Provider STS” we created earlier.
4.
Scroll down the dialog screen and click the Save
button to apply the changes. Note this normally will take a while to complete.
If everything goes well, it will take you back to the Authentication Provider
dialog screen we saw earlier.
5.
Close all instances of the browser, then try
logging into a Sharepoint site, you should see a Sign In screen that asks to
choose an authentication provider.
C. Configure the identity server for Sharepoint
and PH-App as relying parties
This part of exercise requires an existing Thinktecture
Identity Server already installed on a computer accessible on your network with
at least one non-administrator user set up. For installation instructions,
please refer to this guide.
C1. Add Sharepoint as a relying party
on the Identity Server
1.
Log in to the identity server with an
administrator account, click on the administration tab, expand the “Relying
Parties & Resources” node from the Configuration area on the left of the
screen, and click on the New button to create a relying party.
2.
On the New Relying Party form that appears, fill
out the fields as shown, and Click the “Create” button.
3.
At this point, Sharepoint is configured as a
relying party on the Identity Server. We now need to assign permission to an
IP-STS user from Sharepoint.
C2. Assign Sharepoint permission to an
IP-STS user
1.
Log into a sharepoint site with a user with
admin privileges. For this exercise, we’ll use the developer site at /sites/dev.
2.
Navigate to Site Settings -> Users and
Permissions section -> Site permissions.
3.
On the Permissions screen, click on the
"Grant Permissions" menu bar item on the top.
4.
On the “Share” dialog screen, type the email of
the IP-STS user in the "Invite people to 'Edit'" box as it appears on
the identity server admin web page. If the trust is established correctly
between the IP-STS and RP-STS, the user entry should appear on the autocomplete
drop-down list. Select the user entry from the drop-down list when it appears.
5.
On the "Share" screen, click on
"SHOW OPTIONS" to select a group in which to assign user permission
to. In this case, we’re going to assign to the user site owner permission.
6.
Click the “Share” button to assign user permission.
7.
To test out the claims-based authentication via
the identity server, log out the current user from Sharepoint, close all
instances of the browser, and log in to the Sharepoint developer site as the
IP-STS user for which the permission was just assigned.
C3. Add PH-App as a relying party on
the Identity Server
1.
The steps involved in adding a relying party
entry for a PH-App on the IP-STS is similar to section C1. Follow the steps
provided on section C1 and complete the fields as shown, and click the “Create”
button.
D. Making a provider-hosted
Sharepoint app claims-aware
For this part of the exercise, we’re going to use an
existing Visual Studio 2012 PH-App project already configured to hook up with
Sharepoint. If you do not have such a project, please follow the instructions
from here
to create one or download a sample from here. You will also need to update your Visual
Studio 2012 to Update 3 and install the Identity
and Access Tool to allow easy configuration.
D1. Configure the PH-App to be
claims-aware
1.
Open the existing PH-App project with Visual
Studio 2012. From the Solution Explorer, right-click on the web project node
(not the Sharepoint App project node) as shown and choose Identity and Access… from
the context menu.
2.
On the Identity and Access dialog that appears,
from the Providers tab, choose the radio button “Use a business identity
provider”, fill out the remaining fields for the federation metadata URI from
the IP-STS, and your app ID URI (realm).
3.
Switch to the Configuration tab, fill in values
for App ID URI and Audience URI as shown. In this case, these values are the
same as the application realm field value on the Providers tab.
4.
Accept the default values for the remaining
fields and click OK. At this point, the tool will make some changes to the
web.config file, which will look similar to the following:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
....
<appSettings>
....
<add key="ClientId" value="a91915ef-96b2-48e5-a033-bbdba8b8a164" />
<add key="ClientSecret" value="+RseWjNOUIFtFPyDZtr6yb5cNJ2ymMNy8xXhlPMSt8I=" />
<add key="ClientSigningCertificatePath"
value="C:\stscert.pfx"
/>
<add key="ClientSigningCertificatePassword" value="password" />
<add key="IssuerId" value="177cdf87-bb07-4360-a431-5fc2cad5d34e" />
<add key="ida:FederationMetadataLocation" value="https://10.7.8.59/FederationMetadata/2007-06/FederationMetadata.xml" />
<add key="ida:Issuer" value="https://10.7.8.59/issue/wsfed" />
<add key="ida:ProviderSelection" value="productionSTS"
/>
</appSettings>
<location path="FederationMetadata">
<system.web>
<authorization>
<allow users="*" />
</authorization>
</system.web>
</location>
....
<system.identityModel>
<identityConfiguration>
<audienceUris>
<add value="http://MyLocalComputer/" />
</audienceUris>
<issuerNameRegistry type="System.IdentityModel.Tokens.ValidatingIssuerNameRegistry,
System.IdentityModel.Tokens.ValidatingIssuerNameRegistry">
<authority name="STS Identity Provider">
<keys>
<add thumbprint="8F385746D36C1F885A41647AD2BCB5D77E075C9A" />
</keys>
<validIssuers>
<add name="STS Identity Provider"
/>
</validIssuers>
</authority>
</issuerNameRegistry>
<!--certificationValidationMode set to "None" by the
the Identity and Access Tool for Visual Studio. For development purposes.-->
<certificateValidation certificateValidationMode="None" />
</identityConfiguration>
</system.identityModel>
<system.identityModel.services>
<federationConfiguration>
<cookieHandler requireSsl="false" />
<wsFederation passiveRedirectEnabled="true" issuer="https://10.7.8.59/issue/wsfed" realm="http://MyLocalComputer/" requireHttps="false" />
</federationConfiguration>
</system.identityModel.services>
</configuration>
5.
Note the highlighted parts of the markup that
correspond to the values filled from the Identity and Access Tool earlier. The ClientSigningCertificatePath
value from the appSettings
section should also be changed to point to the stscert.pfx file we exported
earlier. The path of this pfx file should point to a physical location on the
machine that hosts the app web site.
D2. Obtaining Sharepoint Client
Context from the Claims-Aware PH-App
The OOTB Sharepoint App project template comes with a
TokenHelper class (TokenHelper.cs) . However, it does not provide facilities to
get Sharepoint client context from claims-based identity. For this, we’re going
to use a custom implementation that extends the TokenHelper class. For this
exercise, it is helpful to get a sample project here.
1.
Download the custom TokenHelper implementation
from here,
extract the ClaimsTokenHelper.cs file from the zip package, and add it to the project
root of the web app project as shown.
2.
Wrap the TokenHelper class on the ClaimsTokenHelper.cs
file with the same namespace as that on the TokenHelper.cs file, and add the
static keyword from the TokenHelper declaration to make the class static.
3.
Add the partial keyword to the TokenHelper class
declaration on the TokenHelper.cs file to also make the class partial.
4.
To get the Sharepoint client context, use the
following snippet anywhere on the server side of the application code. In our
sample project, we placed this code in a controller method of the MVC web
application. Note we’re using the TokenHelper.GetS2SClientContextWithClaimsIdentity
method to get an instance of ClientContext object.
public ActionResult Index() {
....
Uri hostWeb = new Uri(Request["SPHostUrl"]);
using (ClientContext clientContext = TokenHelper.GetS2SClientContextWithClaimsIdentity(hostWeb, User, TokenHelper.IdentityClaimType.SMTP, TokenHelper.ClaimProviderType.SAML, true)) {
//Get the current user.
Web web = clientContext.Web;
clientContext.Load(web);
clientContext.ExecuteQuery();
clientContext.Load(web.CurrentUser);
clientContext.ExecuteQuery();
var currentUser = clientContext.Web.CurrentUser.LoginName;
....
}
return View();
}
5.
Add a new TrustedProviderName key from the
appSettings section as shown on the web.config file. Note this is the same name
we created earlier for the Trusted Identity Token Issuer for Sharepoint.
<appSettings>
....
<add key="ida:FederationMetadataLocation" value="https://10.7.8.59/FederationMetadata/2007-06/FederationMetadata.xml" />
<add key="ida:Issuer" value="https://10.7.8.59/issue/wsfed" />
<add key="ida:ProviderSelection" value="productionSTS"
/>
<add key="TrustedProviderName" value="Identity
Provider STS" />
</appSettings>
6.
Inspect the Sharepoint App project from Solution
Explorer. If Features, Packages, and app.config items have been generated,
remove these items as they may prevent the published .app package from being
installed to the desired Sharepoint site. The Features and Packages folders cannot
be removed with Solution Explorer within Visual Studio. To do this, close
Visual Studio, open the project file (.csproj) with a text editor and delete
the xml content as shown.
The items to be removed from Solution Explorer are as shown:
The
highlighted parts are to be deleted from the .csproj file. Note the values from
your file may vary.
....
<ItemGroup>
<Content Include="AppIcon.png">
<OpcRelationship>manifest-icon</OpcRelationship>
</Content>
<Content Include="Elements.xml" />
<Content Include="Features\Feature1\Feature1.Template.xml">
<DependentUpon>Feature1.feature</DependentUpon>
</Content>
<Content Include="Package\Package.Template.xml">
<DependentUpon>Package.package</DependentUpon>
</Content>
</ItemGroup>
....
<ItemGroup>
<None Include="app.config" />
<None Include="Features\Feature1\Feature1.feature">
<FeatureId>{52f56328-9b95-4986-b8e0-25656f64ab71}</FeatureId>
</None>
<None Include="Package\Package.package">
<PackageId>{f4500b1b-70f5-4a84-8d56-d9fdf84fe68a}</PackageId>
</None>
<None Include="SharePointProjectItem.spdata">
<SharePointProjectItemId>{23234997-8888-49fa-9a23-b87299c28a2f}</SharePointProjectItemId>
</None>
</ItemGroup>
....
7.
Once the app project items have been removed
from the project, also delete the corresponding physical files and folders from
Windows Explorer.
8.
Now publish the configured app project, deploy
the app to a target IIS web site, and add the high trust app to a Sharepoint
site, in this exercise we’re using the developer site at /sites/dev. Follow this
guide for more information on how this is done.
D3. Configure the IIS application
pool of the deployed PH-App site
By default, every IIS application pool is created with the
built-in ApplicationPoolIdentity account. If this account is used for the
application pool associated with the PH-App site, a data protection
operation error will be raised with impersonation failure once the user is
authenticated through the IP-STS. To get around this issue, we need to use a
local or a domain user account to replace the ApplicationPoolIdentity account.
1.
Launch the IIS Manager on the computer hosting
the PH-App, expand the server/host node from the Connections treeview, select
the Application Pools node, and with a list of application pools displayed on
the list, select the app pool, and click on the “Advanced Settings…” on the
Actions pane as shown.
2.
On the Advanced Settings dialog box that
appears, under the Process Model section, click on the ellipsis button on the
Identity field.
3.
On the Application Pool Identity dialog that
follows, select the Custom account radio button and click the Set button to
specify a local or domain account. When a user is set, click OK to apply the
change.
4.
With the app pool identity account changed, we
now need to assign read permission to the certificate we created earlier for
the Sharepoint Identity Token Issuer. For this, we turn to the certificate
management console we created earlier. Open the certificates.mmc located on
Desktop.
5.
Expand the Personal node on the left pane, and select
the certificate on the list.
6.
Right-click on the certificate, choose All Tasks
-> Manage Private Keys…
7.
From the Permission for <certificate>
private keys dialog that opens, add a read only permission for the user as
shown:
8.
Click OK to apply the changes.
E. Test the web SSO between the
relying parties
By default, Sharepoint caches user session in persistent
cookies. So when the browser restarts with a Sharepoint site, it automatically logs
in the previously authenticated user. This presents an inconvenience in development
scenarios where user authentication testing needs to be done by closing and
reopening the browser in quick successions. For this, we can disable the
persistent session cookie by running the following powershell command from
Sharepoint server.
$serviceConfig =
Get-SPSecurityTokenServiceConfig
$serviceConfig.UseSessionCookies =
$true
$serviceConfig.Update()
1.
Once the app is added to a Sharepoint site, launch
it, and copy the URL displayed on the browser’s address bar. We’re going to use
this URL to test out the SSO functionalities.
2.
Close all instances of the browser, launch the
PH-App using the URL copied previously. It should direct you to the Thinktecture
log in screen for authentication. Once logged in, it should redirect you back
to the app authenticated with the home page and the user name displayed on the
top-right corner.
3.
With the app window still open, now try
launching a Sharepoint site with the same browser from a different browser tab.
Sharepoint will display the Sign In screen asking to choose an authentication
provider. In this case, we’ll choose the Identity Provider STS option we
created earlier.
4.
If all goes well, Sharepoint should not redirect
you to the Thinktecture identity server for authentication and should allow you
into the site right away with your logged-in user name, previously
authenticated from the PH-App side, displayed on the top right corner of the
screen.
5.
The reverse should also work; that is, if we’re
logged in from Sharepoint first through Thinktecture IP-STS, and then try to
launch the PH-App, the app should not redirect us to the IP-STS for
authentication for the second time. Instead, it should take us to the home page
right away with the authenticated user context loaded.
Note: if you’re getting the following security token error
after launching the PH-App from an authenticated Sharepoint site, you may have
placed Sharepoint and the PH-App under the same domain (i.e. using the same IP address
for both Sharepoint and PH-App with different port numbers assigned for both). By assigning
the PH-App to a different domain than the Sharepoint web application, this
error will simply disappear. For development scenario where Sharepoint and
PH-App are hosted on the same machine, one can simply assign a unique host name
for the PH-App site from IIS site binding. This will allow for Sharepoint and PH-App using the same IP address to coexist as
relying parties sharing the same identity provider.
Server Error in '/' Application.
ID4230: The SecurityToken
was not well formed. Expecting element name 'SecurityContextToken', found 'SP'.
Description: An unhandled exception occurred during the
execution of the current web request. Please review the stack trace for more
information about the error and where it originated in the code.
Exception Details: System.IdentityModel.Tokens.SecurityTokenException: ID4230: The SecurityToken was not well formed. Expecting element name 'SecurityContextToken', found 'SP'.
Source Error:
Exception Details: System.IdentityModel.Tokens.SecurityTokenException: ID4230: The SecurityToken was not well formed. Expecting element name 'SecurityContextToken', found 'SP'.
Source Error:
An
unhandled exception was generated during the execution of the current web
request. Information regarding the origin and location of the exception can
be identified using the exception stack trace below.
|
Stack Trace:
[SecurityTokenException:
ID4230: The SecurityToken was not well formed. Expecting element name
'SecurityContextToken', found 'SP'.]
System.IdentityModel.Tokens.SessionSecurityTokenHandler.ReadToken(XmlReader
reader, SecurityTokenResolver tokenResolver) +1081888
System.IdentityModel.Tokens.SessionSecurityTokenHandler.ReadToken(Byte[]
token, SecurityTokenResolver tokenResolver) +100
System.IdentityModel.Services.SessionAuthenticationModule.ReadSessionTokenFromCookie(Byte[]
sessionCookie) +623
System.IdentityModel.Services.SessionAuthenticationModule.TryReadSessionTokenFromCookie(SessionSecurityToken&
sessionToken) +164
System.IdentityModel.Services.SessionAuthenticationModule.OnAuthenticateRequest(Object
sender, EventArgs eventArgs) +173
System.Web.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
+80
System.Web.HttpApplication.ExecuteStep(IExecutionStep step,
Boolean& completedSynchronously) +165
|
Version Information:
Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.0.30319.18055
F. Online resources used for this
guide