It’s been awhile since my last blog post. It’s been awhile that I have been developing SharePoint solutions. Lately I had to write a functional design for a new project, so I used Word and Balsamiq very often… But, good times are here again!
The problem
One of my customers has implemented an F5 Firewall and that firewall does the authentication for all user’s that access SharePoint sites. For some user’s RSA code authorization is in place. Once authentication is done by the F5 Firewall, a session is created and the user is redirected to the SharePoint site. If the SharePoint site is set up for Claims and Forms Based Authentication, the default sign in page is shown with the annoying drop down for choosing the authentication provider. A second login was required.
The request
The customer did not want users to login twice. This should be done automatically. The web application has been set up for FBA and Windows Authentication, since both employees and external users (such as customers) need to login using the same url.
The solution
I have analyzed how the F5 firewall was configured, what features it has and how authenticated credentials are passed on to SharePoint. Basically there were 2 options: NTLM and Forms. If the corporate employee is authenticated against their ActiveDirectory the credentials are passed with NTLM. If an external user is authenticated against Lightweight Directory Services (LDS), the credentials are passed with Forms.
I started to build a solution with a custom login page.The login page could be set in the Web Application’s Authentication Provider setting.
The default login page has a control to show a dropdown with multiple authentication providers. If the Forms Authentication option is selected, the user is redirected to a second login page for entering username and password.
The _login/default.aspx page with the dropdown:
This file is located in the [SharePoint Root]/Template/IdentityModel/Login. Analyzing this ASPX file you’ll notice the page is inherited from Microsoft.SharePoint.IdentityModel.Pages.MultiLogonPage and it contains a line of code with:
<SharepointIdentity:LogonSelector ID=”ClaimsLogonSelector” runat=”server” />
When choosing Forms Authentication in the LogonSelector, the user is redirected to _forms/default.aspx for entering his credentials:
This file is located in the [SharePoint Root]/Template/IdentityModel/Forms. This page is inherited from Microsoft.SharePoint.IdentityModel.Pages.FormsSignInPage This page contains a Login control:
<asp:login id=”signInControl” .. >
Exploring the all code behinds with .NET Reflector or ILSpy you’ll be understanding what happens. Basically the process is as follows:
- Load _login/default.aspx
- Select Forms Authentication
- Redirect to _forms/default.aspx
- Enter user credentials
- Get SecurityToken For Forms Authentication
- Establish Session with Token
- Redirect to Success Url (requested site by user)
So, to login automatically I had to combine these two pages and their functionality. I created a new Visual Studio solution and created a new login.aspx that I deploy to the SharePoint Root somewhere.
In the code behind, the Page_load method will have all the code needed. First of all, the retrieve the Request information. If the F5 authenticated a Windows user, the credentials are passed with the NTLM protocol, otherwise the (external) user is authenticated with Forms and the credentials are passed as Forms.
HttpRequest request = HttpContext.Current.Request; bool isWindowsAuth = false; string username = request["username"]; string password = request["password"]; // If no username is provided by F5, it'll probably be Windows Authentication (NTLMv2 protocol) if (String.IsNullOrEmpty(username)) { isWindowsAuth = true; }
Then you can use the SPIisSettings class to communicate with the different kinds of AuthenticationProviders.
SPIisSettings iisSettings = SPContext.Current.Site.WebApplication.IisSettings[SPUrlZone.Default]; if (isWindowsAuth) { // Windows Authentication it is if (null != iisSettings && iisSettings.UseWindowsClaimsAuthenticationProvider) { SPAuthenticationProvider provider = iisSettings.WindowsClaimsAuthenticationProvider; RedirectToLoginPage(provider); // This should cause automatic sign in } }
And if it is not Windows Authentication, we perform the authentication for Forms.
// FBA authentication it is. SPFormsAuthenticationProvider formsClaimsAuthenticationProvider = iisSettings.FormsClaimsAuthenticationProvider; SecurityToken token = SPSecurityContext.SecurityTokenForFormsAuthentication(new Uri(SPContext.Current.Web.Url), formsClaimsAuthenticationProvider.MembershipProvider, formsClaimsAuthenticationProvider.RoleProvider, username, password, false); if (null != token) { EstablishSessionWithToken(token); base.RedirectToSuccessUrl(); }
Now, all this code does not work if you do not reflect, grab and borrow code from the used Microsoft assemblies by the original Login and Forms ASPX files. You will need to copy the code from the following methods:
// Borrowed from Microsoft.SharePoint.IdentityModel.LogonSelector class private void RedirectToLoginPage(SPAuthenticationProvider provider) { } // Borrowed from Microsoft.SharePoint.Utilities.SPUtility class private string EnsureUrlSkipsFormsAuthModuleRedirection(string url, bool urlIsQueryStringOnly) { } // Borrowed from Microsoft.SharePoint.IdentityModel.Pages.IdentityModelSignInPageBase private void EstablishSessionWithToken(SecurityToken securityToken) { }
When you deploy your solution you can change the web application setting to use the custom sign in page.
Custom Sign Out page
Although you cannot set a custom sign out page through Central Admin like we did for the Sign In page, you can do so by code. The SPWebApplication class has a method called UpdateMappedPage(). With this method you can map several type of pages to your custom ones. E.g. the sign out page looks like this:
currentWebApp.UpdateMappedPage(SPWebApplication.SPCustomPage.Signout, "/_login/octavie/SignOut.aspx"); currentWebApp.Update(true);
Just add a feature with an eventreceiver to your solution. On FeatureActivated add the above code.
On MSDN you can reed more about the UpdateMappedPage method.
Summary
With SharePoint you can define your own custom login page for your Claims Based web application. You can use the default login page for starters, but for more complex requirements you will need tools such as Reflector or ILSpy to analyze what really is going on behind the scenes.
Love this – very easy to understand. Your site is bookmarked.
Thank you, James.
Octavie,
I am facing one issue in FBA system.
1)after logged in site first time ,if i click any link in site redirecting login page again.
2) After second time login everything working normally.
Regards
MUrali.
Nice post. Could you please upload / send the code
Thanks – Raj
Great solution! Thx for sharing! Pointed us the way for solving our task.
Hi Octavie,
I am running in a critical situation in my project, and i have a special requirements to do the following: I want to create a custom login page for both Windows Authentication and Form Based Authentication. I have to create only single login page for both authentication. the user should not know what his user name type. i have to types of authentications “Windows and Custom SQL DB”. how to implement such thing. do i have to create a custom membership and role provider or just i can handle it from the login page.
Thanks
Hi Eyad,
First of all, you will need a Membership for those users that are not authenticated by Windows (Active Directory). You can use my other blog post (http://blog.octavie.nl/index.php/2011/05/20/configuring-forms-based-authentication/) for setting up your custom SQL Database and the ASP.NET Membership.
Second, you need a custom login page as described in this post, especially for the FBA users.
Good luck,
Octavie
We are having a few issues getting the single-sign on to work through the form.
Whilst we can pass through the username and password parameters with no issues, there are two hidden parameters that are automatically generated when the forms page is first presented. As these differ each time the page is loaded, we cannot statically set them in our POST request to logon.
The parameters are:
__EVENTVALIDATION
__VIEWSTATE
It appears that these are used by ASP.NET to prevent POST requests coming from unauthorised pages.
Did you have to get around this issue?
Hi Isaac,
Why would you set these properties yourself? You’re not supposed to.
Octavie
I think I may be out of my depth then. My back ground is pure networking and I think I am now crossing into the application area.
Are you able to provide any guidance as to what you configured in the F5 to get the forms based authentication working?
I have managed to get the SSO working, but only on version 11.
I am now having issues passing HTTP auth…….
Got it all working in the end, apps team write another custom login page, this time it worked, quite a lot of work in the F5 though……