While recently helping out my friends at Envision IT with their SharePoint 2010 FBA configuration, I came across an unsurprisingly frustrating aspect of using a custom ASP.NET form to handle the authentication. From most of the sparse documentation that is available on this topic (re: custom FBA forms – there’s tons on OOTB FBA), you’d think that all that’s required is to provide an URL to your form in Central Administration and Bob is your father’s brother.

Ta-da! Ready to go home now!
Um, noooo.
The Bad News: It Ain’t Gonna Work
It’d be great if things worked so easily as just slapping in an URL and the back-end wiring itself up automagically like it came out of Hogwarts – not so fast. Say you have a basic ASP.NET form and you’ve configured it thus with standard <asp:Login> control:
<%@ Page Language="c#" AutoEventWireup="false" Inherits="System.Web.UI.Page" Trace="false" %>
<form id="fbaLoginForm" runat="server">
<asp:login id="signInControl" runat="server" displayrememberme="False"></asp:login>
<asp:checkbox id="chkRememberMe" runat="server" cssclass="RememberMe" text="Remember me and my language preference" tooltip="Tooltip goes here" visible="False" /> <br />
<span style="margin-left: 4px">
<a href="../ForgotPass/default.aspx" class="Label">
<asp:label id="lblForgotPass" runat="server" text="Forgot Password?"></asp:label>
</a>
</span> </div> <script type="text/javascript" language="javascript">
// This Script focuses on the Username field (or the password field if the username was remembered)
if (document.getElementById("ctl00_ContentPlaceHolder1_Login1_UserName").value != "") {
document.getElementById("ctl00_ContentPlaceHolder1_Login1_Password").focus();
} else {
document.getElementById("ctl00_ContentPlaceHolder1_Login1_UserName").focus();}
</script> </form>
Assuming that you’ve already set up the ASP.NET Membership SQL database, configured the Membership and Role providers, made the commensurate changes in the web app, Secure Token Service and Central Admin web.configs and gone into Central Administration to configure Forms Based authentication, you’d find that all your efforts would have been for nought: This form would dutifully validate your credentials against the Membership database, but then unmercilessly given you a 403 HTTP error when the form attempted to redirect to /_layouts/Authenticate.aspx. (See my “How-To” FBA links on Delicious for reference if you’re lost in what I just said)
Why?
Because a vanilla ASP.NET form has no idea how to create the required claims based authentication token to pass along to SharePoint’s doorman to say that you really are on “the list” and that you know this guy inside who can vouch for you. This form inherits from System.Web.UI.Page – you are a nobody as far as Club SharePoint 2010 is concerned. Move along.
The Good News: You Can Make It Work
There is another way, but it is top-secret. Well, at least it seems that way given the rigamarole I had to go through to put it together – there’s no API documentation on this, you see. The solution is to go in-cognito: In this case, to set your login form page to inherit from the same class that the out-of-the-box SharePoint FBA login form implements: Microsoft.SharePoint.IdentityProvider.FormsSignInPage (this can be found in the /_forms sub folder of any SharePoint 2010 web application that has been set for FBA).

Our target: The OOTB SharePoint 2010 FBA Form
Now through the magic of polymorphism, you too can access all the SharePoint FBA/Claims Auth token creation goodness out-of-the-box FBA forms get for “free”. Please, hold your applause.

/_forms/default.aspx – You want to rip this code off
Using the default.aspx page as a guide, you can adapt your own custom login .aspx page and master page to implement the required <asp:Content> areas – you might want to keep them all or put the ones you don’t plan to use inside an <asp:Panel> control with the Visible property set to false. Here’s what my revised login page default.aspx looks like – note the Assembly and Import directives and the Inherits property for the <% Page %> directive, and the <asp:Content> controls – these are required by the FormsSignInPage.
1 <%@ Assembly Name="Microsoft.SharePoint.IdentityModel, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
2 <%@ Assembly Name="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
3 <%@ Assembly Name="Microsoft.Web.CommandUI, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
4 <%@ Import Namespace="Microsoft.SharePoint.WebControls" %>
5 <%@ Import Namespace="Microsoft.SharePoint" %>
6
7 <%@ Page Language="c#" AutoEventWireup="false" MasterPageFile="DefaultGoodMasterPage.master"
8 Inherits="Microsoft.SharePoint.IdentityModel.Pages.FormsSignInPage" Trace="false" %>
9
10 <%@ Register TagPrefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls"
11 Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
12 <%@ Register TagPrefix="Utilities" Namespace="Microsoft.SharePoint.Utilities"
13 Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
14
15 <asp:Content ID="Content2" ContentPlaceHolderID="PlaceHolderPageTitle" runat="server">
16 <SharePoint:EncodedLiteral runat="server" EncodeMethod="HtmlEncode" ID="ClaimsFormsPageTitle" />
17 </asp:Content>
18 <asp:Content ID="Content3" ContentPlaceHolderID="PlaceHolderPageTitleInTitleArea"
19 runat="server">
20 <SharePoint:EncodedLiteral runat="server" EncodeMethod="HtmlEncode" ID="ClaimsFormsPageTitleInTitleArea" />
21 </asp:Content>
22 <asp:Content ContentPlaceHolderID="PlaceHolderSiteName" runat="server" />
23 <asp:Content ContentPlaceHolderID="PlaceHolderMain" runat="server" />
24
25 <asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" runat="Server">
26 <SharePoint:EncodedLiteral runat="server" EncodeMethod="HtmlEncode" ID="ClaimsFormsPageMessage" />
27 <div style="text-align: left; margin-top: 10px;">
28 <asp:Login ID="signInControl" runat="server">
29 </asp:Login>
30 <asp:CheckBox ID="chkRememberMe" runat="server" Text="Remember me"
31 ToolTip="Tooltip goes here" Visible="False" />
32 <br />
33 <span style="margin-left: 4px"><a href="~/forgotmypassword/default.aspx">
34 <asp:Label ID="lblForgotPass" runat="server" Text="Forgot Password?">
35 </asp:Label>
36 </a></span>
37 </div>
38 <script type="text/javascript" language="javascript">
39 // This Script focuses on the Username field (or the password field if the username was remembered)
40 if (document.getElementById("ctl00_ContentPlaceHolder1_Login1_UserName").value != "") {
41 document.getElementById("ctl00_ContentPlaceHolder1_Login1_Password").focus();
42 } else {
43 document.getElementById("ctl00_ContentPlaceHolder1_Login1_UserName").focus();
44 }
45 </script>
46 </asp:Content>
47
Below is the master page code for my demo login form. Note the <asp:Panel> control I’ve placed at line 32 that contains the <asp:ContentPlaceHolder> controls that are required by the FormsSignInPage class, but I don’t want to implement right now so I’ve hidden them. This is an old trick I’ve borrowed from creating minimal master pages for SharePoint 2007 (nod to Heather Solomon; what? you don’t know who she is? For shame…)
1 <%@ Master Language="c#" %>
2
3 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
4 <html xmlns="http://www.w3.org/1999/xhtml" runat="server" id="html">
5 <head id="Head1" runat="server">
6 <title>Good SharePoint 2010 FBA Login</title>
7 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
8 <meta name="keywords" content="" />
9 <!-- Head Content Ends here -->
10 <asp:ContentPlaceHolder ID="ContentPlaceHolderHead1" runat="server">
11 </asp:ContentPlaceHolder>
12 <!-- Head Content Ends here -->
13 </head>
14 <body onload="if (typeof(body_onload) == 'function') window.setTimeout('body_onload();', 500);">
15 <form id="form1" runat="server">
16 <h1 style="color: Green">
17 Good SharePoint 2010 FBA Login Form</h1>
18 <div>
19 This form will process and validate credentials for an FBA user and successfully
20 redirect them to the SharePoint Site.
21 <p>
22 Specifically, it inherits from <b>Microsoft.SharePoint.IdentityModel.Pages.FormsSignInPage</b>
23 </p>
24 </div>
25 <div>
26 <!-- Page Content Starts here -->
27 <asp:ContentPlaceHolder ID="ContentPlaceHolder1" runat="server">
28 </asp:ContentPlaceHolder>
29 <!-- Page Content Ends here -->
30 </div>
31 <!-- Required ContentPlaceHolders for parent page object -->
32 <asp:Panel runat="server" Visible="false">
33 <asp:ContentPlaceHolder ID="PlaceHolderPageTitle" runat="server">
34 </asp:ContentPlaceHolder>
35 <asp:ContentPlaceHolder ID="PlaceHolderPageTitleInTitleArea" runat="server">
36 </asp:ContentPlaceHolder>
37 <asp:ContentPlaceHolder ID="PlaceHolderSiteName" runat="server">
38 </asp:ContentPlaceHolder>
39 <asp:ContentPlaceHolder ID="PlaceHolderMain" runat="server">
40 </asp:ContentPlaceHolder>
41 </asp:Panel>
42 </form>
43 </body>
44 </html>
45

Behold: This login form has actually been nominated for several design awards.
Using this technique, you can now adapt your pre-existing FBA login forms to conform with the claims based auth requirements for SharePoint 2010. As far as I can tell, as of this writing there is no documentation around doing this – I had to use Reflector to determine what was going on inside the FormsSignInPage object as the API has no corresponding entries.
It’s a little more involved than just slapping down an ASP.NET page and setting the Sign In Form URL – but not that much more.
Please enjoy responsibly!