ADFS 2.0: Forms based and Windows Authentication on a single farm (without using Proxy)
Problem: A customer was interested in using both Forms Based and Windows Authentication for an internal SAAS application using ADFS.
By default, a single ADFS farm will only use either Windows Authentication (default) or Forms based.
Solution:
This article describes how you can activate both Forms based and Windows Authentication on a single ADFS farm.
This modification did not disrupt current ADFS Relying Party Trust configurations on the farm. Enabling this funtionality simply allows you to formulate specific URL paths that contain sign-on query strings. The URL path created will dictate the type of authentication used during an IDP initiated request.
This approach is documented in the MSDN article: http://msdn.microsoft.com/en-us/library/ee895361.aspx
Visit the c:/inetpub/adfs/ls directory.
Edit the IdpInitiatedSignOn.aspx.cs file.
- Add a using statement for additional query string parameters:
*using Microsoft.IdentityServer.Protocols.Saml;
* The beginning of the file should now look like this:
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
using System;
using System.Web.UI;
using System.Collections.ObjectModel;
using Microsoft.IdentityServer.Web;
using Microsoft.IdentityServer.Web.UI;
*using Microsoft.IdentityServer.Protocols.Saml;
*
2. Add string constants:
* const string IsPassiveQueryParameter = "IsPassive";
const string ForceAuthenticationQueryParameter = "ForceAuthentication";
const string AuthenticationContextComparisonQueryParameter = "AuthenticationContextComparison";
const string RequestedAuthenticationContextQueryParameter = "RequestedAuthenticationContext";
*
The file content should now look like this:
public partial class IdpInitiatedSignOn : IdentityProviderInitiatedSignonPage
{
const string RpIdentityQueryParameter = "loginToRp";
* const string IsPassiveQueryParameter = "IsPassive";
const string ForceAuthenticationQueryParameter = "ForceAuthentication";
const string AuthenticationContextComparisonQueryParameter = "AuthenticationContextComparison";
const string RequestedAuthenticationContextQueryParameter = "RequestedAuthenticationContext";*
const string IdpAsRpIdentifier = "self";
Collection<Control> _loggedInControls;
Collection<Control> _loggedOutControls;
3. Replace Page_Init method with modified code:
* protected void Page_Init(object sender, EventArgs e)
{
PopulateConditionalVisibilityControls();
RelyingPartyDropDownList.DataSource = RelyingParties;
RelyingPartyDropDownList.DataBind();
UpdateText();
string rpIdentity = Context.Request.QueryString[RpIdentityQueryParameter];
//
// If the query string specified a certain relying party, sign in to that relying party.
//
if (!String.IsNullOrEmpty(rpIdentity))
{
string decodedIdentity = Server.UrlDecode(rpIdentity);
if (decodedIdentity == IdpAsRpIdentifier)
{
rpIdentity = String.Empty;
}
SignOnRequestParameters parameters = new SignOnRequestParameters();
string isPassive = Context.Request.QueryString[IsPassiveQueryParameter];
if (!String.IsNullOrEmpty(isPassive))
{
parameters.IsPassive = Boolean.Parse(isPassive);
}
string forceAuthentication = Context.Request.QueryString[ForceAuthenticationQueryParameter];
if (!String.IsNullOrEmpty(forceAuthentication))
{
parameters.ForceAuthentication = Boolean.Parse(forceAuthentication);
}
string requestedAuthentication = Context.Request.QueryString[RequestedAuthenticationContextQueryParameter];
if (!String.IsNullOrEmpty(requestedAuthentication))
{
if (parameters.RequestedAuthenticationContext == null)
{
parameters.RequestedAuthenticationContext = new RequestedAuthenticationContext();
}
parameters.RequestedAuthenticationContext.References.Add(new Uri(requestedAuthentication));
string comparison = Context.Request.QueryString[AuthenticationContextComparisonQueryParameter];
if (!String.IsNullOrEmpty(comparison))
{
parameters.RequestedAuthenticationContext.Comparison = (AuthenticationContextComparisonType)Enum.Parse(typeof(AuthenticationContextComparisonType), comparison);
}
}
SignIn(rpIdentity, parameters);
}
}
*
4. Now you can formulate unique URLs for your IDP initiated sessions.
A. Normal SSO IDP link (this will allow the user to seamlessly sign-in):
https://sts.example.com/adfs/ls/idpinitiatedsignon.aspx?logintorp=https://saasvendors.example.com
B. IDP link with specified user name-password authentication query string (this will send the user to the forms sign in and after entering credentials it will send user into saas application):
Hope this helps others who need to satisfy this requirement.