Can a UserNamePasswordValidator throw anything other than a MessageSecurityException? - c#

I have a WCF service hooked up with a UserNamePasswordValidator through my web.config, no problem there. In my validator, I override Validate, check the credentials and throw a FaultException if necessary.
Example:
public class CredentialValidator : UserNamePasswordValidator
{
public override void Validate(string userName, string password)
{
if (userName != "dummy")
{
throw new FaultException<AuthenticationFault>(new AuthenticationFault(), "Invalid credentials supplied");
}
}
}
If I consume this service myself in a .NET application and provide invalid credentials, a MessageSecurityException is thrown with the following message:
"An unsecured or incorrectly secured fault was received from the other party. See the inner FaultException for the fault code and detail."
The FaultException I had expected is the InnerException of the MessageSecurityException.
Is there any way to have the client receive just the FaultException?
The MessageSecurityException isn't particularly descriptive concerning the true cause of the exception (a quick search on SO yields a variety of problems including server/client time sync..), and since a third party will be consuming the service, I'd like to be as clear as possible.

I had the very same issue some months ago and after some research I came to the conclusion that you may throw whatever you want from validator code, but client will still get MessageSecurityException, which contains no useful information at all.
We had to however give the client to know what actually happened -
1. wrong username/password
2. password expired, change needed
3. some other custom application specific states
So we changed CredentialValidator logic in a way that it throws exception only in case 1. In other cases it would actually allow the real WCF method to be called, but there we would check also for password expiration etc. and in case of some problems throw FaultException already from method body.
In our case this worked well for only service with this type of validation was log-in service - so the client would always know why exactly he wasn't authenticated.

From custom password validator you can return FaultCode that describe what's wrong:
throw new FaultException("Invalid user name or bad password.", new FaultCode("BadUserNameOrPassword"));
throw new FaultException("Password expired.", new FaultCode("PasswordExpired"));
throw new FaultException("Internal service error.", new FaultCode("InternalError"));

Throw the error as MessageSecurityException with inner exception as FaultException
public override void Validate(string userName, string password)
{
var isValid = ValidateUser(userName, password);
if (!isValid)
{
throw new MessageSecurityException("Userid or Password is invalid", new FaultException("Userid or Password is invalid"));
}
}

Related

HangFire Queued Emails "Succeeding" But not Sending

I can send emails using the method when not using hangfire.
When the method is called from hangfire it is shown as succeeded but the email doesnt send.
BackgroundJob.Schedule(
() => Email(Subject),
TimeSpan.FromMinutes(1)
);
public void Email(string Subject)
{
try
{
//Initialize WebMail
WebMail.SmtpServer = "relay.example.com";
WebMail.SmtpPort = 587;
WebMail.SmtpUseDefaultCredentials = false;
WebMail.UserName = "xxxx";
WebMail.Password = "xxxx";
WebMail.From = "\"Reports\" <Reports#example.com>";
//Send Email
WebMail.Send(to: "client#example.com",
subject: Subject,
body: "Test Email"
);
}
catch (Exception e)
{
}
}
The username, password, and relay service have been removed for security as has the actual email addresses.
I have gone and removed the try/catch blocks to see if there are any errors produced.
Normally, It succeeds as before but occasionally it will throw the following error.
System.NullReferenceException
Object reference not set to an instance of an object.
System.NullReferenceException: Object reference not set to an instance of an object.
at System.Web.WebPages.Scope.AspNetRequestScopeStorageProvider.get_RequestScopeInternal()
at System.Web.WebPages.Scope.AspNetRequestScopeStorageProvider.get_CurrentScope()
at System.Web.Helpers.WebMail.set_SmtpServer(String value)
at Email(String Subject)
I have then attempted the version that allows for type passing.
BackgroundJob.Schedule<WebMail>(
() => Email(Subject),
TimeSpan.FromMinutes(1)
);
However that throws a compiler error about using a static type.
Thanks for any help guys.
EDIT:
Still open to better options. However I did find a cheeky work around. I was able to get this to work by creating a method that fires a post request to a page to actually send the email. Whereas Hangfire just activates the post request.
EDIT: NVM workaround was a false positive due to an extra line that was left in from testing
The WebMail class is intended for usage in ASP.NET Web Pages context. When used outside that context it probably doesn't have everything it needs, and thus you will get a NullReferenceException.
Instead, use the SmtpClient class.

Error thrown when calling HubConnection.Start: StatusCode: 401, ReasonPhrase: 'Incoming principal is null'

I am using SignalR with my WebApi application and that works fine: the client connects to the hub using WebSocket transport and the user is authenticated with some custom middleware authentication.
I am trying to connect to this hub from another backend application (.NET client) and to do this I make a hub connection and create a hub proxy, then invoke the hub method:
string auth = "someEncryptedValue"
HubConnection hubConn = new HubConnection("myUrl");
hubConn.Headers.Add("myauthtoken", auth);
IHubProxy proxy = hubConn.CreateHubProxy("hubName");
Task t = Task.Run(() => hubConn.Start(new LongPollingTransport()));
t.WaitAndUnwrap // An extension method
hubProxy.Invoke("SendMessage", message);
The exception is thrown when t.WaitAndUnwrap() is called (the extension method is not the issue). I had this incoming principal is null issue before I added the http header token (which, of course, is not actually the literal string "someEncryptedValue"). So I added that here and then I added a custom authorization class for the hub back in my other application:
public class HeadersAuthAttribute : AuthorizeAttribute
{
public override bool AuthroizeHubConnection(HubDescriptor hubDescriptor, IRequest request)
{
string auth = request.Headers["myauthtoken"];
if (auth == "someEncryptedValue") //again, this is actually more complex than shown here
{
return true;
}
else
{
// Need to check if the incoming principal is authenticated in case
// the connection to the hub is being made the normal way through the
// WebApi instead of the proxy using http headers
return request.User?.Identity?.IsAuthenticated ?? false;
}
return false;
}
}
And of course my hub has the headers attribute:
[HeadersAuth]
public class MyHub : Hub
{
// Hub logic
}
However, after running this I still get the incoming principal is null error. Then I read that you cannot use custom http headers with WebSockets, which is why I put new LongPollingTransport() in the hubConn.Start call above. But that didn't seem to change anything, at least not the error I get.
Does anyone know what can be going on? It'd be nice if I could debug the code with the actual hub and authorization so I can see what's going on when the hubConn.Start call is made. Is there a way I can check the http header is set correctly and fetched correctly? If the error is about the incoming principal being null, is authentication even the issue? Could it be another part of the code where it's trying to find the user? I'm not sure what to do about that since this hub connection is being made from a .NET client. Also, I know the HeadersAuthAttribute class is being called and is used correctly when connecting to the hub normally using WebSockets since it goes into the else case where it checks that the IIdentity is authenticated.
Just to add a bit more of what I've tried:
I made the auth string token purposefully wrong to see if I get a different error, but I still get incoming principal is null.
I realized that my OnConnected() override method for my hub calls a method that tries to use the incoming principal with this.Context.User without checking if it's null. I removed that and anything else that tries to use the incoming principal in the hub, but unfortunately it still gives the same error.
I figured out what was wrong. It wasn't to do with the hub authentication, as I suspected as I was finishing this post. What was happening was my Startup file makes the call app.ConfigureAuth, but this eventually leads to some custom authorization in the OWIN middleware pipeline that checks if the incoming principal is null. I've taken that out for SignalR connections.
It should also be noted that after I got that working I couldn't make hub method calls from the proxy because it wasn't authorized to do so. To fix that I added
public override bool AuthorizeHubMethodInvocation(IHubIncomingInvokerContext hubIncomingInvokerContext, bool appliesToMethod)
to the HeadersAuthAttribute class, which I got from here.

SignalR - authenticate windows forms user on windows forms server

I have a small project which contains a windows forms signalr client and a windows forms server. Clients can connect to server. Now, I want to implement a login system. I read some blogposts, articles and questions about this, but I didn't found a way to do it. I would like to use the signalr authentication system so I can use attributes such as [Authorize] because it's already there.
To use this, I need to validate the username and password. Can the client sends the credentials in header like this
Connection = new HubConnection(BaseEngine.ServerURI);
Connection.Headers.Add("Username", username);
Connection.Headers.Add("Password", password);
HubProxy = Connection.CreateHubProxy("ChatHub");
await Connection.Start();
and the server should validate those credentials somehow in a method and throw an exception if are not valid?
I've tried to use the builtin system, but no luck. I couldn't obtain the Context.User in OnConnected method. As a workaround, I've tried to send the username and password in header and validate them, but the OnConnected method cannot throw errors to client. I am sure the client has to have an auth cookie, but I really don't know how to add it.
Thank you!
It is never a good idea to send password to the server this way , it is better to send a token that the server can validate .
also SignalR has some authentication features read more about this here
I have found something like a workaround: first of all, I implemented an attribute, derived from AuthorizeAttribute used by SignalR. This implementation overrides the AuthorizeHubMethodInvocation method witch is called when a method that is decorated with this attribute is called. So, in this method, I'm checking that an Authorization Token is present in the header of the request and validate the information. The client has to add this header to connect to the server. It's the easiest method I have found so far, but it's still an workaround.
Implementation, server:
[AttributeUsage(AttributeTargets.Method)]
internal class CustomAuthorizeAttribute : AuthorizeAttribute
{
public override bool AuthorizeHubMethodInvocation(Microsoft.AspNet.SignalR.Hubs.IHubIncomingInvokerContext hubIncomingInvokerContext, bool appliesToMethod)
{
string token = hubIncomingInvokerContext.Hub.Context.Headers["AuthenticationToken"];
if (string.IsNullOrEmpty(token))
return false;
else
{
string decryptedValue = Encryptor.Decrypt(token, Encryptor.Password);
string[] values = decryptedValue.Split(';');
string userName = values[0],
deviceId = values[1];
bool b = ...check if it's ok...
return b;
}
}
}
Implementation, client:
ComEngine.Connection = new HubConnection(BaseEngine.ServerURI);
ComEngine.Connection.Headers.Add("AuthenticationToken", Encryptor.Encrypt(string.Format("{0};{1};{2}", BaseEngine.UserName, BaseEngine.DeviceId, BaseEngine.Password), Encryptor.Password));
try
{
await Connection.Start();
}
catch (Exception ex)
{
...
}

Pass SecurityException through multiple hops of WCF services

I've got an ASP.NET MVC/WebApi application that acts as a WCF client. My WCF services are authorizing via ClaimsPrincipalPermission and additionally throw a SecurityException if the combination of the current user and the data does not match (e.g. user is trying to access data from other user).
I catch this as a SecurityAccessDeniedException in my filters and return status code 403.
This however only works for communication that involves a single hop. If the service itself communicates with yet another service, then the SecurityAccessDeniedException gets turned into a FaultException before it reaches the web application.
Example flow:
Web client --> Service A [SecurityException] --> [SecurityAccessDeniedException] Web
Web client --> Service A --> Service B [SecurityException]
--> [SecurityAccessDeniedException] Service A [FaultException] --> [FaultException] Web
I tried implementing an IErrorHandler that handles the SecurityAccessDeniedException but I cannot just throw a SecurityException there.
What are my options to propagate the SecurityException up to the Web client?
Alternatively: how can I construct a message in my error handler, which the client then correctly interprets as a SecurityAccessDeniedException?
I'm now using an IErrorHandler which generates a corresponding FaultException. On the caller, this generates yet another SecurityAccessDeniedException, which I could then handle with the same IErrorHandler etc.
public void ProvideFault(Exception error, MessageVersion version, ref Message message)
{
if (error is SecurityAccessDeniedException)
{
var code = FaultCode.CreateSenderFaultCode(
"FailedAuthentication",
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
var faultText = new FaultReasonText(error.Message, CultureInfo.CurrentCulture);
var faultException = new FaultException(new FaultReason(faultText), code);
MessageFault messageFault = faultException.CreateMessageFault();
message = Message.CreateMessage(version, messageFault, "FailedAuthentication"); // the last parameter might not be semantically correct...
}
}
I got this idea by looking at http://leastprivilege.com/2007/11/01/wcf-and-securityaccessdeniedexception/

Where to store data in WCF request

I'm using a custom username and password validation for my WCF security. Now i would like to store the username during the request so that i can access it later in the method that is called. How do i do that?
Some sample code to describe my problem:
public class CustomUserValidator : UserNamePasswordValidator
{
public override void Validate(string username, string password)
{
if (username == "aaa" && password == "bbb")
{
// store username where i can get it in method called later.
return;
}
throw new SecurityTokenException("Unknown Username or Password");
}
}
Now the method that is being called:
public void WebServiceMethod()
{
Database.User.Single(c => c.Username == /* username from above */);
}
BR
Andreas
You would typically do this by issuing a custom "principal", which is done via IAuthorizationPolicy; IIRC, the username is made available to the auth-policy via the evaluation-context parameter. A general walkthrough to custom principals in WCF is here, however you may need to experiment a bit in Evaluate to find the incoming username in the evaluation context. In particular, if any of the keys is a "claims" dictionary, look at that. And look at the .Claims on the evaluation context - you should find a "claim" in their issued by CustomUserValidator with the username in it.
I have, however, done exactly what you describe in a previous job - and IIRC it worked fine, using the above page as my starting point.
Once you have issued a principal, it will be available, as normal, via:
string cn = Thread.CurrentPrincipal.Identity.Name;
To store data of any kind through a single WCF request, Darin Dimitrov suggests hooking up a simple IExtension<> helper class to the current OperationContext here: Where to store data for current WCF call?

Categories