Extending the Error Diagnostics of ADFS
Introduction
When working with ADFS and an external partner it can sometimes be difficult to determine why errors occur. There are some helpful pages describing how to setup ADFS debug/trace at http://blogs.msdn.com/b/card/archive/2010/01/21/diagnostics-in-ad-fs-2-0.aspx and http://technet.microsoft.com/en-us/library/adfs2-help-how-to-enable-debug-tracing(WS.10).aspx but this does not always give you enough information to determine what is going wrong or why something is not working. This article provides some more tips for doing diagnostics with ADFS.
Extensions to debug/trace
The debug/trace capabilities with ADFS simply enable you to get a deeper level of detail about the events occurring in ADFS. This can be helpful but it can quickly overwhelm you because of all of the other information provided in the logs. Additionallly, when the diagnostic logs are shown in Event Viewer you are provided with a forward-only view of the logs which can be tedious to work with. The links above give you a way of filtering the Event Viewer logs from within the Event Viewer but this can take a long time to execute. A better way is to use a script to get only the events you want. The Powershell script below takes the error guid shown on error pages and filters the diagnostic log so you only get back the errors you really want to see:
param([string]$errorID)
$query = '<QueryList>
<Query Id="0" Path="AD FS 2.0 Tracing/Debug">
<Select Path="AD FS 2.0 Tracing/Debug">*[System[Correlation[@ActivityID=''{@ReplaceActivityID}'']]]</Select>
</Query>
</QueryList>';
$query = $query -replace "@ReplaceActivityID",$errorID
Get-WinEvent -LogName "AD FS 2.0 Tracing/Debug" -MaxEvents 100 -Oldest -FilterXPath $query| Format-List -Property Id, Message
Something to be aware of is that this information should not be displayed publicly without being further filtered because the error information can contain sensitive information. Some examples of sensitive information can be a rule store connection string. There are also some events that do not really give you much contextual knowledge about an error and are not that useful. The following Powershell block shows how to filter out some events that you may not want to display:
# Filtered out event codes:
# 24 - Rule-store connection string
# 999 - Wif event logging verbosity details
Get-WinEvent -LogName "AD FS 2.0 Tracing/Debug" -MaxEvents 100 -Oldest -FilterXPath $query | Where-Object {
$_.Id -ne 24 -and $_.Id -ne 999
} | Format-List -Property Id, Message
After configuring the debug/trace and enabling quicker access to the source error message, it is still possible to get a confusing error message back from ADFS. Next I will show a technique for doing custom validation on the SAMLResponse from within ADFS.
Custom Validation of the SAMLResponse
One really helpful aspect of ADFS is that there is a code-level capability in the ASPX pages that ship with ADFS. These can be helpful for adding code to use a default home realm or alter the behavior of ADFS for custom reasons. These also provide a simple way to do custom validation on the received SAMLResponses. This can also be used for manipulating the SAMLResponse prior to being consumed by ADFS for some capability that is not provided with ADFS.
Typically there will be several standard things to check for with incoming SAMLResponses and ADFS will do this as well but may not always give you a human readable or understandable error message. Some examples of these are:
- Check for signature as ADFS is configured (correct algorithm, correct certificate used)
- Check for encryption as ADFS is configured
- etc...
Checking for these typical details in a received SAMLResponse will help you to identify the problem so that your partner can handle the problem quickly.
One common request is to be able to log the SAMLResponse to a database for every request received by ADFS. To do this, simply add some code to the global.asax.cs file such as the following snippet:
public void Application_BeginRequest()
{
HttpRequest request = HttpContext.Current.Request;
HttpResponse response = HttpContext.Current.Response;
if (!String.IsNullOrEmpty(request["SAMLResponse"]))
{
SaveSamlResponseToDB(request["SAMLResponse"].ToString());
}
}
After doing this you can manipulate the SAMLResponse and do custom validation on it. This provides a mechanism for doing additional validation beyond what ADFS current does and is very helpful for various testing scenarios.