SharePoint 2010 claims and Home Realm Discover - passing whr on the url to SharePoint
Home Realm Discovery is a process to select the trusted identity provider in a federated claims scenario where there is more than one provider that can authenticate users. The default experience is to have the user select which claims provider to use to authenticate. Often organizations would like to avoid making their users perform this decision. There are a few ways to automate this selection, one of which is through providing a hint on the url to the federation server (in the form of a whr parameter) that is then used by the federation server to pre-select the identity provider. The article https://social.technet.microsoft.com/wiki/contents/articles/windows-identity-foundation-wif-how-to-utilize-the-ws-federation-whr-parameter-to-bypass-home-realm-discovery-hrd.aspx describes the two different approaches for a relying party (in this case SharePoint) to implement the use of whr.
Steve Peschka has a bunch of great blogs on claims and SharePoint, including Using the WHR Parameter with SharePoint 2010 and SAML Auth. In this blog Steve describes how to use a http module in a SharePoint environment to insert a whr parameter based upon some decision logic. This insertion onto the url occurs when SharePoint recognizes the user is not authenticated, and goes through a series of redirects to a trusted claims provider to perform authentication. This solves the second, more sophisticated approach for using whr described in previously mentioned article. This approach also requires that you write custom logic and maintain that logic for selecting the authentication provider.
Ideally SharePoint would just work with the first approach as well, which supports directly appending the whr parameter onto the SharePoint url you are attempting to access. Building off of Steve's blog, an example url accessing SharePoint may look like https://www.vbtoys.com/?whr=https://geneva.vbtoys.com/adfs/services/trust. The advantage of this approach is that it would not require the implementation of custom logic. Unfortunately SharePoint doesn't carry over the whr parameter from an incoming url over to the redirect url to the federation server. So it won't work. This approach is simpler but less powerful than the approach described by Steve. Using Steve's approach I'm not tied to a particular url. Rather any url I attempt to access in SharePoint that is being redirected to the federation server will have the whr inserted. But often customers don't want to write custom code, and the benefit of the simpler approach is that I can just hand out different urls for users to bookmark that will do the pre-selection for them. For example, if I had an extranet site for partners, I could hand out urls to the partners with the appropriate identity provider pre-selected for that partner.
There is simple implementation to carry the url over from the original SharePoint url. Extending from Steve's blog, instead of processing the begin request, process the end request event. In the end request event, check for the whr parameter on the request, and if it is present on the request, but not present on the redirect url, then add it. It's pretty much that simple. Below is example code:
public void EndRequest(object sender, EventArgs e)
{
try
{
//get the app and context sources
HttpApplication application = (HttpApplication)sender;
HttpContext context = application.Context;
if (context.Response.IsRequestBeingRedirected == true &&
context.Request.QueryString.AllKeys.Contains("whr"))
{
string whr = "whr=" + context.Request.QueryString["whr"];
string redir = context.Response.RedirectLocation;
if (!redir.Contains(whr))
{
redir = redir + (redir.Contains("?") ? "&" + whr : "?" + whr);
context.Response.RedirectLocation = redir;
}
}
}
catch(Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
I've attached a modified version of the project from Steve's original blog post that implements this approach. This is only a sample. I'd definitely take time to make it a bit more robust for a production implementation. Also I've put in some pre and post build steps to automatically register and unregister the assembly from the gac - if things aren't working, then the exe referenced may be in a different location. Finally make sure you follow the instructions in the project. Steve's original instructions still hold.
Hope this helps.