Another common problem with external authentication is how to deal with sign in errors.
In active federation like WS-Trust there are well defined SOAP faults to communicate
problem to a client.
But with web applications, the error information is typically generated and displayed
on the external sign in page. The relying party does not know about the error, nor
can it help the user in any way.
The Access Control Service allows to post sign in errors to a specified page. You
setup this page in the relying party registration. That means that whenever an error
occurs in ACS, the error information gets packaged up as a JSON string and posted
to the page specified. This way you get structued error information back into you
application so you can display a friendlier error message or log the error.
I added error page support to my ACS2 sample, which can be downloaded here.
How to turn the JSON error into CLR types
The JSON schema is reasonably simple, the following class turns the JSON into an object:
[DataContract]
public class AcsErrorResponse
{
[DataMember(Name
= "context",
Order = 1)]
public string Context
{ get; set;
}
[DataMember(Name
= "httpReturnCode",
Order = 2)]
public string HttpReturnCode
{ get; set;
}
[DataMember(Name
= "identityProvider",
Order = 3)]
public string IdentityProvider
{ get; set;
}
[DataMember(Name
= "timeStamp",
Order = 4)]
public string TimeStamp
{ get; set;
}
[DataMember(Name
= "traceId",
Order = 5)]
public string TraceId
{ get; set;
}
[DataMember(Name
= "errors",
Order = 6)]
public List<AcsError>
Errors { get; set;
}
public static AcsErrorResponse Read(string json)
{
var serializer
= new DataContractJsonSerializer(
typeof(AcsErrorResponse));
var response
= serializer.ReadObject(
new MemoryStream(Encoding.Default.GetBytes(json))) as AcsErrorResponse;
if (response
!= null)
{
return response;
}
else
{
throw new ArgumentException("json");
}
}
}
[DataContract]
public class AcsError
{
[DataMember(Name
= "errorCode",
Order = 1)]
public string Code
{ get; set;
}
[DataMember(Name
= "errorMessage",
Order = 2)]
public string Message
{ get; set;
}
}
Retrieving the error information
You then need to provide a page that takes the POST and deserializes the
information. My sample simply fills a view that shows all information. But that’s
for diagnostic/sample purposes only. You shouldn’t show the real errors to your end
users.
public class SignInErrorController : Controller
{
[HttpPost]
public ActionResult Index()
{
var errorDetails
= Request.Form["ErrorDetails"];
var response
= AcsErrorResponse.Read(errorDetails);
return View("SignInError",
response);
}
}
Also keep in mind that the error page is an anonymous page and that you are taking
external input. So all the usual input validation applies.