Udostępnij za pośrednictwem


Finally A USEFUL Way to Federate With Windows Live and SharePoint 2010 Using OAuth and SAML

Lots of folks have talked to me in the past about federating SharePoint with Windows Live. On the surface it seems like a pretty good idea – Windows Live has millions of users, everyone logs in with their email address, which is something we use a lot as an identity claim, it’s a big scalable service, and we have various instructions out there for how to do it – either directly or via ACS (Access Control Service). So why might I be so grumpy about using it with SharePoint? Well, for those of you that have tried it before you know – when you federate with Windows Live you never get a user’s email address back as a claim. All you get is a special Windows Live identifier that is called a PUID. As far as I know, “PUID” should stand for “Practically GUID”, because that’s pretty much what it looks like and about how useful it is. 

For example, if you DO federate with Windows Live, how do you add someone to a site? You have to get their PUID, and then add the PUID to a SharePoint group or permission level. Do you seriously know anyone that knows what their PUID is (if you are such a person, it’s time to find something else to do with your free time). Even if you did magically happen to know what your PUID is, how useful do you think that is if you’re trying to grant users rights to different site collections? Do you really think anyone else could pick you out of a PUID lineup (or people picker, as the case may be)? Of course not! And thus my frustration with it grows.

I actually thought that we might have a shot here at a more utopian solution with ACS. ACS is really great in terms of providing out of the box hooks to several identity providers like Windows Live, Google, Yahoo, and Facebook. With Facebook they even sprinkle a little magic on it and actually use OAuth to authenticate and then return a set of SAML claims. Very cool! So why don’t they do that with Windows Live as well? Windows Live supports OAuth now so it seems like there’s an opportunity for something valuable to finally happen. Well despite wishing it were so, the ACS folks have not come to the rescue here. And therein lies the point of this preamble – I finally decided to just write one myself, and that is the point of this posting.

So why do we care about OAuth? Well, contrary to the PUID you get when federating directly with Windows Live, OAuth support in Windows Live allows you to get a LOT more information about the user, including – wait for it – their email address. So the plan of attack here is basically this:

  1. Write a custom Identity Provider using the Windows Identity Foundation (WIF).
  2. When a person is redirected to our STS, if they haven’t authenticated yet we redirect them again to Windows Live. You have to create “an application” with Windows Live in order to do this, but I’ll explain more about that later.
  3. Once they are authenticated they get redirected back to the custom STS. When they come back, the query string includes a login token; that login token can be exchanged for an access token.
  4. The STS then makes another request to Windows Live with the login code and asks for an access token.
  5. When it gets the access token back, it makes a final request to Windows Live with the access token and asks for some basic information about the user (I’ll explain what we get back later).
  6. Once we have the user information back from Windows Live, we use our custom STS to create a set of SAML claims for the user and populate it with the user info. Then we redirect back to whatever application asked us to authenticate to begin with to let it do what it wants with the SAML tokens. In this particular case I tested my STS with both a standard ASP.NET application as well as a SharePoint 2010 web app.

So…all the source code is attached to this posting, but there’s still some configuration to do, and you will have to recompile the application with the app ID and secret that you get from Windows Live, but other than doing that copy and paste there really isn’t any code you need to write to get going. Now lets walk through everything you need to use it.

Create a Token Signing Certificate

You will need to create a certificate that will use to sign your SAML tokens. There’s nothing special about the certificate you use to sign certificates, other than you need to make sure you have the private key for it. In my case I have Certificate Services installed in my domain so I just opened the IIS Manager and selected the option to create a Domain Certificate. I followed the wizard and before you know it I had a new certificate complete with private key. For this project, I created a certificate called livevbtoys. 

As I’ll explain in the next section, when requests initially come into the STS the user is an anonymous user. In order to use that certificate to sign SAML tokens then we need to grant the IIS process access to the private key for that certificate. When an anonymous request comes in the IIS process identity is Network Service. To give it rights to the key you need to:

  1. Start the MMC
  2. Add the Certificates snap-in. Select the Computer store for the local computer.
  3. Open up the Personal…Certificates store and find the certificate you created for signing SAML tokens. If you created as I explained above the certificate will be in there by default. If you create it some other way you may need to add it to that store.
  4. Right click on the certificate and choose the option to Manage Private Keys.
  5. In the list of users that have rights to the keys, add Network Service and give it Read rights to it.

Note that if you don’t do this correctly, when you try running the application you may get an error that says something like “keyset does not exist”. That just means that IIS process did not have sufficient rights to the private key, so it could not use it to sign the SAML token.

Install the Application and Required Assemblies

Installing the application in this sense really just means creating an ASP.NET application in IIS, copying the bits, and making sure the latest version of WIF is installed. Once you get it configured and working on one server of course, you would want to add one or more additional servers to make sure you have a fault tolerant solution. But I’ll just walk through the configuration needed on the single server.

I won’t go into how you create an ASP.NET application in IIS. You can do with Visual Studio, in the IIS Manager, etc. 

NOTE: If you use the code that’s provided here and just open the project in Visual Studio, it will complain about the host or site not existing. That’s because it’s using the name from my server. The easiest way to fix this is just to manually edit the WindowsLiveOauthSts.sln file and change the https values in there to ones that actually exist in your environment.

Once it’s actually created there are a few things you want to make sure you do.

  1. Add PassiveSTS.aspx as the default document in the IIS Manager for the STS web site.
  2. Change the Authentication settings for the application in IIS so that all authentication types are disabled except for Anonymous Authentication.
  3. The STS needs to run over SSL, so you will need to acquire an appropriate certificate for that and make sure you update the bindings on the IIS virtual server where the custom STS application is used.
  4. Make sure you put the thumbprint of your token signing certificate in the thumbprint attribute of the add element in the trustedIssuers section of the web.config of your relying party (if you are NOT using SharePoint to test). If you use the Add STS Reference wizard in Visual Studio it will do this for you.

That should be all of the configuration needed in IIS.

Update and Build the Custom STS Project

The attached zip file includes a Visual Studio 2010 project called WindowsLiveOauthSts. Once IIS is configured and you’ve updated the WindowsLiveOauthSts.sln file as describe above, you should be able to open the project successfully in Visual Studio. One of the first things you’ll need to do is to update the CLIENT_ID and CLIENT_SECRET constants in the PassiveSTS.aspx.cs class. You get these when you create a new Windows Live application. While I’m not going to cover that step-by-step (because there are folks at Windows Live who can help you with it), let me just point you to the location where you can go to create your Windows Live app:  https://manage.dev.live.com/Applications/Index?wa=wsignin1.0. Also, when you create your application, make sure you set the Redirect Domain to the location where your custom STS is hosted, i.e. https://myserver.foo.com.

Now that you have your ID and secret here’s what needs to be updated in the application:

  1. Update the CLIENT_ID and CLIENT_SECRET constants in the PassiveSTS.aspx.cs class.
  2. In the web.config file update the SigningCertificateName in the appSettings section. Note that you don’t have to change the IssuerName setting but you obviously can if you want.
  3. Update the token signing certificate for the FederationMetadata.xml document in the STS project. Once you've selected the certificate you're going to use, you can use the test.exe application included in this posting to get the string value for the certificate. It needs to be copied in to replace the two X509Certificate element values in federationmetadata.xml.

There’s one other thing worth pointing out here – in the CustomSecurityTokenService.cs file you have the option of setting a variable called enableAppliesToValidation to true and then providing a list of Urls that can use this custom STS. In my case I have chosen not to restrict it in any way, so that variable is false. If you do want to lock down your custom STS then you should change that now. Once all of these changes have been made you can recompile the application and it’s ready to go.

One other note here – I also included a sample ASP.NET application that I used for testing while I was building this. It’s in a project called LiveRP. I’m not really going to cover it in here; suffice to say it’s there if you want to try testing things out. Just remember to change the thumbprint for the STS token signing certificate as described above.

SharePoint Configuration

At this point everything is configured and should be working for the custom STS. The only thing left to do really is to create a new SPTrustedIdentityToken issuer in SharePoint and configure a new or existing web application to use it. There are a few things you should know about configuring the SPTrustedIdentityTokenIssuer though; I’m going to give you the PowerShell that I used to create mine and then explain it:

$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2("c:\livevbtoys.cer")

New-SPTrustedRootAuthority -Name "SPS Live Token Signing Certificate" -Certificate $cert

 

$map = New-SPClaimTypeMapping -IncomingClaimType "https://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress" -IncomingClaimTypeDisplayName "EmailAddress" -SameAsIncoming

$map2 = New-SPClaimTypeMapping -IncomingClaimType "https://blogs.technet.com/b/speschka/claims/id" -IncomingClaimTypeDisplayName "WindowsLiveID" -SameAsIncoming

$map3 = New-SPClaimTypeMapping -IncomingClaimType "https://blogs.technet.com/b/speschka/claims/full_name" -IncomingClaimTypeDisplayName "FullName" -SameAsIncoming

$map4 = New-SPClaimTypeMapping -IncomingClaimType "https://blogs.technet.com/b/speschka/claims/first_name" -IncomingClaimTypeDisplayName "FirstName" -SameAsIncoming

$map5 = New-SPClaimTypeMapping -IncomingClaimType "https://blogs.technet.com/b/speschka/claims/last_name" -IncomingClaimTypeDisplayName "LastName" -SameAsIncoming

$map6 = New-SPClaimTypeMapping -IncomingClaimType "https://blogs.technet.com/b/speschka/claims/link" -IncomingClaimTypeDisplayName "Link" -SameAsIncoming

$map7 = New-SPClaimTypeMapping -IncomingClaimType "https://blogs.technet.com/b/speschka/claims/gender" -IncomingClaimTypeDisplayName "Gender" -SameAsIncoming

$map8 = New-SPClaimTypeMapping -IncomingClaimType "https://blogs.technet.com/b/speschka/claims/locale" -IncomingClaimTypeDisplayName "Locale" -SameAsIncoming

$map9 = New-SPClaimTypeMapping -IncomingClaimType "https://blogs.technet.com/b/speschka/claims/updated_time" -IncomingClaimTypeDisplayName "WindowsLiveLastUpdatedTime" -SameAsIncoming

$map10 = New-SPClaimTypeMapping -IncomingClaimType "https://blogs.technet.com/b/speschka/claims/account" -IncomingClaimTypeDisplayName "AccountName" -SameAsIncoming

$map11 = New-SPClaimTypeMapping -IncomingClaimType "https://blogs.technet.com/b/speschka/claims/accesstoken" -IncomingClaimTypeDisplayName "WindowsLiveAccessToken" -SameAsIncoming

$realm = "https://spslive.vbtoys.com/_trust/"

$ap = New-SPTrustedIdentityTokenIssuer -Name "SpsLive" -Description "Window Live oAuth Identity Provider for SAML" -realm $realm -ImportTrustCertificate $cert -ClaimsMappings $map,$map2,$map3,$map4,$map5,$map6,$map7,$map8,$map9,$map10,$map11 -SignInUrl "https://spr200.vbtoys.com/WindowsLiveOauthSts/PassiveSTS.aspx" -IdentifierClaim "https://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"

Here are the things worth noting:

  1. As I stated above, I created a certificate called livevbtoys.cer to sign my tokens with, so I added that to my SPTrustedRootAuthority list and then associate it with my token issuer.
  2. I created claims mappings for all of the claims that my custom STS is returning. As you can see, it’s SIGNIFICANTLY MORE AND BETTER than you would ever get if you just federated directly to Windows Live. One other thing to note here – I include the access token that I got from Windows Live as a claim here. While that works with Facebook, I haven’t tested it so I can’t say for sure if Windows Live will let you reuse it or not. But maybe that will be the topic of a future post.
  3. The $realm value is critically important. It must point to the root site of your web application, and include the /_trust/ directory. If you do this wrong, you will just get 500 errors from SharePoint when you get redirected back after authentication.
  4. The –SignInUrl parameter when creating the token issuer is the absolute Url to PassiveSTS.aspx page for my custom STS.

That’s pretty much it – once it’s set up you are still using the out of the box people picker and claims providers so you won’t have any lookup capabilities, as you would expect. You grant rights to people with the email addresses that they use to sign into Windows Live. You could actually extend this example and also use the Azure claims provider I blogged about here:  https://blogs.technet.com/b/speschka/archive/2012/02/11/the-azure-custom-claim-provider-for-sharepoint-project-part-1.aspx. That means you would be using this STS to enable you to authenticate with Windows Live and get some real SAML claims back, and then using the Azure custom claims provider project to add those authenticated users into your Azure directory store and the people picker to choose them.

The pictures tell it all, so here’s what it looks like when you first hit the SharePoint site and authenticate with Windows Live:

When you first sign in it will ask you if it’s okay to share your information with the custom STS application. There’s nothing to concerned with here – that’s standard OAuth permissions happening. Here’s what that looks like; note that it shows the data I’m asking for in the STS – you could ask for an entirely different set of data if you wanted. You just need to look at the Window Live OAuth SDK to figure out what you need to change and how:

Once you accept, you get redirected back to the SharePoint site. In this example I am using the SharePoint Claims web part I blogged about here:  https://blogs.technet.com/b/speschka/archive/2010/02/13/figuring-out-what-claims-you-have-in-sharepoint-2010.aspx. You can see all the claims I got from Windows Live via OAuth that I now have as SAML claims thanks to my custom STS, as well as the fact that I’m signed in with my Windows Live email address that I created for this project (from the sign in control, top right corner):

 

WindowsLiveOauthSts.zip

Comments

  • Anonymous
    January 01, 2003
    No problem @Martin, I post to share. :-)

  • Anonymous
    January 01, 2003
    thanks

  • Anonymous
    January 01, 2003
    The comment has been removed

  • Anonymous
    January 01, 2003
    The comment has been removed

  • Anonymous
    April 02, 2012
    Steve, This is very timely and useful. People have asked me about the usefulness about the ACS based solution. I too didn't have a very good answer to the PUID issue. S

  • Anonymous
    June 08, 2012
    As an alternative, you can also take a look at SocialConnekt for SharePoint (socialconnekt.codeplex.com), with which you can not only integrate Windows LiveID as an authentication mechanism (as Steve does, not as ACS), but also Yahoo and Facebook. The code source for the OAuth Windows LiveID provider can also be leveraged to develop other social authentication provider such as LinkedIn, Twitter, etc and plug them into the SocialConnekt Identity Server.

  • Anonymous
    August 26, 2012
    The comment has been removed

  • Anonymous
    July 23, 2013
    Hi Steve, Great blog and brilliant solution to a very annoying problem! I hope you don't mind that I heavily referenced your solution in my own blog "SharePoint hosted Apps with SAML authentication" (nearbaseline.com.au/blog) leveraging your solution to solve that particular problem in SharePoint 2013. Let me know if you have a problem with me redistributing sections (only) of your code. Martin

  • Anonymous
    August 18, 2014
    The comment has been removed

  • Anonymous
    September 16, 2014
    Hi,

    I get an error after I want to login with Live ID....
    I redirect to the right site - but there is this error:

    Compiler Error Message: CS0246: The type or namespace name 'CustomSecurityTokenService' could not be found (are you missing a using directive or an assembly reference?)

    May someone can help me?

  • Anonymous
    September 18, 2014
    The comment has been removed

  • Anonymous
    September 25, 2014
    Anmy idea?
    The action '' (Request.QueryString['wa']) is unexpected. Expected actions are: 'wsignin1.0' or 'wsignout1.0'.