WCF UserName authentication and fault contracts
Asked Answered
S

2

6

I have a WCF service configured to use custom UserName validation via the overriden Validate() method of the System.IdentityModel.Selectors.UserNamePasswordValidator class.

All methods of the contract have been decorated with the FaultContractAttribute to specify a custom SOAP fault as being returnable.

When throwing FaultException<T>, where T is the type specified in the FaultContractAttribute, everything behaves as expected and I get the custom fault in the response XML.

However, if I try and throw FaultException<T> in the overriden Validate() method of the username authentication class, I get a generic SOAP fault with the following reason:

"The creator of this fault did not specify a Reason."

However, if I change the code to throw the general SOAP fault as in:

throw new FaultException("Authentication failed.");

I will at least get "Authentication failed." in the reason element.

My questions are:

  • Why aren't the FaultException<T> exceptions treated the same if they're thrown in the Validate() as they are within the service implementation?
  • Is it possible to have exceptions thrown in the Validate() method conform to the FaultContractAttribute specified on the contract methods?

Any help greatly appreciated. My own guess is that the authentication comes before the message is associated with any method of the contract, and therefore, is not associated with the FaultContractAttribute, but any article confirming this and giving a workaround would be very useful.

Tali

Scout answered 24/8, 2009 at 14:28 Comment(1)
Thanks for posting this question. Any solutions? I'm still looking for an answer in March 2013.Designate
C
0

It's a bit annoying but I got round it by doing this:

SecurityTokenValidationException stve 
  = new SecurityTokenValidationException("Invalid username or password");
throw new FaultException<SecurityTokenValidationException>(stve, stve.Message);

Including the message additionally means that you don't get the silly "did not specify a reason" message.

Coonskin answered 25/8, 2010 at 14:57 Comment(0)
W
0

The problem is that the custom validation code is running outside of the context of any specific OperationContract, so there is no FaultContract is place for WCF to handle. So the short answer is no, you cannot get the exceptions thrown from your custom validator to honor the FaultContract.

You have a few options here. The one I prefer is to throw the non-generic FaultException and provide a pre-determined FaultCode; this way my catch blocks can differentiate contract faults from "plumbing" faults. Note that any exception you throw from a custom validator should come back as a MessageSecurityException, as shown below:

// Custom Validator:
public override void Validate(string userName, string password)
{
  throw new FaultException(
    "Invalid username or password.", 
    new FaultCode("AUTHENTICATION_FAILURE"));
}

// Client Code:
try
{
  client.DoSomething();
}
catch ( MessageSecurityException ex )
{
  var inner = ex.InnerException as FaultException;
  if (inner != null && inner.Code.Name.Equals("AUTHENTICATION_FAILURE"))
  { 
    // Security failure.
  }
}
catch ( FaultException<SomethingFault> ex )
{
  // Exception from the method itself.
}
Woodprint answered 20/4, 2012 at 13:57 Comment(5)
I tried the above mentioned but i get a null value when i try to cast ex.innerException as FaultException. Any help would be appreciated.Slushy
Well, what type of exception is the inner exception? If its not a fault exception then your problem isn't in the service.Woodprint
I throw a Fault Exception type in the server but in the client it says the inner exception is of type system.net.webexception and I don't have the msg I throwed from server .Slushy
Are you sure you're using a WCF "service reference", and not trying to use WCF over SOAP/XML (a "web reference")? I belive the behavior you're describing is what happens when a WCF service exception is thrown to a legacy Web Services client.Woodprint
Am not using service reference i tried using CreateChannel class to communicate with the service.Slushy

© 2022 - 2024 — McMap. All rights reserved.