-
Notifications
You must be signed in to change notification settings - Fork 44
Custom Authentication Process
The actions to execute after a successful federated SignOn or Logout can be customized as necessary to accommodate special use cases. Understanding the [Default Authentication Process](Default Authentication Process) is a good idea before extending this process.
The most common use case is to replace the FormsAuthenticationAction with a custom implementation. For instance, if the identity provider does not provide a NameIDFormat which can be easily mapped to a local user, but does provide attributes that are, then the FormsAuthenticationAction can be replaced with a custom MyAuthenticationAction.
There are two steps to do this.
Creating a new action is as simple as inheriting from SAML2.Actions.IAction and implementing your custom authentication scheme. Below is an example of such an implementation, which identifies local users by a RequestedAttribute that the identity provider has passed back instead of the default <Subject>.
public class MyAuthenticationAction : IAction
{
#region Implementation of IAction
private string _name = "MyAuthentication";
public string Name
{
get { return _name; }
set { _name = value; }
}
/// <summary>
/// Action performed during login.
/// </summary>
/// <param name="handler">The handler initiating the call.</param>
/// <param name="context">The current http context.</param>
/// <param name="assertion">The saml assertion of the currently logged in user.</param>
public void SignOnAction(AbstractEndpointHandler handler, HttpContext context, Saml20Assertion assertion)
{
// Ensure that the necessary attributes have been returned
if (!Saml20Identity.Current.HasAttribute("identifier"))
{
throw new ArgumentException("SAML Response did not contain the identifier attribute.");
}
// Ensure that there is a value passed back for identifier
if (!Saml20Identity.Current["identifier"].Any(x => x.AttributeValue.Length > 0))
{
throw new FormatException("SAML Response contained the identifierattribute, but did not include a value.");
}
var identifier = Saml20Identity.Current["identifier"].FirstOrDefault(x => x.AttributeValue.Length > 0).AttributeValue.FirstOrDefault();
var user = LookupUserByIdentifier(identifier);
// Check for existing user
if (user == null)
{
context.Response.Redirect(url.Action("AccessDenied", "Error"));
}
// Sign in
FormsAuthentication.SetAuthCookie(user.Name, false);
}
/// <summary>
/// Action performed during logout.
/// </summary>
/// <param name="handler">The handler.</param>
/// <param name="context">The context.</param>
/// <param name="IdPInitiated">During IdP initiated logout some actions such as redirecting should not be performed</param>
public void LogoutAction(AbstractEndpointHandler handler, HttpContext context, bool IdPInitiated)
{
FormsAuthentication.SignOut();
}
#endregion
...
}
To enable the custom authentication action instead of the FormsAuthenticationAction, the [<actions>](Actions Element) element must be added / modified to override the defaults.
<saml2>
...
<actions>
<clear/>
<action name="SetSamlPrincipal" type="SAML2.Actions.SamlPrincipalAction, SAML2" />
<action name="MyAuthentication" type="MyProject.MyAuthenticationAction, MyProject" />
<action name="Redirect" type="SAML2.Actions.RedirectAction, SAML2" />
</actions>
...
</saml2>
In summary, it easy to override the default behavior to accommodate custom authentication workflows. In this case, the <Subject> is completely ignored, and the attributes passed back are used to find a local identity. This action could just as easily be extended to automatically create a user if they don't exist, etc.