Jump to content

Authenticating socket connections


NotionCommotion

Recommended Posts

Regular clients and super clients can connect to a socket server.  Regular clients must initially register with the server and provide a valid unique ID.  Super clients can tell regular clients what to do, but regular clients cannot tell other regular clients what to do.  The super clients have short lifetimes and are initiated by authenticated human users via a webserver.

 

How can the server determine which type of client has connected?

 

Ideally, when creating the socket connection, I would be able to pass some authentication identification, however, it does appear that the ReactPHP class implements this functionality.

 

As an alternative, I have several ideas.

  1. Have the super client first send a logon message and then the actual message as shown below.
  2. Basically the same, but have the username and password included in the intended message.
$response=null;
$msg=['message to send to the server'];
$loop = \React\EventLoop\Factory::create();
$connector = new \React\Socket\TimeoutConnector(new \React\Socket\Connector($loop), 5, $loop);
$connector->connect($serverIpPort)
->then(function (\React\Socket\ConnectionInterface $connection)  use (&$response, $loop, $msg) {
    $connStream = new \DataLogger\Server\LengthPrefixStream($connection);
    $connStream->on('data', function($data) use (&$response, $loop, $connStream, $msg){
        if(is_null($response)) {
            if(isset($data->success)) {
                $connStream->send($msg);
                $response==false;
            }
            else {
                $connection->close();
                $loop->stop();
            }
        }
        else {
            $response=$data;
            $connection->close();
            $loop->stop();
        }
    });
    $connStream->send(['message with username and password']);
    $loop->addTimer(5, function() use (&$response, $loop, $connection){
        $connection->close();
        $loop->stop();
    });
});
$loop->run();

Then the server can do something like the following for option 1, or check the received data for authentication to determine user type for my option 2.

public function startServer()
{
    $loop = \React\EventLoop\Factory::create();
    $server = new \React\Socket\TcpServer($serverIpPort, $loop);
    $server->on('connection', function (\React\Socket\ConnectionInterface $conn) {
        $connStream = new LengthPrefixStream($conn);
        $connStream->on('data', function($data) use ($connStream){
            $this->processMessage($connStream,$data);
        });
    });
}


private function processMessage($connStream,$data)
{
    if(empty($connStream->type)) {
        //Allow regular or super clients to identify themselves only, and set the $connStream->type property accordingly
    }
    elseif($connStream->type=='regular') {
        //Allow regular client functionallity.
    }
    elseif($connStream->type=='super') {
        //Allow super client functionallity.
    }
}

Don't know for sure if either of these options are the "right" way of doing this, and would expect I am not the first one trying to do this and there are likely proven better solutions.  Any recommendations?  Thanks

Link to comment
Share on other sites

If you are saying that you have role access, it seems to me that should not change the way your authentication works.

 

There's no reason that you would want to make access a property of a connection. Access should be related to the individual user, and something you verify and grant/deny at the processing of every command/action.

Link to comment
Share on other sites

If you are saying that you have role access, it seems to me that should not change the way your authentication works.

 

There's no reason that you would want to make access a property of a connection. Access should be related to the individual user, and something you verify and grant/deny at the processing of every command/action.

Yes, I am saying I have role access.  Not change the way my authentication works in what regard?  Currently, I have zero authentication for socket connections and anyone can connect if they know the given IP and port.
 
Why couldn't the connection be trusted after being authenticated that it was initiated by an authorized user?
 
A shortcoming of my option 2 is it does not support JSON RPC batches very well.
 
PS.  Typo: it does NOT appear that the ReactPHP class implements this functionality.
Link to comment
Share on other sites

Yes, anyone can connect to a public server on a well known port. That isn't different from any other internet based application.

 

What allows someone to proceed from connection to be able to actually use the socket connection? That is where you need to validate the user.

 

This depends on how you have implemented authentication and session in your application.

 

I would suggest you take a look at something built upon the foundation of JSON Web Tokens. There are a number of PHP libraries you can choose from:

 

https://jwt.io/#libraries

 

Here is an article on building something like this with Angular, but the concepts and problems JWT solve for you are likely the same.

 

https://blog.angular-university.io/angular-jwt-authentication/

Link to comment
Share on other sites

Thanks gizmola,  JSON web tokens appear to be a great option, and I will likely use them going forward.  As I see it, they provide the following:

  1. User enters username and password and server returns a JSON Web Token.
  2. Future requests from user includes this JWT in the header so the server can authenticate.

How would someone use them with sockets?  I can pass them with every message, but the first example isn't technically valid JSON-RPC, and the second mixes application data with authentication data.  Furthermore, both require both sides to adapt an additional custom agreement on protocol.  And while I could and should use them for the HTTP client to HTTP server requests, what value do they plan in the sockets communication?   Maybe I wasn't clear in my original post, but the "regular clients" do not use HTTP but naively communicate to the socket server using JSON-RPC.

 

{
    "jsonrpc": "2.0",
    "method": "subtract",
    "params": {
        "subtrahend": 23,
        "minuend": 42
    },
    "id": 3,
    "jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ"
}
{
    "jsonrpc": "2.0",
    "method": "subtract",
    "params": {
        "subtrahend": 23,
        "minuend": 42,
        "jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ"
    },
    "id": 3
}
Link to comment
Share on other sites

Your question doesn't really have anything to do with sockets. It has to do with the protocol being used to communicate, which is something entirely up to you to implement in whatever fashion you see fit.

 

A socket connection is just a communications channel. As the classic analogy goes, it's nothing more than a pipe through with you can pass data. It doesn't care what data is sent, who's sending it, or who's receiving it. That's not it's job.

 

If you want to control those details, then you implement that in your protocol and you make your applications require that protocol to communicate.

 

If you permit multiple messages to be sent across the connection, then you can just have some separate authentication message that is sent first.

{
    "jsonrpc": "2.0",
    "method": "authenticate",
    "params": {
        "role": "super"
        , "password": "blah"
    },
    "id": 3
}
Until the server sees that message, ignore or error on other types of messages. After the message is received, you know what the client is and can respond accordingly.

 

If only a single message is allowed per connection then you'll just have to incorporate your authentication into that message in some way.

Link to comment
Share on other sites

Your question doesn't really have anything to do with sockets. It has to do with the protocol being used to communicate, which is something entirely up to you to implement in whatever fashion you see fit.

 

A socket connection is just a communications channel. As the classic analogy goes, it's nothing more than a pipe through with you can pass data. It doesn't care what data is sent, who's sending it, or who's receiving it. That's not it's job.

 

If you want to control those details, then you implement that in your protocol and you make your applications require that protocol to communicate.

 

If you permit multiple messages to be sent across the connection, then you can just have some separate authentication message that is sent first.

{
    "jsonrpc": "2.0",
    "method": "authenticate",
    "params": {
        "role": "super"
        , "password": "blah"
    },
    "id": 3
}
Until the server sees that message, ignore or error on other types of messages. After the message is received, you know what the client is and can respond accordingly.

 

If only a single message is allowed per connection then you'll just have to incorporate your authentication into that message in some way.

 

 

Thanks kicken,  I am good on the validating that the message is valid and provides the correct data, etc, and it does have to do with sockets.  My assumption was that a socket connection that was once authenticated will always be authenticated and need not be authenticated again.  The approach you showed is basically what I am doing.  I also elected to require two transfers: one to authenticate and two for the single (today) data transfer.

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.