I have a couple of questions regarding SignalR Core authorization on the server side;
My server is written in ASP.NET Core, and it uses SignalR CORE for sending notifications to users.
1) If a client has connected with an options object containing an AccessTokenProvider and the access token changes -- does the server re-authorize the user even when using a long-running connection like a socket? Ie does SignalR create a new User object on the server side when the client's access token changes, while a connection is alive? If not -- how should this be handled?
2) On the client side - if a connection is aborted either from server side or by the client requesting a stop, does DisposeAsync() have to be called and a new connection object created, or can the previous one be reused safely without disposing it?
Thanks.
1) When token expires, you will need to refresh the token. To do that you will need to abort the connection and make it again. This is stupid but server will drop the connection when token will be expired.
2) If it is manually aborted, the object is disposed and the new connection will be new. If connection will drop for like, internet connection issues, with automatic reconnect, it will have the same instance and maintain same connection (and same connectionId).
Related
I get an exception from the SignalR client application(Exception thrown: 'System.IO.InvalidDataException' in Microsoft.AspNetCore.SignalR.Protocols.Json.dll).
This is happening when the signalR Hub send messages to a channel that the client is not configured to listening on. The functionality that i want is described below:
The Signalr server Hub sends messages into channels like "Test1","Test2","Test3".
The SingalR client want to receive messages only for the Test1 channel because the particular user screen/page is associated only with the Test1 channel's messages. Then, the other two unhandled channels throws exceptions. So, the client needs to listening to all channels that the server sends to? I thought the On<>() method is meant for the client in order to subscribe to the channels where he is interested.
What am i missing?
Thanks in andvance!
Ran into this issue for a SignalR hub connection with angular client and .net 5.0 server. Below is the screen shot of the error details on the server side. In this instance I was sending a decimal as part of the json payload like "value": 8.3. The issue went away once I converted the decimal to a string like this "value": "8.3"
I setup SignalR in my application and it works very nice!
I have a Hub which is only supposed to push messages from the server to the client.
However, i can also call the method from the client back to the server, doing like this:
// this makes a call from the client to the server
$.connection.appplicationHub.server.productCreated();
Is there a way to prevent this?
This was a bad question.
To send messages from Server to Client, i don't need to create the method in the Hub class. Methods created in the Hub class are made for Client -> Server messages.
In order to send SignalR message to a specific user I create a Group for each user upon connection
public override Task OnConnected()
{
log.DebugFormat("Connected. Connection Id = {0} UserId = '{1}'", Context.ConnectionId, UserHelper.UserId);
Groups.Add(Context.ConnectionId, UserHelper.UserId);
return base.OnConnected();
}
Now when a message comes in I send it a group in the following way:
var hubContext = GlobalHost.ConnectionManager.GetHubContext<AlertsHub>();
foreach (var recipient in recipients)
{
hubContext.Clients.Group(recipient).broadcastAlertMessage("Group", msg);
}
That works fine when deployed to a server, but for some reason not working when i access the server through our company load balancer (Citrix Netscaler SDX 11500) but eventually hitting the same sole box.
There is no issue sending messages to all clients
hubContext.Clients.All.broadcastAlertMessage("All", msg);
Also i can keep the connection IDs internally and send messages to a specific client works
hubContext.Clients.Client(AlertsHub.UserToConnectionIdDict["admin"]).broadcastAlertMessage("trageted client", msg);
Why "Group" message doesn't work?
By default, a SignalR server is only aware of and will only send messages to clients connected directly to itself. This is because each SignalR server manages its own messages using its own message bus. Without special configuration, SignalR has no way to know there are other clients connected to a different SignalR server at the same global address.
Fortunately SignalR has scaleout providers that allow you to configure all your SignalR servers in such a way that they can communicate with each other by sharing single message bus.
This "Introduction to Scaleout in SignalR" should provide you with the info you need to get SignalR working properly behind a load balancer: http://www.asp.net/signalr/overview/signalr-20/performance-and-scaling/scaleout-in-signalr
I recently started using Signal R library in ASP.Net MVC 3 application. I am able to use Signal R to send message to client. But I noticed that if I logged in to the application from another browser, I get following error -
Exception type: InvalidOperationException
Exception message: Unrecognized user identity. The user identity cannot change during an active SignalR connection.
Server stack trace:
at Microsoft.AspNet.SignalR.PersistentConnection.GetConnectionId(HostContext context, String connectionToken)
at Microsoft.AspNet.SignalR.PersistentConnection.ProcessRequest(HostContext context)
at Microsoft.AspNet.SignalR.Owin.CallHandler.Invoke(IDictionary2 environment)
at Microsoft.AspNet.SignalR.Owin.Handlers.HubDispatcherHandler.Invoke(IDictionary2 environment)
at Microsoft.Owin.Host.SystemWeb.OwinCallContext.Execute()
at Microsoft.Owin.Host.SystemWeb.OwinHttpHandler.BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, Object extraData)
And in some instances, I started getting this error repeatedly. And event log get full in couple of minutes.
I override OnConnect, OnDisconnect functions in my hub class and initiated hub connection from the Java script.
SignalR does not allow a user's Identity to change while maintaining a connection. This is because SignalR's connectionToken acts as an anti-xsrf token.
It appears that your "other" browser must actually be just another browser window. When you log in to the application in another window, it changes your ASP.NET forms authentication cookie (and therefore your Identity) in both browser windows.
Any ongoing SignalR connections will run into the aforementioned InvalidOperationException when they attempt to reconnect after the browser's auth cookie has changed. SignalR may attempt to reconnect quite frequently if the long polling transport is being used. Once the reconnect fails due to the exception, SignalR will repeatedly try to reconnect for about 40 seconds after which SignalR will give up and disconnect.
We are building an app which will send messages to the browser using SignalR. The user may have multiple browser instances open and we would like each message to be sent to the appropriate browser. Our understanding is that the ClientId ConnectionId would allow us to do this. The issue we're running into is accessing the ClientId ConnectionId, or SessionId, at the appropriate times in the codebase. Here's our scenario:
A MVC Action executes and, as part of that processing, a call to a Biztalk endpoint is made. The Biztalk execution is out of process (from the point of view of the MVC Action) and doesn't return when completed. This is by design. To notify the MVC application that it has completed, Biztalk sends a message to the MVC application's SignalR hub by calling the /myapp/signalr endpoint. The message is received by SignalR and then should be routed to the appropriate browser instance.
Since the message to SignalR is being sent by Biztalk, and not the MVC application, the ClientId of the connection to SignalR is not the one that identifies the browser instance that should receive the message. So what we are attempting to implement is somethign similar to the Return Address pattern by including the ClientId ConnectionId of the browser instance that initiates the Biztalk call in the message to Biztalk. When Biztalk sends its message to SignalR one of the contents is that original ClientId ConnectionId value. When SignalR processes the message from Biztalk it then can use the ClientId ConnectionId included in the message to route that message to the appropriate browser instance. (Yes we know that this won't work if the browser has been closed and re-opened and we're fine with that.)
The problem we face is that when initially sending the message to Biztalk from our MVC Action we cannot access the ClientId ConnectionId as it's only available in the Hub's Context. This is understandable since the MVC Action doesn't know which Hub context to look for.
What we have tried in it's place is to pass the SessionId through the Biztalk message and return it to SignalR. This solves the problem of including the browser instance identifier in the Biztalk message and returning it to SignalR. What it introduces is the fact that when a client connects to the Hub we cannot access the Session (and thus the SessionId) in the Hub's OnConnect method.
David Fowler posted a gist that reportedly shows how to make readonly SessionState accessible in a Hub but it doesn't work. (https://gist.github.com/davidfowl/4692934) As soon as we add this code into our application messages sent to SignalR cause a HTTP 500 error which is caused by SignalR throwing the following exception.
[ArgumentNullException: Value cannot be null.Parameter name: s]
System.IO.StringReader..ctor(String s) +10688601
Microsoft.AspNet.SignalR.Json.JsonNetSerializer.Parse(String json, Type targetType) +77
Microsoft.AspNet.SignalR.Json.JsonSerializerExtensions.Parse(IJsonSerializer serializer, String json) +184
Microsoft.AspNet.SignalR.Hubs.HubRequestParser.Parse(String data) +101
Microsoft.AspNet.SignalR.Hubs.HubDispatcher.OnReceived(IRequest request, String connectionId, String data) +143
Microsoft.AspNet.SignalR.<>c__DisplayClassc.<ProcessRequest>b__7() +96
Microsoft.AspNet.SignalR.<>c__DisplayClass3c.<FromMethod>b__3b() +41
Microsoft.AspNet.SignalR.TaskAsyncHelper.FromMethod(Func`1 func) +67
No matter the mode that we set SessionStateBehavior (as shown by David Fowler's gist) we either get this exception when sending a message to the Hub or SessionState is null when we are in the Hub's OnConnect.
So, after all that pre-amble, what we are asking is how do people update the appropriate client when working with this type of disconnected messaging in SignalR?
If you're looking to send data to clients outside of a normal request to a hub then I'd recommend having a static Concurrent Dictionary on your hub that manages your users and maps them to corresponding connection Id's.
With this approach you can send to any user at any point based on their mapped Connection Id. Therefore when sending your data to Biztalk all you need to do is send your user id (created by you) and then when the data flows back to SignalR you can lookup the ConnectionId (if one exists) for that given user id.
Lastly, you can manage your user mappings by adding users to your concurrent dictionary in OnConnected, adding only if they are not there in OnReconnected, and removing in OnDisconnected.