On your way to branding a public-facing SharePoint 2010 site, you may at some point find yourself eye-to-eye with its bare, unfeeling, and unskinnable default login page. Giving life to this page is a dicey affair—it is located under the _login directory in the hive, way outside the jurisdiction of your site’s master pages. It uses the simple.master master page, which, from a design standpoint, might as well be an empty text file.
If you should find yourself facing such a task, do not panic. Rather, reach slowly for Visual Studio 2010 and follow along at home–we’re going to build a Forms Login Web Part that can handle the same authenticatory task in much more design-friendly surroundings.
First, we’ll create an Empty SharePoint Project, and add a Web Part to it. Let’s call it LoginWebPart.
Be sure to select Web Part, and not Visual Web Part. Sure, it’s neato-torpedo to be able to build web parts visually, and nine times out of ten that’s going to be a better fit for what you will be up against in a custom SharePoint development scenario. But for this little adventure, it throws in a sopping heap of clutter that we’d just end up having to slice out in the end.
Let’s right-click on our project and add a reference to the Microsoft.SharePoint.IdentityModel library. This avails to us the SPClaimsUtility class, which houses a method that nicely black-boxes the messy logging-in part of our exercise. Look sharp, though—this evasive batch of IL doesn’t appear on the Add Reference dialog’s regular assemblies menu. Instead, you will have to browse from the dialog to the find the DLL under C:\Windows\assembly\GAC_MSIL\Microsoft.SharePoint.IdentityModel\14.0.0.0__71e9bce111e9429c.
Now, we crack open our web part’s code-behind file (LoginWebPart.cs) and add a ReturnUrl property. This allows the user editing the page to specify a redirect URL for successful login attempts.
private string _returnUrl; /// <summary> /// Gets or sets the URL to redirect to /// upon successful sign in. /// </summary> /// <value></value> /// <returns></returns> /// <remarks></remarks> [Personalizable(PersonalizationScope.Shared), WebBrowsable(true), Category("Behavior"), WebDisplayName("Return URL"), WebDescription("The URL to which users are redirected upon successfully signing in.")] public string ReturnUrl { get { if ((string.IsNullOrEmpty(_returnUrl))) _returnUrl = this.Page.Request["ReturnUrl"]; return _returnUrl; } set { _returnUrl = value; } }
Those attributes decorating the property make it available for editing in the web part’s settings panel. See?
Next, in the overridden CreateChildControls method so thoughtfully provided by our IDE, we want to add a standard ASP.NET Login control, tweak a couple of key properties, and add handler methods for its Authenticate and LoggedIn events.
protected override void CreateChildControls()
{
// create a login control
var loginControl = new Login();
// hide the Remember Me checkbox
loginControl.DisplayRememberMe = false;
// hide the control if the User is already logged in
loginControl.Visible = !this.Page.User.Identity.IsAuthenticated;
// add event handlers
loginControl.Authenticate += new AuthenticateEventHandler(loginControl_Authenticate);
loginControl.LoggedIn += new EventHandler(loginControl_LoggedIn);
Controls.Add(loginControl);
}
In the Authenticate event handler method, we use the aforementioned SPClaimsUtility class to validate the user’s credentials:
void loginControl_Authenticate(object sender, AuthenticateEventArgs e)
{
// validate credentials
var login = (sender as Login);
e.Authenticated = SPClaimsUtility.AuthenticateFormsUser(
this.Page.Request.Url, login.UserName, login.Password);
}
Lastly, in the LoggedIn event, we will use the SPUtility class to redirect the user to the given ReturnUrl:
void loginControl_LoggedIn(object sender, EventArgs e)
{
// redirect to ReturnUrl
SPUtility.Redirect(
this.ReturnUrl, SPRedirectFlags.Default, this.Context);
}
After deploying this to our site, we will find the LoginWebPart under the Custom category when adding a web part to a page:
We can now place this web part anywhere in the site—preferably in an anonymously accessible area. I suppose if you were really touched in the head, you could secure it, too.
Take a deep cleansing breath and forget about the barren default login page—our new Login Web Part lets us place a security prompt within reach of our branded master page, perhaps even with other public content. It gives us more control and flexibility in the arrangement of our site, which may in fact be the whole thrust of Web Parts in the first place.
15 comments
I came across your webpage from twitter and it really is pretty informative. Thanks for discussing this kind of an incredible article.
Keep up the good work – for sure i will check out more posts.
You seem to know a lot about this. This is good blog. A great read. I’ll certainly be back.
Any update on this ? Excellent information keep it up!
Wow this was very helpful! So how do we go about using the “Remember Me” feature? is that built in? Thanks :)
I found this post very informative which I found on yahoo. I bookmarked your blog. Thanks for sharing.
Hi great post I have implmented this however on the Sign Out the pages errors crashes
I know why and its related to the Federated Authorisation cookie used by claims I have also seen posts to fix this implemented on a login status control however with out adding this and overriding / replacing the existing Sign Out is there another way or reason for this
[ArgumentException: Exception of type 'System.ArgumentException' was thrown.
Parameter name: encodedValue]
Microsoft.SharePoint.Administration.Claims.SPClaimEncodingManager.DecodeClaimFromFormsSuffix(String encodedValue) +25840358
Microsoft.SharePoint.Administration.Claims.SPClaimProviderManager.GetProviderUserKey(String encodedSuffix) +73
Microsoft.SharePoint.SPGlobal.CreateSPRequestAndSetIdentity(SPSite site, String name, Boolean bNotGlobalAdminCode, String strUrl, Boolean bNotAddToContext, Byte[] UserToken, String userName, Boolean bIgnoreTokenTimeout, Boolean bAsAnonymous) +27266953
Microsoft.SharePoint.SPWeb.InitializeSPRequest() +223
Microsoft.SharePoint.WebControls.SPControl.EnsureSPWebRequest(SPWeb web) +365
Microsoft.SharePoint.WebControls.SPControl.SPWebEnsureSPControl(HttpContext context) +520
Microsoft.SharePoint.ApplicationRuntime.SPRequestModule.GetContextWeb(HttpContext context) +27
Microsoft.SharePoint.ApplicationRuntime.SPRequestModule.PostResolveRequestCacheHandler(Object oSender, EventArgs ea) +918
System.Web.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +80
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +171
thank you for the solution. When I am trying to add the webpart to the page. it gives me “An error occured while attempting to add the item to the page”. Would you have any clue regarding this?
In the debug mode: A first chance exception of type ‘System.NullReferenceException’ occurred in WebParts.dll is the error. Any suggestions?
Hi Dhawal,
I am receiving the same error as you reported. Did you manage to resolve this issue? Hope you did.
Kindly reply if you have any solution for this error.
Thanks.
I am having the same error as Dhawal. Any ideas?
Hi,
Thank you for this post. I am receiving the same error as reported by @Dhawal. So, I was not able to add the webpart to the page. Is there any solution for this error?
Thanks.
@above…….
Please Try this…
replace >>> private string _returnUrl;
with >>>>private string _returnUrl=”http://your site redirection url after login”;
I am not sure about exact reason.But somehow when you add the webpart, the webpart code is in running state.So It says null reference for the property >>>.ReturnUrl (since it’s not initialized yet) and you are using in this line>>>SPUtility.Redirect(this.ReturnUrl, SPRedirectFlags.Default, this.Context);
and comment the part>>>> loginControl.Visible = !this.Page.User.Identity.IsAuthenticated;
because when you will add the webpart then it is already logged in as administrator.
So if you are already logged in this statement will make invisible the control.
Any update on this post
Replace the code with below code to add the web part on the page
if (!(string.IsNullOrEmpty(_returnUrl)))
_returnUrl = this.Page.Request["ReturnUrl"];
Hi, Neat post. There’s an issue with your website in web explorer, would check this? IE nonetheless is the marketplace leader and a large portion of other folks will leave out your fantastic writing due to this problem.