I have a WCF service configured to use custom UserName validation via the overridden Validate() method of the System. IdentityModel.Selectors. UserNamePasswordValidator class.
When the validation fails, I throw a SecurityTokenValidationException.
The way it was built, in my client, I'm receiving a CommuinicationException with the message: The remote server returned an error: NotFound.
What's the best way to handle this exception in the client side?
My validation code:
if (user == null || (Environment.TickCount - user.LastCall) > 300000)
{
if (!LoginUser.ValidateUser(userName, password))
{
throw new SecurityTokenValidationException("Usuário/Senha inválido");
}
}
Throwing an exception from a service will (I believe) fault the channel, and the client will receive a a non-descriptive message. This is by design - WCF "hides" exceptions on the service side.
To return an error to the client so the client can handle it you should use a FaultException. You can specify a FaultContract for the operation and that will be returned to the client.
Do a search on error handling in WCF and you should get plenty of examples. I'm short on time or I'd add some, but hopefully this will get you pointed in the right direction.
Edited to add
Change the line
throw new SecurityTokenValidationException("Usuário/Senha inválido");
to
throw new FaultException("Usuário/Senha inválido");
Throwing the FaultException will prevent the communication from being faulted - the client can then receive the exception, know it was due to validation failure, and decide what to do from that point (retry, prompt the user to reenter credentials, etc).
This is a very simple change and the example is to illustrate the point. The links I posted below will give more detailed information.
How are you identifying the user object that you check the last call on? Are you using a LINQ Single call? if so this will throw an exception if the user is not found and so the server will abort authentication at that point rather than with the SecurityTokenValidationException which I think would give you the behavior you are seeing
Related
We have an WCF service, using webhttp binding. Users get authenticated, and then a method is called. In the method, we check a variety of settings associated with the user and some information specific to the request before knowing if the user is authorized to make the call.
Since this is WCF, I think I should be throwing a FaultException of some sort, but it's not clear if there is best practices.
My thoughts are that once I know what exception I will be throwing, I'd add a IErrorHandler which would set the headers correctly to 403.
Two questions:
1) Is there a standard FaultException for unauthorized requests? i.e. the equivalent of the http status code of 403?
2) Should I be able to handle the exceptions that I'll be throwing and change the response code to 403? Will I be able to pass through a custom error message? I've seen some posts that setting headers using the operation context in a catch does not get propagated to the client.
Ideally I'd be able to set the status to 403 with additional information like "You must be part of the administrators group to add a user"
Because you're using webhttp binding, traditional WCF fault management is not pertinent here and it's better to use WebFaultException and WebFaultException<>.
Public string MyOperation()
// Operation logic
// ...
throw new WebFaultException<string>("You must be part of the administrators group to add a user", HttpStatusCode.Forbidden);
}
As you think, it's very important to use standard HTTP status codes when developping an HTTP (REST-like) service.
It's been my experience that throwing fault exceptions, at least with wshttpbinding and basichttpbinding, can cause your web service to fail, so I don't recommend that approach.
If you want to send a message back to unauthorized users, just send an HTML response, setting the status to any one of the 400 responses that seem appropriate.
But from experience, fault exceptions, even if they're a controlled response to user actions and not from an actual processing error, will cause your web service to fail. I think they should be reserved genuine processing exceptions.
I went ahead and derived custom exceptions from FaultException, and then added an IErrorHandler to set the appropriate headers.
This seemed to be the best of both worlds. The code only throws exceptions derived from ones used in WCF, and all the handling specific to http binding is done via an IErrorHandler outside the business logic.
I have a WCF service with several operation.
Each operation has the 'PrincipalPermission' tag, something like this :
[PrincipalPermission(SecurityAction.Demand, Role = "Administrator")]
public ProductsDto GetAllProducts()
{
// Do operation here ...
}
Problem is - if the user is not part of the role 'Administratir' - the service throws the exception
Request for principal permission failed
This corrupts the client's channel to a 'Faulted' state.
I want to be able to catch this somehow and send the client a 'Fault' message,
so that the client knows he tried to do something he shouldn't, without faulting the channel !
I tried using a 'try-catch' block inside the operation, but it didn't help.
The exception occurs 'outside' the operation itself.
How can I solve this ?
There's a special interface IErrorHandler that you can implement and hook up with the WCF service to handle the exception. Using IErrorHandler will let you handle both the security exceptions and any exceptions thrown by the serialization code.
Another note on the client's channel getting into a faulted state: I'd recomment creating a new client object for each unit of work you perform from the client. Reusing the client object opens up for this kinds of problems.
When a FaultException is returned from my WCF service, I need to Abort the channel instead of Closing it. Both my client and service work fine with this approach but after implementing IErrorHandler on the service and logging any exception, I can see that calling Abort on the client causes the service to log:
System.ServiceModel.CommunicationException: The socket connection was aborted...
I do not want to pollute my service logs with this information and only want to log service related errors. I know I can obviously stop logging any CommunicationExceptions but my service is also a WCF client for other services and CommunicationExceptions raised by these services should be logged.
How can I stop it doing this?
As nobody else has answered the question (Tomas's answer was not relevant), I asked a few experts in the field. Unfortunately, there is no nice way of stopping this and the best that they could come up with was to add logic in IErrorHandler to not log CommunicationExcepions with a message starting with 'The socket connection was aborted'. Not very elegant, but it does work.
The problem is that you get an exception that covers your underlying exception if you get an exception when calling dispose wich is possible. I wrote a wrapper to deal with scenarios like this, you can read about it on my blog: http://blog.tomasjansson.com/2010/12/disposible-wcf-client-wrapper/
The idea is that you have a wrapper around your channel that deals with the scenario if the dispose method throws an exception.
A small example of how you should use my wrapper:
public class ClientWrapperUsage : IYourInternalInterface
{
public IList<SomeEntity> GetEntitiesForUser(int userId)
{
using(var clientWrapper = new ServiceClientWrapper<ServiceType>())
{
var response = clientWrapper.Channel.GetEntitiesForUser();
var entities = response.YourListOfEntities.TranslateToInternal();
return entities;
}
}
}
Here I have assumed that it existing an extension method for a list that contains the entity that is returned by the service, then you use that method to translate it to internal entities. This is 100 % testable, at least I think :). Just moch the interface IYourInternalInterface everywhere you wan't to fake the service.
I created a custom Membership Provider which is now working in production just fine validating my WCF calls.
I do have an issue every now and then that for some unknown reason my provider cannot validate the user. In those cases I do not want the ValidateUser function to just return false, so I thought of throwing and excception with a little more help (not too much, just a little).
My problem is, even though I am throwing a ProviderException the client always gets a MessageSecurityException with no helpful info... just the good old:
"An unsecured or incorrectly secured fault was received from the other party. See the inner FaultException for the fault code and detail."
with "An error occurred when verifying security for the message." in the Inner.
How can I get the message I am throwing in my own ValidateUser method? what about a custom behavior?
Thanks in advance
I found this post that led me to a solution, I'm not sure if it is the right move
Properly catch SecurityTokenException from a WCF UserNamePasswordValidator
Basically I have to throw a FaultException which I'm not really happy abou because my Membership Provider implementation is WCF agnostic (or at least was until now) now it knows about FaultExceptions :(
Is there a better solution out there?
I'm hosting some SOAP services with WCF. How can I turn off these services via config for the purposes of maintenance, etc., and provide a friendly message to the service consumer with something like "The service you've requested is down for maintenance."?
You would have to have a second service, that offered the same interface, same methods etc., that would all return that friendly message instead of a real result.
That might get a bit trickier when those service methods don't just return a string but a complex data object - where do you put that "friendly" message??
In reality I think this cannot really be done - since your services typically aren't "seen" by actual people, you cannot just put up an app_offline.htm file or anything like that.
Try to have as little downtime as possible, by e.g. setting up your new version of the service on a new port and testing it there, until you're confident enough to switch over.
With WCF, it's mostly an exercise of updating / copying around the appropriate config, so your service should never really be unavailable for any extended period of time (hopefully!).
If you really must, what you could do, is just have a replacement service that will always throw a FaultContract<ServiceDownForMaintenance> - but then all the clients calling your service would have to know about this and they would have to handle this case and present an error or information message. Your service can't really provide that...
How about this: create a custom ServiceBehavior to intercept my incoming requests to the service. Then, have the custom behavior check a user-defined flag in my config file, something like <add key="IsMyServiceUp" value="true" /> and if that value returns as false then throw a ServiceException with my friendly message and HTTP code of 503 - Service Unavailable.
Does that sound reasonable? Then all I have to do is change the flag in my config file to specify where the service is up or down.
Okay, so I've created a new Custom Behavior that implements IOperationBehavior. In the Validate method, I've got
public void Validate(OperationDescription operationDescription)
{
bool isServiceUp = Boolean.Parse(ConfigurationManager.AppSettings["IsOrderServiceUp"].ToString());
if (!isServiceUp)
{
throw new ServiceException(ServiceErrorCode.Generic_Server_Exception,
ServiceErrors.Generic_Server_Exception,
SoapFaultCode.Server);
}
}
The other implemented methods ApplyClientBehavior, ApplyDispatchBehavior and AddBindingParameters are all empty.
I have decorated one of my service operations with [ServiceStatusValidation] which is the class name of my custom behavior.
When I start the service and navigate to the operation with this decoration, I do NOT get the exception I've thrown. SOAP UI shows nothing as returned in the response pane, and my consuming REST facade gives a generic 400 error with The exception message is 'The server did not provide a meaningful reply; this might be caused by a contract mismatch, a premature session shutdown or an internal server error.'.
Any ideas? Should I be doing this logic in one of the other methods that I didn't implement instead of the Validate method?