Technology Toolbox

Your technology Sherpa for the Microsoft platform

Jeremy Jameson - Founder and Principal

Search

Search

Claims Login Web Part for SharePoint Server 2010

Note
This post originally appeared on my MSDN blog:

Since I no longer work for Microsoft, I have copied it here in case that blog ever goes away.

In my previous post, I provided the step-by-step process I use for configuring claims-based authentication in SharePoint Server 2010.

If you follow those steps, then when you browse to the home page of the site and click Sign In, you'll be redirected to the out-of-the-box login page (/_login/default.aspx) shown in Figure 1. Note that since the Web application is configured to support both Forms-Based Authentication (FBA) and Windows authentication, the login page requires you to specify the type of credentials to use to logon to the SharePoint site.

Default login page in SharePoint Server 2010 (/_login/default.aspx)
Figure 1: Default login page in SharePoint Server 2010 (/_login/default.aspx)

If you click Forms Authentication in the dropdown list, you are redirected to the default login form (/_forms/default.aspx), shown in Figure 2.

Default form for FBA in SharePoint Server 2010 (/_forms/default.aspx)
Figure 2: Default form for FBA in SharePoint Server 2010 (/_forms/default.aspx)

Note that if you click Windows Authentication in the dropdown list, you are presented with the login dialog provided by the Web browser (or, if you are logged into a workstation with domain credentials, and you are using Internet Explorer -- and you've added the site to your Local intranet zone -- then instead you are transparently authenticated with the site).

While the sign-in experience shown in the preceeding screenshots is great for prototypes and proofs-of-concept (for example, to demonstrate why claims-based authentication in SharePoint 2010 is much better than FBA in Microsoft Office SharePoint Server 2007), it obviously isn't going to be sufficient for real world solutions. Instead we need a "branded" login page like the one shown in Figure 3.

Custom login page in SharePoint Server 2010
Figure 3: Custom login page in SharePoint Server 2010

Note that I didn't expend significant effort trying to get the custom login page to look great. Rather I wanted to focus on the core functionality. There's definitely signficant room for improvement in the look-and-feel area (for example, using a custom page layout to get rid of the left navigation (i.e. Press Releases) that we probably wouldn't want to show on a real site. [Also note that I didn't even bother to replace the default "Adventure Works" in the masthead with "Fabrikam".]

However, the user experience is already substantially improved by eliminating the dropdown list of available authentication types. Instead, we assume that most users of the site will be customers or partners that login using FBA. Fabrikam employees simply need to click the Fabrikam Employee Sign In link in order to authenticate with their Windows (i.e. domain) credentials.

Now, if all we wanted was to replace the out-of-the-box login pages with the custom page shown in Figure 3, there are plenty of references out there already that show you how to do this. [Just search the Internet for something like SharePoint claims custom login page and you'll find lots of examples that essentially demonstrate the same concept.]

However, rather than replacing the OOTB sign-in experience with a custom application page, I wanted instead to create a custom Claims Login Form Web Part. Why, you ask? Well, imagine that we need to change the Terms and Conditions shown in the sample above or we want to add some other content to the page.

If the custom login page is implemented as an application page, then it will most likely require a developer to modify the content on the page and provide an updated build (although this content could theoretically be stored somewhere else and dynamically inserted into the page, but then you're simply writing a lot of unnecessary custom code).

By using a Web Part instead of an application page, we can leverage all the great Web Content Management features in SharePoint in order to create the custom sign-in page and maintain it going forward (including using approval workflows to ensure changes are reviewed before customers see them on the site).

Consequently, I set out upon creating what I call the "Claims Login Form Web Part" (it's a mouthful, I know).

I started out with a very basic implementation:

using ...

namespace Fabrikam.Demo.Web.UI.WebControls
{
    [ToolboxItemAttribute(false)]
    public class ClaimsLoginFormWebPart : WebPart
    {
        private Login loginForm;

        protected override void CreateChildControls()
        {
            base.CreateChildControls();

            this.loginForm = new Login();

            this.loginForm.Authenticate += new AuthenticateEventHandler(
                LoginForm_Authenticate);

            this.loginForm.LoggedIn += new System.EventHandler(
                LoginForm_LoggedIn);

            this.Controls.Add(loginForm);
        }

        private void LoginForm_Authenticate(
            object sender,
            AuthenticateEventArgs e)
        {
            if (e == null)
            {
                throw new ArgumentNullException("e");
            }

            e.Authenticated = SPClaimsUtility.AuthenticateFormsUser(
                new Uri(SPContext.Current.Web.Url),
                this.loginForm.UserName,
                this.loginForm.Password);
        }

        private void LoginForm_LoggedIn(
            object sender,
            EventArgs e)
        {
            if (e == null)
            {
                throw new ArgumentNullException("e");
            }

            string membershipProviderName = "FabrikamSqlMembershipProvider";
            string roleProviderName = "FabrikamSqlRoleProvider";

            SecurityToken token =
                SPSecurityContext.SecurityTokenForFormsAuthentication(
                    new Uri(SPContext.Current.Web.Url),
                    membershipProviderName,
                    roleProviderName,
                    this.loginForm.UserName,
                    this.loginForm.Password);

            SPFederationAuthenticationModule.Current.SetPrincipalAndWriteSessionToken(
                    token);

            RedirectToSuccessUrl();
        }

        /// <summary>
        /// The following method was snarfed from
        /// Microsoft.SharePoint.IdentityModel.Pages.IdentityModelSignInPageBase.
        /// </summary>
        private void RedirectToSuccessUrl()
        {
            string uriString = null;
            if (
                this.Context == null
                || this.Context.Request == null
                || this.Context.Request.QueryString == null)
            {
                uriString = null;
            }
            else if (
                string.IsNullOrEmpty(
                    this.Context.Request.QueryString["loginasanotheruser"]) == false
                && string.Equals(
                    this.Context.Request.QueryString["loginasanotheruser"],
                    "true",
                    StringComparison.OrdinalIgnoreCase)
                && string.IsNullOrEmpty(
                    this.Context.Request.QueryString["Source"]) == false)
            {
                uriString = this.Context.Request.QueryString["Source"];
            }
            else if (string.IsNullOrEmpty(
                this.Context.Request.QueryString["ReturnUrl"]) == false)
            {
                uriString = this.Context.Request.QueryString["ReturnUrl"];
            }

            if (uriString == null)
            {
                uriString = "/";
            }

            Debug.WriteLine(
                string.Format(
                    CultureInfo.CurrentCulture,
                    "Successfully authenticated, redirecting to {0}.",
                    uriString));

            SPRedirectFlags trusted = SPRedirectFlags.Default;
            if (((SPControl.GetContextWeb(this.Context) == null)
                && Uri.IsWellFormedUriString(uriString, UriKind.Absolute))
                && (SPWebApplication.Lookup(new Uri(uriString)) != null))
            {
                trusted = SPRedirectFlags.Trusted;
            }

            SPUtility.Redirect(uriString, trusted, this.Context);
        }
    }
}

Note that, thanks to the OOTB ASP.NET Login control, very little custom code is required for the Claims Login Form Web Part. In fact, I think there are more lines of code to redirect the request after successful login, than there are to actually perform the claims authentication.

The implementation above actually works as expected -- well, up to a point.

The problem is that when you click the out-of-the-box Sign Out link anytime after authenticating via the custom Web Part, a rather nasty unhandled exception occurs:

[ArgumentException: Exception of type 'System.ArgumentException' was thrown.
Parameter name: encodedValue]
Microsoft.SharePoint.Administration.Claims.SPClaimEncodingManager.DecodeClaimFromFormsSuffix(String encodedValue) +25829214
Microsoft.SharePoint.Administration.Claims.SPClaimProviderManager.GetProviderUserKey(String encodedSuffix) +73
Microsoft.SharePoint.ApplicationRuntime.SPHeaderManager.AddIsapiHeaders(HttpContext context, String encodedUrl, NameValueCollection headers) +845
Microsoft.SharePoint.ApplicationRuntime.SPRequestModule.PreRequestExecuteAppHandler(Object oSender, EventArgs ea) +352
System.Web.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +80
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +171

Obviously this exception doesn't occur when authenticating using the OOTB login pages. I also verified that it doesn't occur when using a custom application page for FBA and claims. This really had me stumped for a little while since my Web Part was calling the exact same code as the custom application page in order to perform the claims authentication.

Using Fiddler, I observed the following cookie when authenticating with the OOTB login page:

  • Cookies / Login
    • Cookie
      • FedAuth
        • 77u/PD94...

I noticed a similar cookie when authenticating with the custom application page (e.g. /_layouts/Fabrikam/SignIn.aspx).

However, when I logged in using the Claims Login Form Web Part, I found that there were two cookies:

  • Cookies / Login
    • Cookie
      • .ASPXAUTH=1D0DDE35...
      • FedAuth
        • 77u/PD94...

When I then clicked Sign Out, I noticed that while the "FedAuth" cookie was removed from the subsequent request, the ".ASPXAUTH" cookie was not. In other words, the presence of an ".ASPXAUTH" cookie (without a "FedAuth" cookie) causes SharePoint Server 2010 to "blow chunks."

To remedy the issue, I moved the code in the LoginForm_LoggedIn event handler into the LoginForm_Authenticate event handler. Since the user is redirected upon successful login, this prevents the ASP.NET Login control from generating the ".ASPXAUTH" cookie. With this change, the OOTB Sign Out link started working as expected.

I then moved on to making the Claims Login Form Web Part more robust by adding the following features:

  • Adding a Web Part property to allow the "Remember me" checkbox to be hidden
  • Replacing the hard-coded membership and role provider names
  • Rendering an "Employee Sign In" link to support Windows authentication (as shown in Figure 3)
  • Hiding the login form when the user is already authenticated (in case somebody decides, for example, to drop an instance of the Claims Login Form Web Part on the home page of the site)
  • Disable the form fields and validator controls when the page is in edit mode (so that content authors don't have to enter "bogus" credentials in order to save changes to the page)
  • Inheriting from my custom SslRequiredWebPart to automatically redirect from HTTP to HTTPS (except in development environments, of course)
  • Show a warning when the connection is not secure (i.e. in development environments or when the SSL redirect has been explicitly disabled via the Web.config file)
  • Better error messages when a user is locked out or when the account is not approved (in order to avoid confusing and frustrating users)
  • Detecting the scenario where the user clicks the "Sign In" link on the custom sign-in page (instead of immediately entering credentials) and redirect to "/" (instead of "/_layouts/Authenticate.aspx?...") in order to avoid an "Access Denied" error

Sample SharePoint solution

I've attached a complete Visual Studio 2010 solution in case you are interested in seeing the Claims Login Form Web Part in action.

Here are the instructions to deploy the Fabrikam Demo sample to your own SharePoint environment. First, download the attachment and unzip the files. Then you simply need to create the ASP.NET membership database and run a few PowerShell scripts, as described below.

To deploy the Fabrikam Demo for claims authentication to SharePoint:

  1. Create three service accounts for the Fabrikam Demo Site:
    • {DOMAIN}\svc-web-fabrikam - used as the application pool identity for the new Fabrikam Web site
    • {DOMAIN}\svc-sp-psr - object cache user account providing Full Read access to Web applications (http://technet.microsoft.com/en-us/library/ff758656.aspx)
    • {DOMAIN}\svc-sp-psu - object cache user account providing Full Control access to Web applications
  2. Complete the procedures detailed in Step 1 from my previous post to create and configure the FabrikamDemo database (i.e. the ASP.NET membership/role database).
  3. Complete the procedures detailed in Step 5 from my previous post to add the Web.config modifications for claims-based authentication to the following sites:
    • SharePoint Central Administration v4
    • Security Token Service
    Note
    You do not need to manually add the Web.config modifications for the "Fabrikam" Web application because this is done automatically upon activation of a feature (Fabrikam Demo - Web Application Configuration).
  4. On the Start menu, click All Programs, click Microsoft SharePoint 2010 Products, right-click SharePoint 2010 Management Shell, and then click Run as administrator. If prompted by User Account Control to allow the program to make changes to the computer, click Yes.
  5. From the Windows PowerShell command prompt, change to the directory containing the deployment scripts (Demo\Dev\SharePointClaimsAuthentication\Source\DeploymentFiles\Scripts), and run the following scripts:
    & '.\Create Web Application.ps1'
    & '.\Create Site Collections.ps1'
    & '.\Enable Anonymous Access.ps1'
    & '.\Configure Object Cache User Accounts.ps1'
    & '.\Add Event Log Sources.ps1'
    & '.\Add Solutions.ps1'
    & '.\Deploy Solutions.ps1'
    & '.\Activate Features.ps1'

At this point you should be able to browse to the Fabrikam site and click the Sign In link to view the Claims Login Form Web Part. In order to actually login, you'll need to first add a user to the FabrikamDemo database (refer to Step 6 in my previous post for instructions on how to do this).

Update (2011-02-26)
There was a bug in the original version of the code sample when logging in as an FBA user over HTTPS (while attempting to determine the membership and role providers from the IIS settings). This has been fixed in the updated attachment.

Attachment

SharePointClaimsAuthentication.zip (949.04 KB)

Comments

  1. # re: Claims Login Web Part for SharePoint Server 2010

    March 1, 2011 5:07 AM
    alaska65

    Ever tried to create a blog post with Windows Live Writer (WLW) in a FBA environment?

    Still got a problem with the authentication of WLW in case of FBA.

    My posts in WLW discussions never gave back a solution. :-(

  2. # re: Claims Login Web Part for SharePoint Server 2010

    March 1, 2011 7:41 AM
    Jeremy Jameson
    Gravatar

    @alaska65,

    Sorry, but I don't use Windows Live Writer. If it doesn't work for a blog hosted on a claims-based Web application in SharePoint 2010, then I recommend you open a support incident with Microsoft. If it is confirmed to be a bug in the product, then you won't be charged for the support case.

    When researching claims-based authentication in SharePoint 2010 for my current project, I did discover that there are some things that don't work, but I don't recall Windows Live Writer being one of them. That's not to say it isn't an issue, it simply means that I didn't come across it in my previous research.

  3. # re: Claims Login Web Part for SharePoint Server 2010

    March 8, 2011 6:23 PM
    ellen

    Hi Jeremy, I was keen to try out your solution, however hte zip file appears to be corrupted.  I couldnt open with 7-zip or with winzip. Could you check and upload new one if possible?  thanks so much.

  4. # re: Claims Login Web Part for SharePoint Server 2010

    March 9, 2011 8:20 AM
    Jeremy Jameson
    Gravatar

    Ellen, I've uploaded a rebuilt version of the zip file and verified that I'm able to extract it successfully after downloading from the blog post.

    Thanks for pointing out the issue.

  5. # re: Claims Login Web Part for SharePoint Server 2010

    March 9, 2011 3:50 PM
    ellen

    Hi again,  Just tried it and works fine now, thanks for that, and thanks for creating the demo, its very well put together, always makes more sense when its a practical example like this rather than doco.

  6. # re: Claims Login Web Part for SharePoint Server 2010

    March 17, 2011 9:43 PM
    Amr Attia

    Hi Jeremy, can't say except great post and cool analysis for the cookie issue when signing out.

    Really thanks.

  7. # re: Claims Login Web Part for SharePoint Server 2010

    March 17, 2011 9:46 PM
    Amr Attia

    The checking for the SSL security using the page object throws an error when trying to add the webpart to a SharePoint page, checking for that flag using the HTTP context fixes this issue.

  8. # re: Claims Login Web Part for SharePoint Server 2010

    March 31, 2011 7:40 PM
    Jason Robertson

    Ignored this one until now hoping someone would post on it - Thanks

  9. # re: Claims Login Web Part for SharePoint Server 2010

    August 15, 2011 2:52 AM
    Scott Clifford

    You lifesaver, was having the cookie issue you described!

  10. # re: Claims Login Web Part for SharePoint Server 2010

    January 6, 2012 2:25 PM
    Tina Cross
    Wow Jeremy - this is priceless - and really appreciate your work.

    Tried to build your solution... and it came up with the following on the file ClaimsLoginFormWebPart\ClaimsLoginFormWebPart.cs.

    Warning as Error: 'Microsoft.SharePoint.SPSecurityContext.SecurityTokenForFormsAuthentication(System.Uri, string, string, string, string)' is obsolete"

    It is asking me to provide an extra parameter (isPersisted). Should I set this to 0 or 1:

    SecurityTokenForFormsAuthentication(Uri context, String membershipProviderName, String roleProviderName, String username, String password, bool isPersisted) instead.'
  11. # re: Claims Login Web Part for SharePoint Server 2010

    January 9, 2012 7:32 AM
    Jeremy Jameson
    Gravatar
    Tina,

    It looks like the SharePoint team decided to introduce a new parameter in Service Pack 2 and mark the original method obsolete. (The sample project builds just fine on my pre-SP2 machine).

    Unfortunately, the SharePoint team hasn't yet updated the corresponding documentation on MSDN (msdn.microsoft.com/...) -- which is incredibly lame.

    Consequently, I can't tell you with 100% certainty what this new parameter is used for. I could venture a guess -- or better yet, crack open the SP2 assembly in Reflector or ILSpy to see what this new parameter does -- but you can do that just as easily as I could, so I'll leave that as an exercise for the reader :-)

    You could also choose to open a support case with Microsoft and tell them to do a better job updating their documentation when they make potentially breaking changes like this.

    Note that if you change the project option to treat "All" warnings as errors (to "None" instead), then you should no longer get a build error (rather just a warning to use the new overload for the method).
  12. # re: Claims Login Web Part for SharePoint Server 2010

    February 28, 2012 9:23 AM
    Jimmy Jimenez
    Gravatar
    Hello Jeremy,

    Can this work in SharePoint online (office 365)?

    Thanks.
  13. # re: Claims Login Web Part for SharePoint Server 2010

    February 28, 2012 9:47 AM
    Jeremy Jameson
    Gravatar
    Whether or not you can use this for SharePoint online depends on which version you are using. Back when I worked at Microsoft, we used to refer to them as BPOS-D (dedicated) or BPOS-S (shared).

    In the "dedicated" solution, SharePoint is hosted for you by Microsoft (or a partner) and you are allowed to use essentially any custom code you want in your solution. (You may have to "coerce" Microsoft a little bit -- depending on what you are trying to do -- but I've never known Microsoft to walk away from a deal (assuming sufficient "revenue upside" on their end, of course).

    However, in the "shared" solution (which is where the Office 365 stuff falls) you are extremely limited in what customizations you can use in your solution. Obviously the key to successfully (and profitably) running a shared environment is keeping things as simple -- and stable -- as possible.

    I believe in "shared" solutions you can only use "sandbox" solutions -- which this obviously doesn't comply with.

    You should definitely reach out to Microsoft to see if they (or you) can provide this kind of functionality in the "shared" option, but my guess is the answer will be "no."
  14. # re: Claims Login Web Part for SharePoint Server 2010

    June 11, 2012 5:18 PM
    Ofer Gal
    Gravatar
    Do you think I can take parts of your code to create a classic authentication custom login on a site that supports annonymous?
  15. # re: Claims Login Web Part for SharePoint Server 2010

    May 19, 2015 2:49 PM
    alejandro garcia
    Gravatar
    we have exatly the same problem... and this workaround saveds my day...

    very thanks....

Add Comment

Optional, but recommended (especially if you have a Gravatar). Note that your email address will not appear with your comment.
If URL is specified, it will be included as a link with your name.

To prevent spam from being submitted, please select the following fruit: Apple

Apple
Cherries
Pear
Watermelon
Strawberry
Grapes
 
Please add 5 and 8 and type the answer here: