Bypassing the Multi Authentication Provider Selection Page in SharePoint 2010
SIDE NOTE: Yet another kudos to the fabulous folks that run this site. This latest version now retains even LESS formatting from Word and Visual Studio than before! I didn't think it was possible to make this site any worse than it was, and yet you've shattered, dare I say blown away, my expectations in this regard. Congrats! I hope to follow suit soon myself and ditch Word, Excel and PowerPoint for notepad - who needs formatting anyways?
I recently needed to bypass the provider selection page that you get when you enable multiple authentication providers on a single zone in SharePoint 2010. The scenario I had was a fairly simple one, but the methodology can be extended quite a bit to support much more complicated scenarios. In my case, I had Windows authentication and forms based authentication (FBA) enabled on a zone. However I always wanted to redirect users to use FBA for this particular scenario.
Accomplishing this was relatively straightforward by following these steps:
- MAKE A BACKUP COPY of default.aspx in the C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\IDENTITYMODEL\LOGIN folder.
- Open Visual Studio and create new Windows Class Library project.
- Add references to System.Web, Microsoft.SharePoint.dll and Microsoft.SharePoint.IdentityModel.dll. Unfortunately the identity model assembly is only found in the GAC so I had to get a copy of it and put it in the root of my drive to add my reference. For a suggestion of how to find it and copy it up you can review my posting that describes getting it here: https://blogs.technet.com/b/speschka/archive/2010/07/21/writing-a-custom-forms-login-page-for-sharepoint-2010-part-1.aspx.
- Strong-name the assembly because it will be going in the GAC.
- Add a new ASPX page to your project. Honestly I find the easiest way to do this is to just copy a page from an existing ASP.NET web application project; that way can copy over the .aspx, the .aspx.cs, and .aspx. designer. cs files all at once. Remember in this case we want a file called “default.aspx” and it will be easier if there’s no code written in it yet and minimal markup in the page.
- In the code-behind (.aspx.cs file) change the namespace to match the namespace of your current project.
- Change the class so it inherits from Microsoft.SharePoint.IdentityModel.Pages.MultiLogonPage.
- Override the OnLoad event. What happens when users hit a site with multiple authentication providers enabled is that they are sent first to the /_login/default.aspx page (the one I described in #1 above). On that page a user will select which authentication provider to use and then he or she is redirected to the correct page to authenticate. In this scenario I’ve said that I always want users to authenticate with FBA, so I’ll always want to send them to /_forms/default.aspx. If you step through a normal login, you’ll see that you are redirected to /_login/default.aspx, you make your selection and then you post back to /_login/default.aspx, and then you are redirected to the correct login page. So in this case I merely looked to see if my login page was being posted back. If it isn’t, then I know that no selection has been made yet for the authentication provider. So I just enumerate all of the query string values and then append them to /_forms/default.aspx and redirect the user there. Here’s what the entire code snippet looks like:
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
try
{
//if this isn't a postback, then the user hasn't selected which
//auth provider they want to use
//in this case we want to always refer the person to forms login
if (!this.IsPostBack)
{
//grab all the query string parameters
System.Text.StringBuilder qp = new System.Text.StringBuilder(2048);
foreach (string key in this.Request.QueryString.Keys)
{
qp.Append(key + "=" + this.Request.QueryString[key] + "&");
}
//redirect to the forms login page
this.Response.Redirect("/_forms/default.aspx?" + qp.ToString());
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
- Now compile the application so you can get the strong name for it and add it to the markup for default.aspx.
- Paste the following into default.aspx; you will just need to change the class from which the page inherits (highlighted below); note that all I did was just copy it from /_login/default.aspx and replace the Inherits value with my custom class info:
<%@ Page Language="C#" CodeBehind="Default.aspx.cs" Inherits="MultiAuthLoginPage._Default,MultiAuthLoginPage, Version=1.0.0.0, Culture=neutral, PublicKeyToken=907bf41ebba93579" MasterPageFile="~/_layouts/simple.master" %>
<%@ Assembly Name="Microsoft.SharePoint.IdentityModel, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="SharepointIdentity" Namespace="Microsoft.SharePoint.IdentityModel" Assembly="Microsoft.SharePoint.IdentityModel, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Assembly Name="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"%>
<%@ Import Namespace="Microsoft.SharePoint.WebControls" %>
<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Import Namespace="Microsoft.SharePoint" %>
<%@ Assembly Name="Microsoft.Web.CommandUI, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<asp:Content ID="Content1" ContentPlaceHolderId="PlaceHolderPageTitle" runat="server">
<SharePoint:EncodedLiteral runat="server" EncodeMethod="HtmlEncode" Id="ClaimsLogonPageTitle" />
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderId="PlaceHolderPageTitleInTitleArea" runat="server">
<SharePoint:EncodedLiteral runat="server" EncodeMethod="HtmlEncode" Id="ClaimsLogonPageTitleInTitleArea" />
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderId="PlaceHolderSiteName" runat="server"/>
<asp:Content ID="Content4" ContentPlaceHolderId="PlaceHolderMain" runat="server">
<SharePoint:EncodedLiteral runat="server" EncodeMethod="HtmlEncode" Id="ClaimsLogonPageMessage" />
<br />
<br />
<SharepointIdentity:LogonSelector ID="ClaimsLogonSelector" runat="server" />
</asp:Content>
- Register your assembly in the GAC.
- Copy your new custom default.aspx page into the C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\IDENTITYMODEL\LOGIN folder. AGAIN, MAKE A BACKUP OF THE ORIGINAL BEFORE YOU DO THIS!
- Do steps 1, 11, and 12 on every web front end in your farm.
That’s all there is to it. I tested this with a standard user login, as well as opening documents directly from the Microsoft Office 2010 clients. One other thing worth noting here: this changes the behavior for ALL web applications in your farm! Again, that’s why it’s a simple example. However you could easily look at the host name of the request (which maps to your web application) and make different authentication decisions based on which web application is being accessed or even which site collection. You can obviously also make other decisions based on information you have about the current user. The HttpRequest.Context.Current, Page.Request and Page.Response classes can provide you with a lot of information to make these kinds of decisions.
Bypassing the Multi Authentication Provider Selection Page in SharePoint 2010.docx
Comments
Anonymous
January 01, 2003
The comment has been removedAnonymous
January 01, 2003
first reading the article i wondered why not use a custom login page... I seems to me that using the custom login page is not a good idea because the search application cannot handle the 302 Redirect. (highest level error - object moved) now i do it the same way: replace the default.aspx in the IDENTITYMODELLOGIN folder. and search crawler does fine. btw. the search crawler solves the "IIS Sharepoint warm up" problem. crawling all site-cols in the web-app warms them up. rightly?!Anonymous
January 01, 2003
thanksAnonymous
January 01, 2003
thanksAnonymous
May 02, 2011
Looks great STeve, but if I want to authenticate in via AD (this is a off premise server), how can I chose to authenticate that way?Anonymous
May 04, 2011
Would it not be easier to create a custom login form and handle both authentication methods in one?Anonymous
May 04, 2011
@Charles, it depends on your requirements, which is why I tried to stress that this was a simple example for one scenario. In this particular case I wanted all users to go through FBA so there was no need to handle both auth types.Anonymous
August 04, 2011
Hello, Steve. Thanks for great article! Can i do that without change original default.aspx - just configure app to use custom login page?Anonymous
September 09, 2011
Found an interesting blog about using the custom login page field and specifying the trust provider. Thisi s the custom login page, not code option. Only issue is that logout breaks so..... shojeeb.com/.../custom-sign-in-page-url-for-sharepoint-quick-wayAnonymous
November 11, 2011
Hi Steve, I implemented the code and it works. However, it's actually throwing an exception when I attached the code to the process and debugging it. Another thing I notice is the Sign in as different user link doesn't work, The query string got appended twice. Thanks, KevinAnonymous
October 04, 2013
Hi, I have a problem. I get parse error 'could not load type MultiAuthLogin' even though I have registered the library in GAC. <%@ Page Language="C#" CodeFile="~/Default.aspx.cs" Inherits="MultiAuthLoginPage.MultiAuthLoginPage.Default" MasterPageFile="~/_layouts/simple.master" %> Please advise. Thanks, David.Anonymous
April 22, 2014
Hello Steve
Thank you very much for your blog post. It was very helpful for us. We implemented your solution, because the customer authenticate only over ADFS, but for SharePoint internally we need Windows Authentication (ex. Search). It works perfect for about 18000 Users :-)
The Customer has SharePoint 2013 Enterprise (CU October 13). The problem that we have is that it does not work for mobile clients (ex. iPhone or Windows Phones). We get there the Selection of multiple Providers.
We see in the URL that the requests are forwarded to /_layouts/15/mobile/authn_dispatch.aspx. Do you have an idea how we can get it also to work for mobile clients?
Thank you very much for your help in advance.
Best Regards
JoëlAnonymous
April 28, 2014
The comment has been removedAnonymous
May 15, 2014
We found the solution with the help of Microsoft. We just choose the wrong Namespace :-) Here is the code from Microsoft:
using System;
using System.Collections.Specialized;
using System.Web;
public partial class MobileSignInPage : Microsoft.SharePoint.IdentityModel.Pages.MobileMultiLogonPage
{
protected override void OnLoad(EventArgs e)
{
try
{
//if this isn't a postback, then the user hasn't selected which
//auth provider they want to use
//in this case we want to always refer the person to forms login
if (!this.IsPostBack)
{
//grab all the query string parameters
System.Text.StringBuilder qp = new System.Text.StringBuilder(2048);
foreach (string key in this.Request.QueryString.Keys)
{
qp.Append(key + "=" + this.Request.QueryString[key] + "&");
}
//redirect to the forms login page
this.Response.Redirect("/_trust/default.aspx?" + qp.ToString());
}
}
catch (Exception ex)
{
//Debug.WriteLine(ex.Message);
}
}
}
Maybe it is helpful for someone :-)Anonymous
July 08, 2014
In a nutshell, there is an undocumented assumption baked into SharePoint Search that the Default PublicAnonymous
September 18, 2014
The comment has been removedAnonymous
March 01, 2015
A scenario that is happening more frequently in SharePoint 2010 is using a single zone for multiple authenticationAnonymous
March 02, 2015
Okay, I'm going to start out by saying I don't how many parts there will be to this little series