NEW PREFACE TO THIS POSTING, AND OPEN COMMENT TO THE SITE OWNER:
Since the blog site was upgraded to the latest version of whatever the blazes this thing is, the formatting has stunk like a pig on the 4th of July. I usually spend a fair amount of time trying to jury-rig the pathetic markup (or lack thereof) this new version provides. PLEASE CONSIDER CONTACTING THE WEB MASTER AND LETTING THEM KNOW HOW DIFFICULT IT IS TO READ THESE AWFULLY FORMATTED POSTS! In the meantime, I will try and attach a Word document with the original post as I typed it whenever possible. Who says technology doesn't stink?? Okay, here we go...
In part 1 of this series, which you can find at http://blogs.technet.com/b/speschka/archive/2010/07/21/writing-a-custom-forms-login-page-for-sharepoint-2010-part-1.aspx, I described how to create an entirely new forms login page. The scenario behind it is when you need something beyond what is available with the out of the box UI – for example, two factor authentication. In this post I’m going to walk through a different scenario. In this case the out of the box UI is fine, but we want to do “something” additional at login time.
To start with, I’m going to create my new login page. I just added a new ASPX page to my project. In this project I’m going to need to add references to Microsoft.SharePoint and Microsoft.SharePoint.IdentityModel. For details on how to add the reference to Microsoft.SharePoint.IdentityModel, see part 1, which I’ve linked to above. In the code behind for my page I’m going to use a gaggle of using statements again. Here’s which one’s I’ve used:
Because the page I’m writing is a login page for forms based authentication, it needs to inherit from FormsSignInPage. My class declaration looks like this:
public partial class AgreeLogin : FormsSignInPage
The first thing I’m going to do in code is to override the page Load event. In there I’ll add a handler for the logging in event for the page’s ASP.NET Login control. Here’s what the override looks like for Page Load:
protected override void OnLoad(EventArgs e)
catch (Exception ex)
One note here – I put this in a try…catch block because if you goof up the page markup, which I’ll explain below, you will toss an error when base.OnLoad(e) is called. Next, I’ll go ahead and show you the implementation of the LoggingIn event handler:
void signInControl_LoggingIn(object sender, LoginCancelEventArgs e)
//look for a cookie; if not there then redirect to our accept terms page
const string AGREE_COOKIE = "SignedTermsAgreement";
//we want to check for our cookie here
HttpCookieCollection cookies = HttpContext.Current.Request.Cookies;
HttpCookie ck = cookies[AGREE_COOKIE];
if (ck == null)
//user doesn't have the cookie indicating that they've signed the
catch (Exception ex)
Debug.WriteLine("There was an error processing the request: " +
<allow users="*" />
So now I can hit my AgreeTerms.aspx page without being authenticated. I won’t bore you with the UI of the page now, but in the code behind for this page it is pretty simple:
protected void SubmitBtn_Click(object sender, EventArgs e)
const string AGREE_COOKIE = "SignedTermsAgreement";
//make sure the checkbox is checked
if ((AgreeChk.Checked) && (Request.Browser.Cookies))
//create the cookie
HttpCookie ck = new HttpCookie(AGREE_COOKIE, "true");
//set the expiration so it's persisted
ck.Expires = DateTime.Now.AddYears(3);
//write it out
//get the src attribute from the query string and redirect there
string msg = "There was an error processing the request: " + ex.Message;
StatusLbl.Text = msg;
That’s all there is to it from a coding perspective, but there is one other thing that is SUPER important to have down, and that’s the mark up for the login page. As I described earlier, if the mark up is wrong you’ll be in all sorts of trouble. So to start with, here’s the mark up you should start with:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="AgreeLogin.aspx.cs" Inherits="FormsLoginPage.AgreeLogin,FormsLoginPage, Version=188.8.131.52, Culture=neutral, PublicKeyToken=cf32e76ff986e00f" MasterPageFile="~/_layouts/simple.master" %>
<%@ Assembly Name="Microsoft.SharePoint.ApplicationPages, Version=184.108.40.206, Culture=neutral, PublicKeyToken=71e9bce111e9429c"%>
<%@ Import Namespace="Microsoft.SharePoint.ApplicationPages" %> <%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=220.127.116.11, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=18.104.22.168, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> <%@ Import Namespace="Microsoft.SharePoint" %>
<%@ Assembly Name="Microsoft.Web.CommandUI, Version=22.214.171.124, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<asp:Content ContentPlaceHolderId="PlaceHolderPageTitle" runat="server">
<SharePoint:EncodedLiteral ID="ClaimsFormsPageTitle" runat="server" text="<%$Resources:wss,login_pagetitle%>" EncodeMethod='HtmlEncode'/>
<asp:Content ContentPlaceHolderId="PlaceHolderPageTitleInTitleArea" runat="server">
<SharePoint:EncodedLiteral ID="ClaimsFormsPageTitleInTitleArea" runat="server" text="<%$Resources:wss,login_pagetitle%>" EncodeMethod='HtmlEncode'/>
<asp:Content ContentPlaceHolderId="PlaceHolderSiteName" runat="server"/>
<asp:Content ContentPlaceHolderId="PlaceHolderMain" runat="server">
<SharePoint:EncodedLiteral runat="server" EncodeMethod="HtmlEncode" ID="ClaimsFormsPageMessage" />
<asp:login id="signInControl" FailureText="<%$Resources:wss,login_pageFailureText%>" runat="server" width="100%">
<asp:label id="FailureText" class="ms-error" runat="server"/>
<td nowrap="nowrap"><SharePoint:EncodedLiteral ID="EncodedLiteral3" runat="server" text="<%$Resources:wss,login_pageUserName%>" EncodeMethod='HtmlEncode'/></td>
<td><asp:textbox id="UserName" autocomplete="off" runat="server" class="ms-long ms-login-textbox"/></td>
<td nowrap="nowrap"><SharePoint:EncodedLiteral ID="EncodedLiteral4" runat="server" text="<%$Resources:wss,login_pagePassword%>" EncodeMethod='HtmlEncode'/></td>
<td><asp:textbox id="password" TextMode="Password" autocomplete="off" runat="server" class="ms-long ms-login-textbox"/></td>
<td colspan="2" align="right"><asp:button id="login" commandname="Login" text="<%$Resources:wss,login_pagetitle%>" runat="server" /></td>
<td colspan="2"><asp:CheckBox id="RememberMe" text="<%$SPHtmlEncodedResources:wss,login_pageRememberMe%>" runat="server" /></td>
The one and only thing you really want to change is the very first line, the @Page directive.
The part highlighted in yellow should be changed to be the strong name for your custom assembly.
Once you’ve got this all put together and compiled, just register your assembly in the GAC and copy your ASPX page(s) to the layouts directory. The last step before you actually try it out is to change login page for the web application zone where you are using FBA. Go to central administration, Application Management, Manage web applications. Select your web application and then click the Authentication Providers button on the toolbar. Click the link to the zone you want to change, and in the dialog that comes up click the Custom Sign In Page radio button and enter the Url to your login page. In my case it was _layouts/AgreeLogin.aspx. Save your changes and you’re ready to log into your site.
Finally, here’s what it looked like as I logged into my site with my custom pages – hope this post helps you do the same!
1. Initial Login
3. Finally Hitting the Site
Excelent post. It really helped me!
great post as usual!
I agree with all of the points keep up the good work.
I have one problem, though:
When I redirect to the AgreeTerms.aspx a windows authenticaction is requested. And even when I provide the system account as user, get an HTTP 401.
I have provided the allow users="*" in the web.config for the Folder _layouts/Login, where the site resides.
Any ideas, on what could have gone wrong? I' into this for hours now and I do not have any clue.
Thanks in advance!
The problem has been the SharePoint masterpage I was still using on the terms screen... Creating a "normal" aspx did the trick.
So again: Great work!
This is great information. Does anyone know if there is a way to do this without using FBA?
This is a great post just like the first.
I have a question on making a simple modification to the sign in form, I just want to add a link. Since I am not a developer I am having trouble finding out where to make the change. Any help will be greatly appreciated.
In our SharePoint projects we use a tool to customize SharePoint Forms: Advanced Forms for SharePoint (http://www.kaldeera.com)
Hope this helps.
Really great post but can this solution be adjusted to run in a SharePoint 2010 Publishing Site?