Server¶
The server package contains building blocks for an S2 Connect pairing server and communication server.
The official protocol description is here:
https://docs.s2standard.org/docs/communication-layer/discovery-pairing-authentication/
Responsibilities¶
The server helpers cover:
- creating a pairing attempt and pairing token;
- handling
/requestPairing; - validating the client's HMAC response in
/requestConnectionDetails; - returning connection details;
- handling
/initiateConnection; - confirming pending access tokens;
- validating the one-time token for the S2 communication channel.
The functions in s2auth.server.pairing and s2auth.server.connection_initiation are designed to be called from your web framework endpoints.
Configuration¶
Server configuration is split into two Pydantic settings classes.
Runtime config¶
s2auth.server.config.Config contains runtime infrastructure settings:
| Environment variable | Purpose | Default |
|---|---|---|
SQLALCHEMY_DB_URI |
SQLAlchemy database URI | postgresql://postgres:postgres@localhost/s2auth |
HMAC_SALT |
Salt used for HMAC challenge-response calculation | s2.example.com |
Server settings¶
s2auth.server.settings.Settings contains identity and endpoint metadata used by default hooks:
| Environment variable | Purpose | Default |
|---|---|---|
PAIRING_NODE_ID |
Short pairing node ID alias, 8-12 characters | Required |
SERVER_S2_NODE_ID |
UUID for the communication server node | Required |
SUPPORTED_COMMUNICATION_PROTOCOLS |
Communication protocols supported by the server | ["WebSocket"] |
SUPPORTED_S2_VERSIONS |
Supported S2 message versions, most recent first | ["v0.02-beta"] |
SUPPORTED_S2_CONNECT_VERSIONS |
Supported S2 Connect versions, most recent first | ["v1.0-beta-2"] |
CEM_S2_NODE_ID |
UUID returned in the default CEM node description | Required |
CEM_TYPE |
Node type returned in default server node description | Required |
CEM_MODEL_NAME |
Model name returned in default server node description | Required |
CEM_BRAND |
Brand returned in default server node description | Required |
CEM_URL |
Connection initiation URL returned after pairing | None |
CEM_DEPLOYMENT_TYPE |
Deployment type returned for the CEM | WAN |
Example:
SQLALCHEMY_DB_URI=postgresql://postgres:postgres@localhost/s2auth
HMAC_SALT=s2.example.com
PAIRING_NODE_ID=pairnode1
SERVER_S2_NODE_ID=00000000-0000-0000-0000-000000000001
SUPPORTED_COMMUNICATION_PROTOCOLS='["WebSocket"]'
SUPPORTED_S2_VERSIONS='["v0.02-beta"]'
SUPPORTED_S2_CONNECT_VERSIONS='["v1.0-beta-2"]'
CEM_S2_NODE_ID=00000000-0000-0000-0000-000000000002
CEM_TYPE=server
CEM_MODEL_NAME=default
CEM_BRAND=s2auth
CEM_URL=https://example.com/connection
CEM_DEPLOYMENT_TYPE=WAN
Getting started with the reference server¶
Install the server dependencies, copy the example environment file, and start the reference server:
The reference server starts on port 8000. The first communication is with the user, not another S2 node: use the existing /pairing/userBeginPairing endpoint to communicate the pairing token between the user and the S2 Connect server. This step is technically outside the S2 Connect protocol, but the server needs it so a later S2 Connect pairing request can prove knowledge of the same pairing token.
The reference server accepts HTTP Basic credentials alice:alice and bob:bob:
curl -X POST http://localhost:8000/pairing/userBeginPairing \
-u alice:alice \
-H 'Content-Type: application/json' \
-d '"pairingToken123"'
After this request succeeds, the server has stored the pairing token for the authenticated user's client node. The S2 Connect pairing client can then call /pairing/{s2_connect_version}/requestPairing with the matching nodeIdAlias and complete the challenge-response flow.
Pairing flow¶
Use s2auth.server.pairing for the server side of the pairing flow:
initiate_pairing()creates and stores aPairingAttemptContext.request_pairing()handles the client's pairing request, stores anAuthenticationContext, selects the HMAC algorithm, computes the response to the client's challenge, and returns server descriptions.handle_client_response()validates the client's response to the server challenge, stores the active access token, marks the pairing attempt as completed, and returns connection details.finalize_pairing()marks the authentication context as paired after the client confirms it stored the returned connection details.
requestConnectionDetails and finalizePairing only need the pairingAttemptId header. The server resolves the matching AuthenticationContext through the PairingAttemptContext.client_node_id.
Connection initiation flow¶
Use s2auth.server.connection_initiation after pairing:
initiateConnection()verifies the current access token, negotiates S2 message version and communication protocol, and generates a pending access token.validate_access_token()confirms the pending token and promotes it to the current active access token.validate_s2_connection_token()validates the one-time token used to authenticate the actual communication channel.
initiateConnection() receives a newly generated access token from DI as new_access_token; the injected value is an AccessToken, not a generator function.
Reference server¶
The reference server initializes server hooks and dependency injection in FastAPI lifespan with:
Its connection initiation endpoint hook uses the active FastAPI request to return request.url_for("connection_root"), so the connection URL always points at the mounted connection router.
Customization¶
- Hooks describes how to customize pairing decisions and server descriptions.
- Dependency Injection explains this project's use of
wepositive-di. - Pairing Tokens explains how to override pairing token generation.