- Added `NativeClient` for non-WASM environments with automatic reconnection and message handling. - Introduced `WasmClient` for WASM environments, supporting WebSocket communication and reconnection logic. - Created protocol definitions for `SignRequest` and `SignResponse` with serialization and deserialization. - Developed integration tests for the client functionality and sign request handling. - Implemented WASM-specific tests to ensure compatibility and functionality in browser environments.
219 lines
5.5 KiB
Markdown
219 lines
5.5 KiB
Markdown
# SigSocket Client
|
|
|
|
A WebSocket client library for connecting to sigsocket servers with **WASM-first support**.
|
|
|
|
## Features
|
|
|
|
- 🌐 **WASM-first design**: Optimized for browser environments
|
|
- 🖥️ **Native support**: Works in native Rust applications
|
|
- 🔐 **No signing logic**: Delegates signing to the application
|
|
- 👤 **User approval flow**: Notifies applications about incoming requests
|
|
- 🔌 **sigsocket compatible**: Fully compatible with sigsocket server protocol
|
|
- 🚀 **Async/await**: Modern async Rust API
|
|
- 🔄 **Automatic reconnection**: Both platforms support reconnection with exponential backoff
|
|
- ⏱️ **Connection timeouts**: Proper timeout handling and connection management
|
|
- 🛡️ **Production ready**: Comprehensive error handling and reliability features
|
|
|
|
## Quick Start
|
|
|
|
### Native Usage
|
|
|
|
```rust
|
|
use sigsocket_client::{SigSocketClient, SignRequestHandler, SignRequest, Result};
|
|
|
|
struct MySignHandler;
|
|
|
|
impl SignRequestHandler for MySignHandler {
|
|
fn handle_sign_request(&self, request: &SignRequest) -> Result<Vec<u8>> {
|
|
// 1. Present request to user
|
|
println!("Sign request: {}", request.message);
|
|
|
|
// 2. Get user approval
|
|
// ... your UI logic here ...
|
|
|
|
// 3. Sign the message (using your signing logic)
|
|
let signature = your_signing_function(&request.message_bytes()?)?;
|
|
|
|
Ok(signature)
|
|
}
|
|
}
|
|
|
|
#[tokio::main]
|
|
async fn main() -> Result<()> {
|
|
// Your public key bytes
|
|
let public_key = hex::decode("02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388")?;
|
|
|
|
// Create and configure client
|
|
let mut client = SigSocketClient::new("ws://localhost:8080/ws", public_key)?;
|
|
client.set_sign_handler(MySignHandler);
|
|
|
|
// Connect and handle requests
|
|
client.connect().await?;
|
|
|
|
// Client will automatically handle incoming signature requests
|
|
// Keep the connection alive...
|
|
|
|
Ok(())
|
|
}
|
|
```
|
|
|
|
### WASM Usage
|
|
|
|
```rust
|
|
use sigsocket_client::{SigSocketClient, SignRequestHandler, SignRequest, Result};
|
|
use wasm_bindgen::prelude::*;
|
|
|
|
struct WasmSignHandler;
|
|
|
|
impl SignRequestHandler for WasmSignHandler {
|
|
fn handle_sign_request(&self, request: &SignRequest) -> Result<Vec<u8>> {
|
|
// Show request to user in browser
|
|
web_sys::window()
|
|
.unwrap()
|
|
.alert_with_message(&format!("Sign request: {}", request.id))
|
|
.unwrap();
|
|
|
|
// Your signing logic here...
|
|
let signature = sign_with_browser_wallet(&request.message_bytes()?)?;
|
|
Ok(signature)
|
|
}
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
pub async fn connect_to_sigsocket() -> Result<(), JsValue> {
|
|
let public_key = get_user_public_key()?;
|
|
|
|
let mut client = SigSocketClient::new("ws://localhost:8080/ws", public_key)
|
|
.map_err(|e| JsValue::from_str(&e.to_string()))?;
|
|
|
|
client.set_sign_handler(WasmSignHandler);
|
|
|
|
client.connect().await
|
|
.map_err(|e| JsValue::from_str(&e.to_string()))?;
|
|
|
|
Ok(())
|
|
}
|
|
```
|
|
|
|
## Protocol
|
|
|
|
The sigsocket client implements a simple WebSocket protocol:
|
|
|
|
### 1. Introduction
|
|
Upon connection, the client sends its public key as a hex-encoded string:
|
|
```
|
|
02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388
|
|
```
|
|
|
|
### 2. Sign Requests
|
|
The server sends signature requests as JSON:
|
|
```json
|
|
{
|
|
"id": "req_123",
|
|
"message": "dGVzdCBtZXNzYWdl" // base64-encoded message
|
|
}
|
|
```
|
|
|
|
### 3. Sign Responses
|
|
The client responds with signatures as JSON:
|
|
```json
|
|
{
|
|
"id": "req_123",
|
|
"message": "dGVzdCBtZXNzYWdl", // original message
|
|
"signature": "c2lnbmF0dXJl" // base64-encoded signature
|
|
}
|
|
```
|
|
|
|
## API Reference
|
|
|
|
### `SigSocketClient`
|
|
|
|
Main client for connecting to sigsocket servers.
|
|
|
|
#### Methods
|
|
|
|
- `new(url, public_key)` - Create a new client
|
|
- `set_sign_handler(handler)` - Set the signature request handler
|
|
- `connect()` - Connect to the server with automatic reconnection
|
|
- `disconnect()` - Disconnect from the server
|
|
- `send_sign_response(response)` - Manually send a signature response
|
|
- `state()` - Get current connection state
|
|
- `is_connected()` - Check if connected
|
|
|
|
#### Reconnection Configuration (WASM only)
|
|
|
|
- `set_auto_reconnect(enabled)` - Enable/disable automatic reconnection
|
|
- `set_reconnect_config(max_attempts, initial_delay_ms)` - Configure reconnection parameters
|
|
|
|
**Default settings:**
|
|
- Max attempts: 5
|
|
- Initial delay: 1000ms (with exponential backoff: 1s, 2s, 4s, 8s, 16s)
|
|
- Auto-reconnect: enabled
|
|
|
|
### `SignRequestHandler` Trait
|
|
|
|
Implement this trait to handle incoming signature requests.
|
|
|
|
```rust
|
|
trait SignRequestHandler {
|
|
fn handle_sign_request(&self, request: &SignRequest) -> Result<Vec<u8>>;
|
|
}
|
|
```
|
|
|
|
### `SignRequest`
|
|
|
|
Represents a signature request from the server.
|
|
|
|
#### Fields
|
|
- `id: String` - Unique request identifier
|
|
- `message: String` - Base64-encoded message to sign
|
|
|
|
#### Methods
|
|
- `message_bytes()` - Decode message to bytes
|
|
- `message_hex()` - Get message as hex string
|
|
|
|
### `SignResponse`
|
|
|
|
Represents a signature response to send to the server.
|
|
|
|
#### Methods
|
|
- `new(id, message, signature)` - Create a new response
|
|
- `from_request_and_signature(request, signature)` - Create from request and signature bytes
|
|
|
|
## Examples
|
|
|
|
Run the basic example:
|
|
|
|
```bash
|
|
cargo run --example basic_usage
|
|
```
|
|
|
|
## Building
|
|
|
|
### Native Build
|
|
```bash
|
|
cargo build
|
|
cargo test
|
|
cargo run --example basic_usage
|
|
```
|
|
|
|
### WASM Build
|
|
```bash
|
|
wasm-pack build --target web
|
|
wasm-pack test --headless --firefox # Run WASM tests
|
|
```
|
|
|
|
## Requirements
|
|
|
|
### Native
|
|
- Rust 1.70+
|
|
- tokio runtime
|
|
|
|
### WASM
|
|
- wasm-pack
|
|
- Modern browser with WebSocket support
|
|
|
|
## License
|
|
|
|
MIT OR Apache-2.0
|