Server Pairing¶
Module: s2auth.server.pairing
Pairing functionality for the S2 server.
ClientNodeId = UUID
module-attribute
¶
PairingAttemptId = UUID
module-attribute
¶
pairing_attempt_id_var = ContextVar('pairing_attempt_id', default=None)
module-attribute
¶
s2_client_node_id_var = ContextVar('s2_client_node_id', default=None)
module-attribute
¶
PairingToken = Annotated[str, StringConstraints(pattern='^[A-Za-z0-9+/]{4,}={0,2}$', min_length=4)]
module-attribute
¶
UTC = timezone.utc
module-attribute
¶
log = logging.getLogger(__name__)
module-attribute
¶
AccessError
¶
Bases: S2ConnectError
Permission is denied to the client. This generally results in an HTTP 401 error.
Source code in src/s2auth/common/exceptions.py
PairingNotCompleteError
¶
Bases: S2ConnectError
Pairing was not completed successfully before initiating a connection.
Source code in src/s2auth/common/exceptions.py
ConnectionDetails
¶
Bases: BaseModel
Source code in src/s2auth/common/model/s2_connect_pairing.py
FinalizePairingPostRequest
¶
HmacChallengeResponse
¶
Bases: RootModel[Base64Bytes]
Source code in src/s2auth/common/model/s2_connect_pairing.py
S2PairingAttemptId
¶
Bases: RootModel[str]
Source code in src/s2auth/common/model/s2_connect_pairing.py
NodeIdAlias
¶
Bases: RootModel[str]
Source code in src/s2auth/common/model/s2_connect_pairing.py
RequestConnectionDetailsPostRequest
¶
RequestPairingPostRequest
¶
Bases: BaseModel
Source code in src/s2auth/common/model/s2_connect_pairing.py
RequestPairingPostResponse
¶
Bases: BaseModel
Source code in src/s2auth/common/model/s2_connect_pairing.py
AccessToken
¶
Bases: RootModel[Base64Bytes]
Source code in src/s2auth/common/model/s2_connect_common.py
Deployment
¶
NodeId
¶
AuthenticationContext
¶
Bases: BaseModel
Authentication context data for a client connection.
Note: Modifications to context instances should be done carefully in multi-threaded/async environments. Consider using the storage's locking mechanisms if implementing complex state updates.
Source code in src/s2auth/server/context.py
ClientState
¶
PairingAttemptContext
¶
Bases: BaseModel
Context data for a pairing attempt.
Note: Modifications to context instances should be done carefully in multi-threaded/async environments. Consider using the storage's locking mechanisms if implementing complex state updates.
Source code in src/s2auth/server/context.py
PairingState
¶
ReadOnlyAuthenticationContext
¶
Bases: AuthenticationContext
Read-only view of AuthenticationContext for passing to hooks.
This class prevents accidental modification of context state in hooks. Any attempt to modify attributes will raise a ValidationError.
Source code in src/s2auth/server/context.py
ReadOnlyPairingAttemptContext
¶
Bases: PairingAttemptContext
Read-only view of PairingAttemptContext for passing to hooks.
This class prevents accidental modification of context state in hooks. Any attempt to modify attributes will raise a ValidationError.
Source code in src/s2auth/server/context.py
S2InMemoryContextStorage
¶
Bases: InMemoryContextStorage
In-memory context storage with typed context listing support.
Source code in src/s2auth/server/context.py
list_contexts(ctx_type)
async
¶
Return snapshots of all stored contexts for the requested type.
Source code in src/s2auth/server/context.py
delete_context(ctx_type, context_id)
async
¶
Delete a stored context for the requested type and ID.
Source code in src/s2auth/server/context.py
Config
¶
Bases: BaseSettings
Source code in src/s2auth/server/config.py
HookRegistry
¶
Registry for server hooks that can be overridden by client code.
Source code in src/s2auth/server/hooks.py
register(original_hook, custom_hook)
¶
Register a custom hook implementation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
original_hook
|
HookFunction
|
The original hook function to override |
required |
custom_hook
|
HookFunction
|
The custom hook implementation function |
required |
Raises:
| Type | Description |
|---|---|
RuntimeError
|
If a custom implementation is already registered for this hook |
KeyError
|
If the original hook is not recognized |
Source code in src/s2auth/server/hooks.py
get(hook)
¶
Get a hook implementation (default or custom).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
hook
|
HookFunction
|
The hook function reference |
required |
Returns:
| Type | Description |
|---|---|
HookFunction
|
The hook implementation function (either default or custom) |
Raises:
| Type | Description |
|---|---|
KeyError
|
If the hook is not registered |
Source code in src/s2auth/server/hooks.py
Settings
¶
Bases: BaseSettings
Source code in src/s2auth/server/settings.py
ExpiredOneTimePairingTokenError
¶
authentication_context(client_node_id=Depends[client_node_id], storage=Depends[context_storage_singleton])
async
¶
Retrieves the context for the specified client_node_id.
This is an async generator provider that yields the context while holding its per-ID lock. The lock is held for the entire duration that dependent functions use the context, ensuring thread-safe and async-safe modifications.
Works in both async and threaded environments through wepositive-di storage.
Source code in src/s2auth/server/context.py
authentication_context_by_pairing_attempt_context(pairing_attempt_id=Depends[pairing_attempt_id], storage=Depends[context_storage_singleton])
async
¶
Retrieve authentication context through the current pairing attempt.
Source code in src/s2auth/server/context.py
pairing_attempt_context(pairing_attempt_id=Depends[pairing_attempt_id], storage=Depends[context_storage_singleton])
async
¶
Retrieves the context for the specified pairing_attempt_id.
This is an async generator provider that yields the context while holding its per-ID lock. The lock is held for the entire duration that dependent functions use the context, ensuring thread-safe and async-safe modifications.
Works in both async and threaded environments through wepositive-di storage.
Source code in src/s2auth/server/context.py
pairing_attempt_context_by_client_node_id(client_node_id=Depends[client_node_id], storage=Depends[context_storage_singleton])
async
¶
Retrieve a pairing attempt context by its client_node_id.
Source code in src/s2auth/server/context.py
store_authentication_context(storage=Depends[context_storage_singleton])
async
¶
Provider that returns a function to store authentication contexts.
Returns a callable that can be used to safely store AuthenticationContext objects in the context storage. Thread-safe and async-safe.
Usage
@inject async def my_function( store_ctx: Callable[[AuthenticationContext], Awaitable[None]] = Depends[store_authentication_context] ): ctx = AuthenticationContext(client_node_id=some_uuid) await store_ctx(ctx)
Source code in src/s2auth/server/context.py
store_pairing_attempt_context(storage=Depends[context_storage_singleton])
async
¶
Provider that returns a function to store pairing attempt contexts.
Returns a callable that can be used to safely store PairingAttemptContext objects in the context storage. Thread-safe and async-safe.
Usage
@inject async def my_function( store_ctx: Callable[[PairingAttemptContext], Awaitable[None]] = Depends[store_pairing_attempt_context] ): ctx = PairingAttemptContext(pairing_attempt_id=some_uuid, ...) await store_ctx(ctx)
Source code in src/s2auth/server/context.py
calculate_certificate_fingerprint_from_certificate_file(certificate_file)
¶
Read a certificate file and return the SHA-256 fingerprint of the leaf certificate.
Source code in src/s2auth/common/hmac.py
create_challenge(length=128)
¶
Create the base64 encoded challenge (sequence of random bytes) to be sent to the other side of the connection. The challenge needs to be passed to the the other side of the connection, who should sign it with a shared pairing token. verify_response can then be used to verify that signature.
Source code in src/s2auth/common/hmac.py
create_response(pairing_token, challenge, deployment, domain_name, fingerprint, algorithm=HmacHashingAlgorithm.SHA256)
¶
Source code in src/s2auth/common/hmac.py
create_pairing_code(s2_node_id=None, length=9)
¶
Create pairing code, which is [pairing S2 node ID]-[pairing token] if the S2 node id is set otherwise just the token
Source code in src/s2auth/common/hmac.py
generate_access_token()
¶
select_algorithm(node_algorithms)
¶
Source code in src/s2auth/common/hmac.py
verify_response(pairing_token, challenge, response, deployment, domain_name, fingerprint, algorithm=HmacHashingAlgorithm.SHA256)
¶
Verify that a received challenge response signature for correctness based on pairing token and algorithm.
Source code in src/s2auth/common/hmac.py
config()
async
¶
get_server_connection_initiation_endpoint(authentication_context, server_settings=Depends[settings])
async
¶
Default hook implementation to get the server connection initiation endpoint.
This hook is called during the pairing phase to retrieve the server's endpoint so the S2 Client Node can connect to the server side to establish an S2 connection.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
authentication_context
|
ReadOnlyAuthenticationContext
|
Read-only view of the authentication context (contains client_node_id, state, etc.) |
required |
server_settings
|
Settings
|
Server configuration settings |
Depends[settings]
|
Source code in src/s2auth/server/hooks.py
get_server_endpoint_description(client_node_id, server_settings=Depends[settings])
async
¶
Default hook implementation for getting the server endpoint description.
This hook is called during the pairing request phase and connection initialization phase to generate the server's S2 endpoint descriptions. Override this hook to customize the server's identity or to refuse pairing by raising an S2ConnectError.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
client_node_id
|
NodeId
|
NodeId of the client. |
required |
server_settings
|
Settings
|
Server configuration settings |
Depends[settings]
|
Returns:
| Type | Description |
|---|---|
EndpointDescription
|
EndpointDescription for the server |
Source code in src/s2auth/server/hooks.py
get_server_node_description(client_node_id, server_settings=Depends[settings])
async
¶
Default hook implementation for pairing request.
This hook is called to get the server node description the pairing and connection initiation phase to generate the server's S2 endpoint and node descriptions. Override this hook to customize the server's identity.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
client_node_id
|
NodeId
|
NodeId of the client. |
required |
server_settings
|
Settings
|
Server configuration settings |
Depends[settings]
|
Returns:
| Type | Description |
|---|---|
NodeDescription
|
NodeDescription for the server |
Source code in src/s2auth/server/hooks.py
hook_registry()
¶
pairing_attempt_request(authentication_context, pairing_context, server_settings=Depends[settings])
async
¶
Default hok implementation during pairing attempt requests.
This hook is called during pairing attempt requests to allow custom code to accept/refuse the pairing attempt. If it returns True, the pairing attempt request is allowed. On errors or False it is refused.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
authentication_context
|
ReadOnlyAuthenticationContext
|
Read-only view of the authentication context (contains client_node_id, state, etc.) |
required |
pairing_context
|
ReadOnlyPairingAttemptContext
|
Read-only view of the pairing attempt context (contains pairing_attempt_id, pairing_token, etc.) |
required |
server_settings
|
Settings
|
Server configuration settings |
Depends[settings]
|
Returns:
| Type | Description |
|---|---|
bool
|
Boolean whether the pairing is allowed |
Raises:
| Type | Description |
|---|---|
S2ConnectError
|
To refuse the pairing attempt with a specific error message |
Source code in src/s2auth/server/hooks.py
settings()
¶
resolve_pairing_token(server_settings, generated_token)
¶
Resolve the token to use for a new pairing attempt.
Resolution order: 1. Pending one-time token generated during runtime (if not expired) 2. Startup DEFAULT_PAIRING_TOKEN (one-time, only if still within TTL) 3. Generated fallback token
Source code in src/s2auth/server/token_manager.py
_pairing_token_expiry(ttl_seconds)
¶
_is_pairing_token_expired(pairing_context)
¶
_effective_deployment(auth_ctx, server_settings)
¶
Source code in src/s2auth/server/pairing.py
_effective_fingerprint(deployment, cfg, server_settings)
¶
Source code in src/s2auth/server/pairing.py
initiate_pairing(client_node_id, store_pairing_ctx=Depends[store_pairing_attempt_context], server_settings=Depends[settings], pairing_token=Depends[create_pairing_code])
async
¶
Create and store a pairing attempt for a client node.
This function is the supported Python API for starting pairing state.
In-process callers should invoke initiate_pairing directly and either
provide a pairing token explicitly or rely on the configured token provider.
Source code in src/s2auth/server/pairing.py
unpair(auth_ctx=Depends[authentication_context], pairing_context=Depends[pairing_attempt_context_by_client_node_id], storage=Depends[context_storage_singleton])
async
¶
Remove the authentication and pairing contexts for a paired client.
Source code in src/s2auth/server/pairing.py
request_pairing(request, store_authentication_ctx=Depends[store_authentication_context], storage=Depends[context_storage_singleton], hooks=Depends[hook_registry], cfg=Depends[config], server_settings=Depends[settings], generated_pairing_token=Depends[create_pairing_code])
async
¶
Initiate a new pairing attempt.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
request
|
RequestPairingPostRequest
|
The pairing request containing client descriptions |
required |
store_authentication_ctx
|
Callable[[AuthenticationContext], Awaitable[None]]
|
Function to store authentication context |
Depends[store_authentication_context]
|
hooks
|
HookRegistry
|
Hook registry for calling server hooks |
Depends[hook_registry]
|
Returns:
| Type | Description |
|---|---|
RequestPairingPostResponse
|
The pairing response with server descriptions and challenge |
Source code in src/s2auth/server/pairing.py
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 | |
handle_client_response(request, pairing_context=Depends[pairing_attempt_context], auth_ctx=Depends[authentication_context_by_pairing_attempt_context], hooks=Depends[hook_registry], new_access_token=Depends[generate_access_token], cfg=Depends[config], server_settings=Depends[settings])
async
¶
Handle the client's response and return the server's connection details.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
request
|
RequestConnectionDetailsPostRequest
|
The request from the client for connection details |
required |
pairing_context
|
PairingAttemptContext
|
The pairing attempt context |
Depends[pairing_attempt_context]
|
auth_ctx
|
AuthenticationContext
|
The authentication context with its connection and endpoint details |
Depends[authentication_context_by_pairing_attempt_context]
|
hooks
|
HookRegistry
|
Hook registry for calling server hooks |
Depends[hook_registry]
|
Returns:
| Type | Description |
|---|---|
ConnectionDetails
|
The ConnectionDetails for the client to setup the s2 connection. |
Source code in src/s2auth/server/pairing.py
finalize_pairing(request, pairing_context=Depends[pairing_attempt_context], auth_ctx=Depends[authentication_context_by_pairing_attempt_context])
async
¶
Finalize a completed pairing attempt.
The client calls finalizePairing after it has successfully stored the
connection details returned by requestConnectionDetails. Only then is the
authentication context marked as paired.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
request
|
FinalizePairingPostRequest
|
Finalization request with the client-reported success flag. |
required |
pairing_context
|
PairingAttemptContext
|
Pairing attempt context loaded from context storage. |
Depends[pairing_attempt_context]
|
auth_ctx
|
AuthenticationContext
|
Authentication context loaded from context storage. |
Depends[authentication_context_by_pairing_attempt_context]
|
Raises:
| Type | Description |
|---|---|
PairingNotCompleteError
|
If the pairing attempt has not reached the completed state. |