SignalR client not sending updated cookies when calling hub methods - asp.net-core

Using JS SingleR client v7.0. Server side is ASP.NET Core V5.0.
When inspecting the server side I get the same cookie value from client for every hub method call equal to that of the cookie value received during the initial connection. This is true even though the cookie value on client has been updated and changed in the time between the connection creation and subsequent method calls to the hub.
If I reestablish the connection, the new cookie does get sent but then again it becomes stale as it is never updated on subsequent hub method calls.
From what I understand WebSockets should send the client cookie every time through it's header but seems to keep providing the old cookie. Is there some setting in SignalR that does some caching on the cookie per connection?

This is by design. The websocket connection is a single request, the connection cannot be updated per invocation because it's not an HTTP request.

Related

Does SignalR client send the authentication cookie for each hub request

I'm using signalr core and am expect to be sending and receiving messages with the client frequently over mobile, so I've been trimming off the fat to minimize my message sizes. One thing I've simply been curious about is, when making requests from a hub that requires authentication, does every request sent to the hub also sends it's asp.net core authentication cookie, or does the client only send the cookie once when they initiate their connection to the hub and then all subsequent requests no longer need a cookie?
does every request sent to the hub also sends it's asp.net core authentication cookie, or does the client only send the cookie once when they initiate their connection to the hub and then all subsequent requests no longer need a cookie?
The cookies would be sent with the POST [endpoint-base]/negotiate request that is used to establish a connection between the client and the server, like below.
If a connection is established and the WebSockets transport is used, exchanging messages between server and client would be on WebSockets protocol.
If WebSockets is not available, and Long Polling transport is used, while client communicates with hub, cookies are sent with each request.
Besides, as mentioned in this doc: cookie authentication isn't recommended unless the app only needs to authenticate users from the browser client.

ASP.Net Core SignalR bearer token on negotiate

From reading the Microsoft docs on authentication with SignalR, it looks like the only way to authenticate using a bearer token is to send it in the query string on the WebSocket connection.
Upon inspecting the SignalR handshake, it looks like the Authorization header is included in the Negotiate call. Since the connection ID is returned in the negotiate response, the server could keep track of whether that connection ID has authenticated.
Why is it required to add the bearer token to the Websocket connection query string as well?
It seems this is the clue for your question.
When using WebSockets or Server-Sent Events, the browser client sends the access token in the query string. Receiving the access token via query string is generally secure as using the standard Authorization header. Always use HTTPS to ensure a secure end-to-end connection between the client and the server.
Security considerations in ASP.NET Core SignalR
This is the alternative:
SignalR can be used with ASP.NET Core authentication to associate a user with each connection. In a hub, authentication data can be accessed from the HubConnectionContext.User property. Authentication allows the hub to call methods on all connections associated with a user. For more information, see Manage users and groups in SignalR. Multiple connections may be associated with a single user.
Authenticate users connecting to a SignalR hub
Additional:
However, I think you can also try some injection for both server and client. On server eg. use websocket helper and on client eg. use Promise vs XMLHttpRequest.

Does deepstream requires login after reconnect?

I am building a service which would depend on maintaining a unique session ID for the connected clients. Currently I am using the login() method to return sessionID as a part of userData from deepstream authentication handler.
When a client(in browser) disconnects and reconnects due to bad network will the login call be made again? In that case a new sessionID will be generated which is undesirable.
deepstream will internally cache the authentication data and re-send them upon reconnecting. However, if establishing a connection fails continuously and the connection closes with an error state you'd have to call .login() again manually.

What does connectAs="endUser" actually do?

The Worklight documentation refers to an attribute within the element of an adapter's XML file called connectAs="endUser". It says that this means that:
The connection to the back end is created with the user’s identity.
Only valid if a user realm has been identified in the security tests
for this procedure.
However, what does this actually mean in terms of the HTTP connection that is performed from the adapter to the back-end HTTP server? How does it affect, for example, the JSESSIONID?
EDIT: Further to my original post, Anton Aleksandrov has provided a blog post with more details on how this mechanism works:
https://www.ibm.com/developerworks/community/blogs/worklight/entry/configuring_http_adapters_for_stateless_stateful_backend_connectivity_and_user_identity_propagation?lang=en
What this actually means is that the Worklight server will behave as if it were an "end user" (specifically, a web browser).
Within a given Worklight adapter, the connectAs="endUser" parameter will result in the HTTP Set-Cookie headers being stored as part of an authenticated Worklight session. Subsequent requests that request connectAs="endUser" will send any cookies that are stored as part of that "endUser" server-side session.
The Worklight documentation specifically states that it is only valid in a realm has been identified since if there is no realm, it is not possible to save those cookies for later use in the server-side session.
The effect from a Worklight client app point of view should not change if you choose to use this parameter.
The Worklight server to back end HTTP services will change. Essentially the back end server will treat a Worklight adapter that uses connectAs="endUser" as a single HTTP web browser. So for the example of JSESSIONID:
You initiate a procedure "login" for the first time that specifies connectAs="endUser". This procedure also has an associated security test which enforces a user to be logged into a realm (by whatever means, or anonymous - it doesn't really matter so long as Worklight has a user session to attach cookies to)
When this request reaches a Java-based server that tracks sessions by JSESSIONID, it will detect that this is a first-time request. It will process the request, and send a HTTP response, with whatever content is necessary in the HTTP body. In the HTTP headers, there will be a Set-Cookie response that contains a JSESSIONID.
By virtue of the Worklight procedure having contained connectAs="endUser", the Worklight server will process these Set-Cookie headers and store them alongside the session of the user authorised against the Worklight realm (this is why you need to have a realm that is logged in for this to work)
On a subsequent request to a procedure "mySecondServerProcedure", which also has connectAs="endUser" and the same realm specified, the Worklight server will automatically provide the stored cookies to the server on the outgoing HTTP request, in addition to any you add as parameters on a requestHttp() call in your adapter. In this example, the JSESSIONID that was set as part of "login" will be provided.
If you make another request to a procedure "myThirdServerProcedure" that does NOT have connectAs="endUser" set, no cookies will be provided to the server on the outgoing HTTP response that you do not provide manually as part of the "Cookies" parameters on the requestHttp() call in your adapter.
Important points to note:
A user must be logged in for this to work so that Worklight can associate the HTTP cookies with a session
The session cookie storage is only valid WITHIN the adapter that made the initial request; if you run a connectAs="endUser" request from another adapter after having gotten your JSESSIONID set as part of "login" above, this request will NOT have the JSESSIONID cookie automatically appended to the outgoing request.
If you log out of the authenticated Worklight user session, all reference to these cookies will be gone.
My general rule of thumb is as follows:
If you are doing some fairly simple authentication that requires cookies on the back-end server to remain consistent, use endUser
If you're doing something more complex, or potentially requiring the cookies sent by the server to be available from multiple adapters, find another way to store the cookies. A pattern I like is to have a wrapper method present that makes the outgoing HTTP request and processes the headers that come back in the response to store necessary "global" properties somewhere. In the Worklight world, this can either be as part of a Worklight user session object, or you can call out to an underlying Java or database storage implementation.

How does wininet handle cookies

I have a .NET client application that needs to communicate with a server using two distinct user credentials.
Lets say that the application runs two threads. When start running, every thread sends the user & password to authenticate and the server in return stores a cookie on the http session. The subsequent calls send the authentication cookie and not the user credentials.
We have two cookies for the same process. How does wininet "knows" to send the appropriate cookie for each thread?
Does wininet manage the cookies collection per thread? per http session? per process?
Thanks
Wininet uses cookies per process.
However in a .NET client you can use a Cookie container with the HttpWebRequest object.
You create one cookie container for each "session". Assign the appropriate container to each HttpWebRequest when making the various requests for each session.