Server/Client Example
Overview
This page demonstrates secure communication between two entities using Auth-distributed session keys. Choose a technology to see the matching setup, run commands, and client/server implementation.
The examples share the same flow:
- The client requests session keys from Auth.
- The client connects to the server and completes the SST handshake.
- Both entities exchange encrypted messages over the secure channel.
- Session keys are cached and reused when the implementation supports it.
Prerequisites
- Clone the
iotauth/iotauthrepository. - Run
./generateAll.shfromexamples/to generate the shared credentials and the entity configs supported by the generator. - Start Auth 101 before starting either entity:
cd auth/auth-server
mvn clean install
java -jar target/auth-server-jar-with-dependencies.jar \
-p ../properties/exampleAuth101.properties
Technology example
Choose one of the following technologies:
- C
- Node.js
- Python
C example
The C example uses the native SST API and demonstrates the protocol lifecycle directly.
Source: examples/server_client_example/ in iotauth/sst-c-api.
Build the C example
cd entity/c/examples/server_client_example
mkdir -p build
cd build
cmake ../
make
For verbose debug output:
cmake -DCMAKE_BUILD_TYPE=DEBUG ../
make
This produces entity_server and entity_client in build/.
Run the C server
cd entity/c/examples/server_client_example/build
./entity_server ../c_server.config
The server binds to its configured port and waits for the client.
Run the C client
cd entity/c/examples/server_client_example/build
./entity_client ../c_client.config
The client requests session keys from Auth, completes the handshake, and sends encrypted messages.
C client implementation
SST_ctx_t* ctx = init_SST(config_path);
session_key_list_t* keys = get_session_key(ctx, NULL);
SST_session_ctx_t* session =
secure_connect_to_server(&keys->s_key[0], ctx);
pthread_t receive_thread;
pthread_create(
&receive_thread,
NULL,
receive_thread_read_one_each,
(void*)session
);
send_secure_message(
"Hello server",
strlen("Hello server"),
session
);
pthread_cancel(receive_thread);
pthread_join(receive_thread, NULL);
free_session_ctx(session);
free_session_key_list_t(keys);
free_SST_ctx_t(ctx);
C server implementation
SST_ctx_t* ctx = init_SST(config_path);
session_key_list_t* keys = init_empty_session_key_list();
int client_socket = accept(server_socket, NULL, NULL);
SST_session_ctx_t* session =
server_secure_comm_setup(ctx, client_socket, keys);
pthread_t receive_thread;
pthread_create(
&receive_thread,
NULL,
receive_thread_read_one_each,
(void*)session
);
send_secure_message(
"Hello client",
strlen("Hello client"),
session
);
The server keeps keys available for later accepted connections so cached session keys can be reused without another Auth round trip.
C configuration
| File | Purpose |
|---|---|
c_client.config | Auth coordinates, server address, session key purpose, and client credentials. |
c_server.config | Auth coordinates, listening port, and server credentials. |
Node.js example
The Node.js example uses SecureCommClient and SecureCommServer, which expose secure communication through input handlers and output callbacks.
Source: entity/node/example_entities/ in iotauth/iotauth.
Prepare the Node.js example
The repository generation step installs the required Node.js packages and creates the JSON configuration files under entity/node/example_entities/configs/.
To install the entity dependencies manually:
cd entity/node
npm install
Run the Node.js server
cd entity/node/example_entities
node server.js configs/net1/server.config
Run the Node.js client
cd entity/node/example_entities
node client.js configs/net1/client.config
At the client prompt, connect and send a message:
initComm net1.server
send hello from node client
finComm
At the server prompt, send to every client or to one socket:
send hello from node server
sendTo <socketID> hello from node server
Node.js client implementation
const SecureCommClient = require('../accessors/SecureCommClient');
const client = new SecureCommClient('configs/net1/client.config');
client.initialize();
client.setOutputHandler('connected', (connected) => {
if (connected) {
client.provideInput('toSend', Buffer.from('Hello server'));
}
});
client.setOutputHandler('received', (data) => {
console.log(data.toString());
});
client.setOutputHandler('error', (message) => {
console.error(message);
});
client.provideInput(
'serverHostPort',
{host: 'localhost', port: 21100},
'Servers'
);
Node.js server implementation
const SecureCommServer = require('../accessors/SecureCommServer');
const server = new SecureCommServer('configs/net1/server.config');
server.initialize();
server.setOutputHandler('listening', (port) => {
console.log(`Listening securely on ${port}`);
});
server.setOutputHandler('received', ({id, data}) => {
console.log(data.toString());
server.provideInput('toSend', {
id,
data: Buffer.from('Hello client'),
});
});
server.setOutputHandler('error', (message) => {
console.error(message);
});
Node.js configuration
| File | Purpose |
|---|---|
configs/net1/client.config | Client identity, Auth details, credentials, and target server list. |
configs/net1/server.config | Server identity, Auth details, credentials, and listening address. |
Python example
The Python example uses IoTAuthContext, SecureClient, SecureServer, and SecureChannel to provide a synchronous, context-managed API.
Source: entity/python/examples/ in iotauth/iotauth.
Prepare the Python environment
cd entity/python
python3 -m venv .venv
source .venv/bin/activate
python3 -m pip install -e .
Keep the virtual environment active in the terminals used for the server and client.
Create or select the Python configuration
generateAll.sh generates the shared example credentials and the Node-style JSON entity configs, but it does not generate or update dedicated Python config files.
The repository already contains these example Python configuration files:
entity/python/examples/configs/py_client.config
entity/python/examples/configs/py_server.config
For a new Python entity or a replacement config, create the file manually from scratch or copy and adapt one of these supported formats:
- A C-style properties config, such as
entity/c/examples/server_client_example/c_client.configorc_server.config. - A generated Node-style JSON config, such as
entity/node/example_entities/configs/net1/client.configorserver.config.
Relative credential and key paths are resolved differently in the two formats. See the Python Guide configuration formats table before copying paths: C-style paths are relative to the config file, while JSON paths are relative to the process working directory.
Run Auth, the Python server, and the Python client
Auth, the server, and the client must run concurrently.
Use three separate terminals or three tmux panes and run one process in each.
In terminal or pane 1, start Auth and leave it running:
cd auth/auth-server
java -jar target/auth-server-jar-with-dependencies.jar \
-p ../properties/exampleAuth101.properties
In terminal or pane 2, activate the virtual environment and start the Python server:
cd entity/python
source .venv/bin/activate
cd examples
python3 py_server.py configs/py_server.config
In terminal or pane 3, activate the virtual environment and start the Python client:
cd entity/python
source .venv/bin/activate
cd examples
python3 py_client.py configs/py_client.config
Expected Python exchange and shutdown
On a successful run, the client requests a session key, completes the secure handshake, and sends exactly three messages in this order:
Hello serverHello server - second messageHello server - third message
The server decrypts each message and replies in the same order with:
Hello clientHello client 2Hello client 3
After receiving the third reply, the client finishes its loop, closes the secure channel through its context manager, and exits.
The server detects the client disconnect, closes its channel and listening socket through its context manager, and exits after serving that one connection.
Auth does not stop automatically.
After both entity processes exit, manually stop Auth with Ctrl+C in its terminal or tmux pane.
Python client implementation
from iotauth import IoTAuthContext, SecureClient
ctx = IoTAuthContext.from_config("configs/py_client.config")
with SecureClient(ctx, timeout=5.0) as client:
channel = client.connect()
channel.send(b"Hello server")
reply = channel.recv(timeout=1.0)
print(reply.decode("utf-8"))
Python server implementation
from iotauth import IoTAuthContext, SecureServer
ctx = IoTAuthContext.from_config("configs/py_server.config")
with SecureServer(ctx, handshake_timeout=5.0) as server:
channel = server.serve_once()
data = channel.recv()
print(data.decode("utf-8"))
channel.send(b"Hello client")
The context managers close sockets and release runtime resources when the operation completes or raises an exception.
What should happen
You should see:
- Auth logs showing session key requests from the entities.
- The server logging a successful handshake and the decrypted client message.
- The client logging the decrypted server response.
All three implementations use the same SST message framing, authenticated encryption, and sequence-number validation.
Next steps
- See C API Reference for the native API.
- See Node.js API Reference for the accessor-based API.
- See Python API Reference for the Python runtime API.
- See File Block Encryption for per-block encryption without sockets.
- See IPFS File Sharing for Auth-managed file encryption and distribution.