SharePoint and Windows Live ID

One of the great features of Claims authentication in SharePoint (2010 or 2013) is the ability to use external authentication providers such as ADFS, Microsoft LiveID (Hotmail, Outlook.com etc) or even Google among others. Better yet, using Microsoft Azure ACS makes setting up and managing this for extranet sites or Cloud applications simple!

 

The Catch!

However there’s one catch (isn’t there always?) Microsoft Live ID doesn’t give the email address in the claim. This sounds obscure but in effect what it means is that once you configure all of this this is what you get:

image

What a nice and friendly username!

(If you want instructions on configuring all this, I highly recommend Wictor Wilén’s indispensible series of articles on the topic: http://www.wictorwilen.se/Post/Visual-guide-to-Azure-Access-Controls-Services-authentication-with-SharePoint-2010-part-1.aspx)

 

Solutions?

Most people suggest using something like Google to authenticate instead to avoid this, however with SharePoint Online, Project Online, EPMonDemand  (ahem – excuse the blatant plug!) and Office365 all tied to my LiveID, I personally don’t think that’s an option!

So basically we need to populate that information, sure we could ask users to do it manually, but better yet let’s find out how to do this the Microsoft way.

 

Live Connect API

In order to support Windows 8 apps, Web apps and all manner of mobile devices MS have this great resource available, head over to the Live Connect Developer Center to see more information. Once you’ve registered then you can follow my steps below to get you setup to auto-populate SharePoint user information via Javascript!

 

Getting this going

Note: Getting to the business end here; you’ll Visual Studio, some JavaScript, SharePoint Object Model and WebPart development experience to continue. (Or just skip to the end and download my example, like everyone else!)

Firstly you’ll need to create yourself an ‘application’ on Live Connect, something like the following:

image

The important thing is to note your Client ID, and then enter the correct URL under Redirect domain.

All done, easy!

 

If you want to see how all this works, the Interactive Live SDK is one of the best SDK implementations I’ve seen from MS, have a look at what kind of options are available to you.

For example, the following JavaScript will return the basic user information for the currently logged in user including name, email addresses and more;

 WL.init({
    client_id: “***********”,
    redirect_uri: “callback.aspx”,
    response_type: “token”
 });
 WL.login({ “scope”: “wl.emails” }).then(
    function (response) {
        updateUserData();
    },
    function (response) {}
 );
 function updateUserData() {
    WL.api({ path: “/me”, method: “GET” }).then(
        function (response) {
            // Do something here!
        },
        function (response) {}
    );
 }

Neat hey?

 

Bring it all together

Okay not quite done yet, lets put this into a nice simple SharePoint webpart and then using the good old Object Model update the current users’ details.

What you need to do:

  1. Open Visual Studio and create a new SharePoint – Visual Web Part Project

image

  1. Create some ASP controls for your webpart, I won’t bore you with all the details here, get the full solution below in which I created the following:
    1. AccountTextBox
    2. NameTextBox
    3. EmailTextBox1
    4. UpdateButton
    5. Plus a bunch of labels to make things neat.
  2. We need a link to the Live Connect http://js.live.net/v5.0/wl.jsfile, to make this easier I have saved a copy in a Layouts folder in my solution, so my ScriptLink looks like this:
    <SharePoint:ScriptLink ID="LiveIdscript" runat="server" Name="UpdateUserWebPart/wl.js"></SharePoint:ScriptLink>
  3. Also for the Live ID bit to work we need a callback.aspx file in our solution, which is referenced in the “redirect_uri” parameter passed to to WL.init in the JavaScript, this file should look something like the following;
 <!DOCTYPE html>
 <html xmlns="http://www.w3.org/1999/xhtml">
 <head>
 <title>Authentication Complete</title>
 </head>
 <body>
 <!--
 The script will handle passing oauth/authorize response back to the calling page.
 -->
 <script src="/_layouts/15/UpdateFromLiveID/js/wl.js" type="text/javascript">
 </script>
 <p>This window can be closed.</p>
 </body>
 </html>
  1. Now for the JavaScript, first we need to initialise and try to login to Windows Live.
<SharePoint:ScriptBlock ID="ScriptBlock1" runat="server">
    WL.init({
        client_id: "*************",
        redirect_uri: "<%=SPContext.Current.Web.Url %>/_layouts/15/UpdateUserWebPart/callback.aspx",
        response_type: "token"
    });
    if (document.getElementById("<%=EmailTextBox1.ClientID %>").value == '') {
        WL.login({ "scope": "wl.emails" }).then(
        function (response) {
             updateUserData();
         },
         function (response) {}
         );
    }

Above I’ve included an IF that checks for an existing Email address, if none is found then it automatically tries to login. (Pop-up blockers hate this, so you’ll need to do something nicer)

  1. Next here’s my updateUserData() function which is called on successful login;
function updateUserData() {
    WL.api({ path: "/me", method: "GET" }).then(
        function (response) {
            document.getElementById("<%=EmailTextBox1.ClientID %>").value = response.emails.preferred;

            if (!response.name) {
                document.getElementById("<%=NameTextBox.ClientID %>").value = response.emails.preferred;
            }
            else {
                 document.getElementById("<%=NameTextBox.ClientID %>").value = response.name;
            }

            document.getElementById("<%=UpdateButton.ClientID %>").click();
            },
            function (response) {}
        );
    }
</SharePoint:ScriptBlock>

So once we have the user data we update our ASP controls created previously so we can use those in the code behind.

  1. Lastly we need some code behind to get the details and update our SPUser object.
protected void Page_Load(object sender, EventArgs e)
{
    if (string.IsNullOrEmpty(AccountTextBox.Text))
    {
        AccountTextBox.Text = SPContext.Current.Web.CurrentUser.LoginName;
        NameTextBox.Text = SPContext.Current.Web.CurrentUser.Name;
        EmailTextBox1.Text = SPContext.Current.Web.CurrentUser.Email;
    }
    UpdateButton.Click += UpdateButton_Click;
}
  1. Here I’m updating the controls on Page_Load but only once, then creating an event for our Button.
  2. For the button I basically do the following:
void UpdateButton_Click(object sender, EventArgs e)
{
    [...]
    SPUser currentUser =
        elevatedWeb.SiteUsers.GetByID(SPContext.Current.Web.CurrentUser.ID);

    currentUser.Name = NameTextBox.Text;
    currentUser.Email = EmailTextBox1.Text;

    currentUser.Update();
    [...]
}

To keep this short(er) I have cut out the RunWithElevatedPrivileges bits and such, you actually might not need to have that depending on your user permissions, but if you leave it in then I suggest reading this.

 

Are we done yet?

Now lets deploy and have a look;

image

 

That’s what we see initially, but once we allow that popup:

image

image

 

Make sure you select Yes.

image

 

Nice.

Now if everything’s working, clicking Update should do just that!

image

Hurrah!

 

Get my full example solution here built for Visual Studio 2012 in SharePoint 2012, just make sure you update it with your Client ID from Live Connect.

 

Conclusion

It’s not perfect, in fact it’s a way off (think application pages, modal dialogs, mmm), but hopefully this will get you started.

Any questions, post below, but I strongly recommend you play with the Interactive SDK on Live Connect and read the Getting started documentation there if you have any problems with the JavaScript. It took me quite a bit to get all that working together the first time!

Update: See my latest post on this topic for a better approach: http://nearbaseline.azurewebsites.net/blog/2013/07/sharepoint-hosted-apps-with-saml-authentication/

ADFS Login Failure on one SharePoint site collection

After migrating a Project Server (or any other SharePoint web app) to a new SharePoint 2010 farm using ADFS for federated authentication and multi-tenancy you receive the below error message when trying to access some of the migrated site collections. In my case the root site worked but /PWA failed.

Also if you reprovision a new site collection (include a PWA) in the same web application it also works fine.

 

Error message from ADFS

clip_image001

There was a problem accessing the site. Try to browse to the site again.

If the problem persists, contact the administrator of this site and provide the reference number to identify the problem.

Reference number: [GUID]

 

Event log on ADFS server

Log Name: AD FS 2.0/Admin
Source: AD FS 2.0
Date: 5/11/2012 10:00:43 PM
Event ID: 364
Task Category: None
Level: Error
Keywords: AD FS

Description:

Encountered error during federation passive request.
Additional Data

Exception details:
Microsoft.IdentityServer.Web.InvalidRequestException: MSIS7042: The same client browser session has made ‘6’ requests in the last ‘5’ seconds. Contact your administrator for details.

at Microsoft.IdentityServer.Web.FederationPassiveAuthentication. UpdateLoopDetectionCookie()

at Microsoft.IdentityServer.Web.FederationPassiveAuthentication. SendSignInResponse(MSISSignInResponse response)

 

Cause

What’s happening is that while authentication works as expected on some sites, when you open a particular site collection the authentication goes into a loop, eventually failing when the ADFS server detects this redirect loop.

After a fair amount of digging the problem turned out to be in the multi-tenancy configuration of this particular farm. Specifically the site collections for some reason did not all have a subscription.

Using the PowerShell command ‘Get-SPSite https://site/sitecollection1’ against two site collections to compare the settings turned up the key difference:

image

 

This makes sense as to why the authentication is failing; the different site subscription overrides the FedAuth authentication cookie assigned by ADFS as soon as it is assigned, causing the page to redirect back to re-authenticate.

 

Resolution

Fortunately once the above was found the fix is simple, run the following command in PowerShell:

Get-SPSite | Set-SPSite 
–SiteSubscription [Guide of site subscription]

 

Now after a quick IISRESET all should be working!