SIgnalR: How to send exceptions to client on connect? - c#

In the hub i want to authenticate the clients based on Connection Header.
If the client is not allowed to connect, i want to throw an Exception with the detailed error.
But, if i throw an exception on the hub, the client only receives "(500) Internal Server Error"
public override Task OnConnected()
{
string clientKey = Context.Headers["clientKey"];
string version = Context.Headers["version"];
if (!this.isValid(clientKey, version))
throw new InvalidOperationException("SIGNALR: Invalid client");
return base.OnConnected();
}
What i have to do to send the exception properly?
Thanks!

When error is going to happen you can return that error or return your custom message to the client and based of that message you can redirect the client to Error page or display dialog that indicates that Error has happened.
Don't throw the error on the server side. Return the error on the client side and notify the user

To enable sending error details to clients you should enable this in your Hub configuration. add this line of code in your startup class.
var hubConfiguration = new HubConfiguration {EnableDetailedErrors = true};
app.MapSignalR(hubConfiguration);

Related

Connection error SASL error with QPID and AmqNetLite

I am not familiar with AMQP.
I try to connect a AmqpNetLite program to a Qpid server and I get the following exception :
Amqp.AmqpException: sasl-mechanisms(sasl-server-mechanisms:[CRAM-MD5,SCRAM-SHA-1,SCRAM-SHA-256])
Note : I am able to connect to a basic AMQP server, but not QPID.
Here is the code to create the connection:
Please tell where to start to fix my problem.
void Main(string[] args)
{
try
{
Address address = new Address("amqp://guest:guest#localhost:5672");
Connection connection = new Connection(address);
Session session = new Session(connection);
ReceiverLink receiver = new ReceiverLink(session, "receiver-link", "queue");
Message message = receiver.Receive();
receiver.Accept(message);
receiver.Close();
session.Close();
connection.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
Console.ReadLine();
}
The error would seem to indicate that the broker is not able to provide the client with a SASL mechanism that it supports. I think that AmqpNetLite only does ANONYMOUS, PLAIN and EXTERNAL but perhaps that's changed. You could look into your broker configuration and make one of those mechanisms available to the client and that will probably allow for a match and successful authentication. Or you could use an SSL connection which would then allow those SASL mechanisms to work and provide a bit of extra security for you connection.
The security section of the Broker-J documentation site should shed some light on this for you.

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.

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/

SignalR .NET Client not receiving messages

I have a .NET console application that I am practicing signalR with.
var hubConnection = new HubConnection("http://URL/signalr/");
var hub = hubConnection.CreateHubProxy("Hub");
hub.StateChanged += change =>
{
Console.WriteLine(change.NewState);
};
hub.Received += s =>
{
Console.WriteLine(s);
};
hub.On<string, string>("processMessage", (group, message) =>
{
Console.WriteLine(message);
});
await hubConnection.Start();
await hub.Invoke<string>("Subscribe", "New group");
I see the state changing from Connecting to Connected but I am not getting a "Received" event on the client when the server sends a message. The server is sending a group message as soon as the client subscribes and I can see the message being sent with the correct "New group" groupname, however I never receive the message on the client. I also do not receive the processMessage event when the server uses that method.
Server Code
private void CallBack(string group, string message)
{
Clients.Group(group).processMessage(group, message);
}
The other method on the server is Subscribe which just sets my inner server to use the CallBack method when it has data available to send to the client.
Edit
This works in Javascript it just doesn't seem to work in the .NET client.
Without full serverside code it's hard to say but I think this part is wrong
hubConnection.CreateHubProxy("Hub");
as argument you need the name of your hubclass on serverside. For example
hubConnection.CreateHubProxy("MyHub");
To get more informations on clientside why it fails you can temporary add the following to your HubConnection
hubConnection.TraceLevel = TraceLevels.All;
hubConnection.TraceWriter = Console.Out;
After adding this you will get further debuging informations in your output section in VS

Can a UserNamePasswordValidator throw anything other than a MessageSecurityException?

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"));
}
}

Categories