You are not logged in.
Pages: 1
Is there currently a way to ensure that https can only be upgraded to wss connections for already authenticated/logged in users and then to be able to link the wss requests/connectionids to the specific logged in user?
Offline
If it helps you find the way, on my side, I use TWebSocketProtocol.UpgradeBearerToken for that: read more there
Online
My initial thought was to define a protocol that is similar to the http uri signing, which would keep things standard for third parties. Then wss commands can be sent using a signing format similar to this:
{
"command": "/root/mycommand?session_signature=same as calculated as if command was a uri",
"parameters": {}
}
But then I would need to somehow pass the command to the rest server for the authentication to be checked. The advantage would be that, if a user logs out from the https server, the wss functions will also fail. So no additional session management would have to be done.
Offline
Thanks @ab. How would I send the uri to TRestServerAuthenticationDefault for verification? Is there a function I am missing that takes a uri and returns a session? I have searched, but may have missed it.
Does TWebSocketAsyncServer have an OnBeforeBody property?
Offline
This is indeed the problem.
There are several issues:
1) The TRestHttpServer can handle several TRestServer instances, with a complex redirection system which may involve THttpServerRequestAbstract.Host value.
So it is not easy to redirect to the proper TRestServer. We could restrict to a single server.
2) The URI authentication is using a TRestServerUriContext instance, which does not exist yet when the WebSockets upgrade is done.
3) The URI authentication depends on the actual URI used.
So I guess it would need to add a dedicated method to TRestServerAuthentication...
Which seems pretty much complex for such a task.
Offline
Note that you have an alternative to OnBeforeBody.
You can just set an aOnWSUpgraded: TOnWebSocketProtocolUpgraded callback as parameter to WebSocketsEnable().
Then, don't use the mORMot URI signing, but a custom challenge, either in the URI or in the bearer.
Note that putting a signature in an URI should always be time-limited to avoid replay attacks.
Offline
Please try with
https://github.com/synopse/mORMot2/commit/1e3a67727
There is now a new rsoWebSocketsUpgradeSigned option which will require either a HTTP bearer or a specific URI parameter when upgrading to websockets.
You just supply the URI or the bearer from a safe service method, then use this value to upgrade.
On the client side:
https://github.com/synopse/mORMot2/commit/f322cc420
My guess is that this pattern (which I have commonly used before) is the right to use for safe WebSockets upgrade, in a cross-platform way.
Offline
Wow that was quick, thanks @ab!
I'm trying to understand the workflow of the new solution, but not very sure how to implement it. It looks as if the upgrade to websockets replaces the mormot signing/authentication with cookies?
Offline
It is only for the Websockets upgrade itself.
Other regular HTTP requests are not affected.
But if you restrict your system to only support WebSockets connections on the server, it may be a good way to secure your service.
If it is properly used, and all other methods and services are setup to require a websockets, then TRestServerAuthenticationNone could be enough.
It is exactly the same as with a JWT bearer: in OnBeforeBody, you could only allow the upgrade URI (and perhaps /root/timestamp) and you would have a secure endpoint.
Offline
I am missing something frustratingly basic about the workflow. How can I tie this back to a specific user account on the websockets side? My workflow is:
1) user logs in using rest
2) user does rest calls - all of which need to know the user's session
3) user logs out
The websockets requirement is for when bidirectional actions are needed. So the user upgrades the connection. Now, for calls made by this user, I still need to tie this request to a specific session, since the user rights need to be checked and logged with every request.
I think I'm misunderstanding or missing something fundamental in the workflow of the changes to the code:
1) user logs in with rest and a session is created
2) user upgrades connection to wss and a new cookie is created
3) user gets access to the rest calls using the cookie, but there is no way to resolve the cookie to the session, so if the user logs out on rest, the wss sessions continue to be valid?
Last edited by squirrel (2025-08-20 06:48:24)
Offline
It could be e.g.
1) user logs in with rest and a session is created
2) user call a rest API to retrieve a bearer or a uri
3) user upgrades connection to wss using this bearer or uri
4) user gets access to the rest calls using wss on this connection, using its previous session as usual
In fact, step 4) is transparent from the client point of view: it just makes some calls, and the framework will use the websockets protocol to perform its process.
The bearer or uri returned at step 2) is only used at step 3) not after.
For additional security, if you expect to accept only mORMot ws clients, you could filter the API calls to only be accessed via ws - but of course for the rest API which retrieve the bearer/uri.
Use OnBeforeBody for such input filtering.
Offline
Hi ab
I assume there is something I need to set, but not sure what. The websockets signer is readonly:
/// the TBinaryCookieGenerator created by rsoWebSocketsUpgradeSigned option
// - equals nil if this option was not set
// - WebSockets upgrade will be authenticated with an ephemeral secure token,
// as retrieved from WebSocketsUrl/WebSocketsBearer associated methods
property WebSocketsSigner: TBinaryCookieGenerator
read fWebSocketsSigner;
so I assume it will be created automatically, but in function TRestHttpServer.WebSocketsBearer, it is nil, even though the option is included. So the exception is raised.
How should I create the httpserver so that the new websocket options can be used? Like this?
HTTPsServer := TSQLHTTPServer.Create('8084', [RestSvr], '+', useHttpApiRegisteringURI, 32, secSSL, '', '', [rsoWebSocketsUpgradeSigned]); //https
HTTPsServer.AccessControlAllowOrigin := '*'; // allow cross-site AJAX queries
Log('BEARER: ' + HTTPsServer.WebSocketsBearer(RestSvr)); //this causes the exception
Offline
Pages: 1