Writing A Custom Forms Login Page for SharePoint 2010 Part 1

In SharePoint 2007 writing a custom login page for a forms based authentication (FBA) site was not too terribly hard.  There were a few things to know, most of which weren’t SharePoint specific, and some tips to have your login form take on the look and feel of a standard SharePoint layouts page.  Overall though, if you knew ASP.NET and the FormsAuthentication class you were good to go.  As luck would have it, things get somewhat more complicated in SharePoint 2010. 

In this post we’ll walk through one scenario of a custom login page.  In this example we decide that we need an entirely custom login page – we’re not just changing the look and feel, we require an entirely different UI.  For example, maybe we need to grab the Membership credentials that are used to log in, and then maybe we need someone to enter a secondary authentication ID, like you might have with SecurID.  In that case we’re going to have a couple of text boxes on an ASP.NET page for username and password and we’ll need to take those and programmatically log our user in.

The most important point to remember here is that your old friend, the FormsAuthentication class, will no longer be used.  The reason for that is because in SharePoint 2010, FBA users are actually claims users.  So while you may think you’re working with a standard ASP.NET Membership user and Role provider, under the covers those objects have a shiny claims authentication shell.  Because of that, we need to use some of the SharePoint claims classes to work through the FBA login process.

I need to add one caveat here!  Normally through one way or another, before I post something on my blog I either know or validate as best I can with someone that yeah, this is the right / blessed / supported way of doing something.  In this case, I tried repeatedly to get this approach vetted but was unable to do so.  I have used this code for a project I was working on and it does work, but I don’t want someone going into cardiac arrest if the code police tell you at a later date that you need to modify it to meet some other better / more appropriate way of doing things.  So enough with the CYA, let’s just look at some code.

Before we get started there’s a couple of references we’re going to need that you probably haven’t used before.  The first one is Microsoft.SharePoint.Security.dll, and it’s in the 14 hive in the ISAPI folder.  The other one is trickier, and primarily why I gave my caveat above.  You need a reference to Microsoft.SharePoint.IdentityModel.dll.  However, if you go to add references you won’t readily find this assembly, and thus my source of being slightly embarrassed and wary at the same time.  As I described in another post, the best thing I found to do is find it on the file system, copy it to an easy to find location, and add your reference to the copied version.  The way I usually do that, ‘cause I’m old school I guess, is I go to a command prompt, change to the root of the drive and do a “dir Microsoft.SharePoint.IdentityModel.dll /s” and find it that way.   Once you have that, you’ll probably want to add a little gaggle of using statements:

using System.Web.Security;

using System.IdentityModel.Tokens;

using Microsoft.SharePoint;

using Microsoft.SharePoint.IdentityModel;

So now that we have that bit of awkwardness out of the way, when I call into the claims class to validate the FBA user credentials the user typed in I need to tell it what Membership and Role provider it should use.  It just needs a name is all.  In my particular case I had written a custom Membership and Role provider for what I was doing, so I just enumerated through all the providers my web application knew about until I found mine:

//get the provider names for our type

string userProviderName = string.Empty;

string roleProviderName = string.Empty;

 

//get the membership provider name

foreach (MembershipProvider p in Membership.Providers)

{

if (p.GetType().Equals(typeof(Microsoft.SE.AnonProvider.Users)))

       {

       userProviderName = p.Name;

              break;

       }

}

 

//get the role provider name

foreach (RoleProvider rp in System.Web.Security.Roles.Providers)

{

if (rp.GetType().Equals(typeof(Microsoft.SE.AnonProvider.Roles)))

       {

              roleProviderName = rp.Name;

              break;

       }

}

 

Okay, great, I got my provider names.  Now we need to take the username and password and get back a SecurityToken.  In order to do that, we’re going to use the SPSecurityContext class.  It has a method designed just to do this forms based auth login for us; if it’s successful it returns a SecurityToken – if not it returns null.  Here’s what it looks like when we authenticate the user credentials:

SecurityToken tk = SPSecurityContext.SecurityTokenForFormsAuthentication(

new Uri(SPContext.Current.Web.Url), userProviderName, roleProviderName,

              UserNameTxt.Text, PasswordTxt.Text);

 

So I’ve passed in a Uri for the site I’m trying to authenticate against, I’ve told it the name of my membership and role providers, and I’m passing in the username and password values that were typed in the textboxes in my login page.   Now I need to check to make sure that my SecurityToken is not null, and if it isn’t I need to write a session token.  That is done with the SPFederationAuthenticationModule.  Once I’ve written my session token then I can go ahead and redirect the user to whatever page or resource it was that they requested.  Here’s the rest of the code that does that:

if (tk != null)

{

//try setting the authentication cookie

SPFederationAuthenticationModule fam = SPFederationAuthenticationModule.Current;

fam.SetPrincipalAndWriteSessionToken(tk);

 

       //look for the Source query string parameter and use that as the redirection

       string src = Request.QueryString[“Source”];

       if (!string.IsNullOrEmpty(src))

       Response.Redirect(src);

}

else

{

StatusLbl.Text = “The credentials weren’t valid or didn’t work or something.”;

}

 

Now you see when I’ve successfully done everything then I just grab the Source query string parameter because it tells us where the user was originally headed.  Once I have that I just send them on their way.

Hopefully this helps you get going.  I know it was a real struggle to find the documentation on how best to do this when I was looking for it.  In part 2 we’ll look at doing this a different way for a different scenario.  In that well want to have someone sign an “I agree to the terms of use for this website” thing before they use the site the first time.  To do that we’ll look at extending the base login page and adding a handler at login time. 

One thought on “Writing A Custom Forms Login Page for SharePoint 2010 Part 1

  1. Hi Steve,

    We are running in some trouble here at work as we are making the double hop from sharepoint 2007 to 2010 and them 2013. I am looking for all over the net abou FBA on 2010 and nobody seems to have our scenario. See if you can enlighten us with your sharepoint wisdom🙂
    We have a 2007 sharepoint site that uses some ASP pages from an homebread application. This app uses a cookie generated by our custom authentication provider. In 2007 this was fine as we put sharepoint to use this authentication provider. The user typed a sharepoint url, the sharepoint redirected it to our custom login page, the user was validated, the redirected back to sharepoint and a cookie was generated. When in sharepoint if the user hit one of our ASP pages it checked if it was validated looking for the cookie generated before. All was good.
    With 2010 this does not work as there is the claims auth, and the brand new STS that does the job now. We tried to use FBA with native sharepoint form and validation using our provider (changed web.config in site, central admin and STS, included auth provider in site adm) but no joy for our cookie. Sharepoint generates the federation cookie but not ours. So at the end of the day, my question is: Is there a way to use claims auth and still get my custom provider generat cookie? Without this cookie my user will be able to login in sharepoint, but not in the ASP pages.

    Thanks,

    Marcelo – Rio de Janeiro – Brazil

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s