initial commit
This commit is contained in:
133
interfaces/websocket/server/docs/ARCHITECTURE.md
Normal file
133
interfaces/websocket/server/docs/ARCHITECTURE.md
Normal file
@@ -0,0 +1,133 @@
|
||||
# `server` Architecture
|
||||
|
||||
This document provides a detailed look into the internal architecture of the `server` crate, focusing on its `Actix`-based design, the structure of the authentication service, and the request lifecycle.
|
||||
|
||||
## 1. Core Design: The `Actix` Actor System
|
||||
|
||||
The `server` is built around the `Actix` actor framework, which allows for highly concurrent and stateful handling of network requests. The key components of this design are:
|
||||
|
||||
- **`HttpServer`**: The main `Actix` server instance that listens for incoming TCP connections.
|
||||
- **`App`**: The application factory that defines the routes for the server.
|
||||
- **`CircleWs` Actor**: A dedicated actor that is spawned for each individual WebSocket connection. This is the cornerstone of the server's design, as it allows each client session to be managed in an isolated, stateful manner.
|
||||
|
||||
When a client connects to the `/{circle_pk}` endpoint, the `HttpServer` upgrades the connection to a WebSocket and spawns a new `CircleWs` actor to handle it. The circle public key is extracted from the URL path to identify which circle the client wants to connect to. All further communication with that client, including the entire authentication flow, is then processed by this specific actor instance.
|
||||
|
||||
## 2. Module Structure
|
||||
|
||||
The `server` crate is organized into the following key modules:
|
||||
|
||||
- **`lib.rs`**: The main library file that contains the `spawn_circle_server` function, which sets up and runs the `Actix` server. It also defines the `CircleWs` actor and its message handling logic for all JSON-RPC methods.
|
||||
- **`auth/`**: This module encapsulates all the logic related to the `secp256k1` authentication system.
|
||||
- **`signature_verifier.rs`**: A self-contained utility module that provides the `verify_signature` function. This function performs the core cryptographic verification of the client's signed nonce.
|
||||
- **`types.rs`**: Defines the data structures used within the authentication service.
|
||||
- **`webhook/`**: This module provides HTTP webhook handling capabilities for external services.
|
||||
- **`mod.rs`**: Main webhook module with route configuration and exports.
|
||||
- **`handlers/`**: Contains individual webhook handlers for different providers (Stripe, iDenfy).
|
||||
- **`verifiers.rs`**: Signature verification utilities for webhook authenticity.
|
||||
- **`types.rs`**: Local webhook types (configuration, errors, verification results).
|
||||
|
||||
## 3. Request Lifecycle and Authentication Flow
|
||||
|
||||
The diagram below illustrates the flow of a typical client interaction. The entire process, from fetching a nonce to executing a protected command, occurs over the WebSocket connection and is handled by the `CircleWs` actor.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client
|
||||
participant ActixHttpServer as HttpServer
|
||||
participant CircleWsActor as CircleWs Actor
|
||||
participant SignatureVerifier as auth::signature_verifier
|
||||
|
||||
Client->>+ActixHttpServer: Establishes WebSocket connection
|
||||
ActixHttpServer->>ActixHttpServer: Spawns a new CircleWsActor
|
||||
ActixHttpServer-->>-Client: WebSocket connection established
|
||||
|
||||
Note over CircleWsActor: Session created, authenticated = false
|
||||
|
||||
Client->>+CircleWsActor: Sends "fetch_nonce" JSON-RPC message
|
||||
CircleWsActor->>CircleWsActor: Generate and store nonce for pubkey
|
||||
CircleWsActor-->>-Client: Returns nonce in JSON-RPC response
|
||||
|
||||
Client->>Client: Signs nonce with private key
|
||||
|
||||
Client->>+CircleWsActor: Sends "authenticate" JSON-RPC message
|
||||
CircleWsActor->>+SignatureVerifier: verify_signature(pubkey, nonce, signature)
|
||||
SignatureVerifier-->>-CircleWsActor: Returns verification result
|
||||
|
||||
alt Signature is Valid
|
||||
CircleWsActor->>CircleWsActor: Set session state: authenticated = true
|
||||
CircleWsActor-->>-Client: Returns success response
|
||||
else Signature is Invalid
|
||||
CircleWsActor-->>-Client: Returns error response
|
||||
end
|
||||
|
||||
Note over CircleWsActor: Client is now authenticated
|
||||
|
||||
Client->>+CircleWsActor: Sends "play" JSON-RPC message
|
||||
CircleWsActor->>CircleWsActor: Check if authenticated
|
||||
alt Is Authenticated
|
||||
CircleWsActor->>CircleWsActor: Get public key from authenticated connections map
|
||||
CircleWsActor->>CircleWsActor: Execute Rhai script with public key
|
||||
CircleWsActor-->>-Client: Returns script result
|
||||
else Is Not Authenticated
|
||||
CircleWsActor-->>-Client: Returns "Authentication Required" error
|
||||
end
|
||||
```
|
||||
|
||||
This architecture ensures a clear separation of concerns and a unified communication protocol:
|
||||
- The `HttpServer` handles connection management.
|
||||
- The `CircleWs` actor manages the entire session lifecycle, including state and all API logic.
|
||||
- The `auth` module provides a self-contained, reusable signature verification utility.
|
||||
|
||||
## 4. Webhook Integration Architecture
|
||||
|
||||
In addition to WebSocket connections, the server supports HTTP webhook endpoints for external services. This integration runs alongside the WebSocket functionality without interference.
|
||||
|
||||
### Webhook Request Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant WS as Webhook Service
|
||||
participant HS as HttpServer
|
||||
participant WH as Webhook Handler
|
||||
participant WV as Webhook Verifier
|
||||
participant RC as RhaiDispatcher
|
||||
participant Redis as Redis
|
||||
|
||||
WS->>+HS: POST /webhooks/{provider}/{circle_pk}
|
||||
HS->>+WH: Route to appropriate handler
|
||||
WH->>WH: Extract circle_pk and signature
|
||||
WH->>+WV: Verify webhook signature
|
||||
WV->>WV: HMAC verification with provider secret
|
||||
WV-->>-WH: Verification result + caller_id
|
||||
|
||||
alt Signature Valid
|
||||
WH->>WH: Parse webhook payload (heromodels types)
|
||||
WH->>+RC: Create RhaiDispatcher with caller_id
|
||||
RC->>+Redis: Execute webhook script
|
||||
Redis-->>-RC: Script result
|
||||
RC-->>-WH: Execution result
|
||||
WH-->>-HS: HTTP 200 OK
|
||||
else Signature Invalid
|
||||
WH-->>-HS: HTTP 401 Unauthorized
|
||||
end
|
||||
HS-->>-WS: HTTP Response
|
||||
```
|
||||
|
||||
### Key Webhook Components
|
||||
|
||||
- **Modular Handlers**: Separate handlers for each webhook provider (Stripe, iDenfy)
|
||||
- **Signature Verification**: HMAC-based verification using provider-specific secrets
|
||||
- **Type Safety**: Webhook payload types defined in `heromodels` library for reusability
|
||||
- **Script Integration**: Uses the same Redis-based Rhai execution system as WebSocket connections
|
||||
- **Isolated Processing**: Webhook processing doesn't affect WebSocket connections
|
||||
|
||||
### Webhook vs WebSocket Comparison
|
||||
|
||||
| Aspect | WebSocket | Webhook |
|
||||
|--------|-----------|---------|
|
||||
| **Connection Type** | Persistent, bidirectional | HTTP request/response |
|
||||
| **Authentication** | secp256k1 signature-based | HMAC signature verification |
|
||||
| **State Management** | Stateful sessions via CircleWs actor | Stateless HTTP requests |
|
||||
| **Script Execution** | Direct via authenticated session | Via RhaiDispatcher with provider caller_id |
|
||||
| **Use Case** | Interactive client applications | External service notifications |
|
||||
| **Data Types** | JSON-RPC messages | Provider-specific webhook payloads (heromodels) |
|
214
interfaces/websocket/server/docs/authentication.md
Normal file
214
interfaces/websocket/server/docs/authentication.md
Normal file
@@ -0,0 +1,214 @@
|
||||
# WebSocket Server Authentication
|
||||
|
||||
This document describes the optional authentication features added to the Circle WebSocket server.
|
||||
|
||||
## Overview
|
||||
|
||||
The WebSocket server now supports optional secp256k1 signature-based authentication while maintaining full backward compatibility with existing clients. Authentication is completely opt-in and can be enabled per server instance.
|
||||
|
||||
## Features
|
||||
|
||||
### 1. Optional Authentication
|
||||
- **Backward Compatible**: Existing clients continue to work without any changes
|
||||
- **Opt-in**: Authentication can be enabled/disabled per server instance
|
||||
- **Graceful Degradation**: Servers can accept both authenticated and unauthenticated connections
|
||||
|
||||
### 2. Nonce-based Security
|
||||
- **Nonce Endpoints**: REST API for requesting cryptographic nonces
|
||||
- **Replay Protection**: Each nonce can only be used once
|
||||
- **Expiration**: Nonces expire after 5 minutes
|
||||
- **Health Monitoring**: Health endpoint for monitoring nonce service
|
||||
|
||||
### 3. Signature Verification
|
||||
- **secp256k1**: Uses the same cryptographic standard as Ethereum
|
||||
- **Ethereum-style Signing**: Compatible with eth_sign message format
|
||||
- **Public Key Recovery**: Verifies signatures against provided public keys
|
||||
|
||||
## API Endpoints
|
||||
|
||||
These HTTP API endpoints are served by the WebSocket server instance itself, on the same host and port where the WebSocket service is running.
|
||||
|
||||
### Nonce Request
|
||||
```
|
||||
GET /auth/nonce?public_key=<optional_public_key>
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"nonce": "nonce_1234567890_abcdef",
|
||||
"expires_at": 1234567890
|
||||
}
|
||||
```
|
||||
|
||||
### Health Check
|
||||
```
|
||||
GET /auth/health
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"status": "healthy",
|
||||
"active_nonces": 42,
|
||||
"timestamp": 1234567890
|
||||
}
|
||||
```
|
||||
|
||||
## WebSocket Authentication
|
||||
|
||||
### Query Parameters
|
||||
Clients can authenticate by including these query parameters in the WebSocket URL:
|
||||
|
||||
- `pubkey`: The client's public key in hex format (130 characters, uncompressed)
|
||||
- `sig`: The signature of the nonce in hex format (130 characters)
|
||||
- `nonce`: The nonce that was signed (optional)
|
||||
|
||||
**Example:**
|
||||
```
|
||||
ws://localhost:8080/{circle_pk}?pubkey=04abc123...&sig=def456...&nonce=nonce_123_abc
|
||||
```
|
||||
|
||||
### Authentication Flow
|
||||
1. **Request Nonce**: Client requests a nonce from `/auth/nonce`
|
||||
2. **Sign Nonce**: Client signs the nonce with their private key
|
||||
3. **Connect**: Client connects to WebSocket with `pubkey` and `sig` parameters
|
||||
4. **Verify**: Server verifies the signature and accepts/rejects the connection
|
||||
|
||||
## Server Configuration
|
||||
|
||||
### Basic Server (No Authentication)
|
||||
```rust
|
||||
use circle_ws_lib::{ServerConfig, spawn_circle_server};
|
||||
|
||||
let config = ServerConfig::new(
|
||||
"localhost".to_string(),
|
||||
8080,
|
||||
"redis://localhost".to_string(),
|
||||
);
|
||||
|
||||
let (server_task, server_handle) = spawn_circle_server(config)?;
|
||||
```
|
||||
|
||||
### Server with Authentication
|
||||
```rust
|
||||
use circle_ws_lib::{ServerConfig, spawn_circle_server};
|
||||
|
||||
let config = ServerConfig::new(
|
||||
"localhost".to_string(),
|
||||
8080,
|
||||
"redis://localhost".to_string(),
|
||||
).with_auth();
|
||||
|
||||
let (server_task, server_handle) = spawn_circle_server(config)?;
|
||||
```
|
||||
|
||||
## Client Integration
|
||||
|
||||
### JavaScript/TypeScript Example
|
||||
```javascript
|
||||
// 1. Request nonce (from the WebSocket server's HTTP interface)
|
||||
const nonceResponse = await fetch('http://localhost:8080/auth/nonce');
|
||||
const { nonce } = await nonceResponse.json();
|
||||
|
||||
// 2. Sign nonce (using your preferred secp256k1 library)
|
||||
const signature = signMessage(privateKey, nonce);
|
||||
const publicKey = derivePublicKey(privateKey);
|
||||
|
||||
// 3. Connect with authentication (replace {circle_pk} with actual circle public key)
|
||||
const ws = new WebSocket(
|
||||
`ws://localhost:8080/${circle_pk}?pubkey=${publicKey}&sig=${signature}&nonce=${nonce}`
|
||||
);
|
||||
```
|
||||
|
||||
### Rust Client Example
|
||||
```rust
|
||||
use circle_ws_lib::auth::*;
|
||||
|
||||
// Request nonce. NonceClient will derive the HTTP API path from this WebSocket URL.
|
||||
let nonce_client = NonceClient::from_ws_url("ws://localhost:8080/{circle_pk}")?;
|
||||
let nonce_response = nonce_client.request_nonce(Some(public_key)).await?;
|
||||
|
||||
// Sign nonce
|
||||
let signature = sign_message(&private_key, &nonce_response.nonce)?;
|
||||
|
||||
// Connect with authentication (replace {circle_pk} with actual circle public key)
|
||||
let ws_url = format!(
|
||||
"ws://localhost:8080/{}?pubkey={}&sig={}",
|
||||
circle_pk, public_key, signature
|
||||
);
|
||||
```
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Nonce Management
|
||||
- Nonces expire after 5 minutes
|
||||
- Each nonce can only be used once
|
||||
- Nonces are stored in memory (consider Redis for production)
|
||||
|
||||
### Signature Security
|
||||
- Uses secp256k1 elliptic curve cryptography
|
||||
- Ethereum-style message signing for compatibility
|
||||
- Public key verification prevents impersonation
|
||||
|
||||
### Backward Compatibility
|
||||
- Unauthenticated connections are allowed by default
|
||||
- No breaking changes to existing APIs
|
||||
- Optional authentication can be enabled gradually
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Authentication Errors
|
||||
- **401 Unauthorized**: Authentication required but not provided
|
||||
- **403 Forbidden**: Authentication provided but invalid
|
||||
- **400 Bad Request**: Malformed authentication parameters
|
||||
|
||||
### Nonce Errors
|
||||
- **404 Not Found**: Nonce endpoint not available
|
||||
- **410 Gone**: Nonce expired or already used
|
||||
- **429 Too Many Requests**: Rate limiting (if implemented)
|
||||
|
||||
## Monitoring
|
||||
|
||||
### Metrics
|
||||
- Active nonce count via `/auth/health`
|
||||
- Authentication success/failure rates in logs
|
||||
- Connection counts by authentication status
|
||||
|
||||
### Logging
|
||||
```
|
||||
INFO Incoming WebSocket connection for circle: 04abc123... (auth_enabled: true)
|
||||
INFO Authentication successful for pubkey: 04abc123...
|
||||
WARN Authentication failed: invalid signature
|
||||
```
|
||||
|
||||
## Production Considerations
|
||||
|
||||
### Scalability
|
||||
- Consider Redis-backed nonce storage for multiple server instances
|
||||
- Implement rate limiting for nonce requests
|
||||
- Monitor memory usage of in-memory nonce storage
|
||||
|
||||
### Security
|
||||
- Use HTTPS/WSS in production
|
||||
- Implement proper key management
|
||||
- Consider certificate-based authentication for additional security
|
||||
|
||||
### Monitoring
|
||||
- Set up alerts for authentication failure rates
|
||||
- Monitor nonce service health
|
||||
- Track connection patterns and anomalies
|
||||
|
||||
## Migration Guide
|
||||
|
||||
### Existing Deployments
|
||||
1. **No Changes Required**: Existing clients continue to work
|
||||
2. **Gradual Rollout**: Enable authentication on new servers first
|
||||
3. **Client Updates**: Update clients to support authentication when ready
|
||||
4. **Full Migration**: Eventually require authentication on all servers
|
||||
|
||||
### Testing
|
||||
1. Test unauthenticated connections still work
|
||||
2. Test authenticated connections with valid signatures
|
||||
3. Test authentication failures are handled gracefully
|
||||
4. Test nonce expiration and replay protection
|
357
interfaces/websocket/server/docs/webhooks.md
Normal file
357
interfaces/websocket/server/docs/webhooks.md
Normal file
@@ -0,0 +1,357 @@
|
||||
# Webhook Integration Architecture
|
||||
|
||||
## Overview
|
||||
|
||||
This document outlines the architecture for adding webhook handling capabilities to the Circle WebSocket Server. The integration adds HTTP webhook endpoints alongside the existing WebSocket functionality without disrupting the current system.
|
||||
|
||||
## Architecture Diagram
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "External Services"
|
||||
A[Stripe Webhooks]
|
||||
B[iDenfy Webhooks]
|
||||
end
|
||||
|
||||
subgraph "Circle Server"
|
||||
C[HTTP Router]
|
||||
D[WebSocket Handler]
|
||||
E[Webhook Handler]
|
||||
F[Stripe Verifier]
|
||||
G[iDenfy Verifier]
|
||||
H[Script Dispatcher]
|
||||
I[RhaiDispatcherBuilder]
|
||||
end
|
||||
|
||||
subgraph "Configuration"
|
||||
J[.env File]
|
||||
K[Environment Variables]
|
||||
end
|
||||
|
||||
subgraph "Backend"
|
||||
L[Redis]
|
||||
M[Rhai Worker]
|
||||
end
|
||||
|
||||
A --> |POST /webhooks/stripe/{circle_pk}| E
|
||||
B --> |POST /webhooks/idenfy/{circle_pk}| E
|
||||
|
||||
C --> D
|
||||
C --> E
|
||||
|
||||
E --> F
|
||||
E --> G
|
||||
F --> H
|
||||
G --> H
|
||||
H --> I
|
||||
I --> L
|
||||
L --> M
|
||||
|
||||
J --> K
|
||||
K --> F
|
||||
K --> G
|
||||
|
||||
D --> I
|
||||
```
|
||||
|
||||
## URL Structure
|
||||
|
||||
### Webhook Endpoints
|
||||
- **Stripe**: `POST /webhooks/stripe/{circle_pk}`
|
||||
- **iDenfy**: `POST /webhooks/idenfy/{circle_pk}`
|
||||
|
||||
### Existing WebSocket Endpoints (Unchanged)
|
||||
- **WebSocket**: `GET /{circle_pk}` (upgrades to WebSocket)
|
||||
|
||||
## Configuration
|
||||
|
||||
### Environment Variables (.env file)
|
||||
```bash
|
||||
# Webhook secrets for signature verification
|
||||
STRIPE_WEBHOOK_SECRET=whsec_...
|
||||
IDENFY_WEBHOOK_SECRET=your_idenfy_secret
|
||||
|
||||
# Existing configuration
|
||||
REDIS_URL=redis://127.0.0.1/
|
||||
```
|
||||
|
||||
### Server Configuration Updates
|
||||
```rust
|
||||
pub struct ServerConfig {
|
||||
// ... existing fields
|
||||
pub stripe_webhook_secret: Option<String>,
|
||||
pub idenfy_webhook_secret: Option<String>,
|
||||
}
|
||||
```
|
||||
|
||||
## Webhook Processing Flow
|
||||
|
||||
### 1. Request Reception
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant WS as Webhook Service
|
||||
participant CS as Circle Server
|
||||
participant WV as Webhook Verifier
|
||||
participant SD as Script Dispatcher
|
||||
participant RC as RhaiDispatcher
|
||||
participant RW as Rhai Worker
|
||||
|
||||
WS->>CS: POST /webhooks/stripe/{circle_pk}
|
||||
CS->>CS: Extract circle_pk from URL
|
||||
CS->>CS: Read request body and headers
|
||||
CS->>WV: Verify webhook signature
|
||||
|
||||
alt Stripe Webhook
|
||||
WV->>WV: Verify Stripe signature using STRIPE_WEBHOOK_SECRET
|
||||
WV->>WV: Deserialize to Stripe webhook payload
|
||||
else iDenfy Webhook
|
||||
WV->>WV: Verify iDenfy signature using IDENFY_WEBHOOK_SECRET
|
||||
WV->>WV: Deserialize to iDenfy webhook payload
|
||||
end
|
||||
|
||||
WV->>CS: Return verification result + parsed payload
|
||||
|
||||
alt Verification Success
|
||||
CS->>SD: Dispatch appropriate script
|
||||
SD->>RC: Create RhaiDispatcherBuilder
|
||||
RC->>RC: Set caller_id="stripe" or "idenfy"
|
||||
RC->>RC: Set recipient_id=circle_pk
|
||||
RC->>RC: Set script="stripe_webhook_received" or "idenfy_webhook_received"
|
||||
RC->>RW: Execute via Redis
|
||||
RW->>RC: Return result
|
||||
RC->>CS: Script execution result
|
||||
CS->>WS: HTTP 200 OK
|
||||
else Verification Failed
|
||||
CS->>WS: HTTP 401 Unauthorized
|
||||
end
|
||||
```
|
||||
|
||||
### 2. Signature Verification
|
||||
|
||||
#### Stripe Verification
|
||||
- Uses `Stripe-Signature` header
|
||||
- HMAC-SHA256 verification with `STRIPE_WEBHOOK_SECRET`
|
||||
- Follows Stripe's webhook signature verification protocol
|
||||
|
||||
#### iDenfy Verification
|
||||
- Uses appropriate iDenfy signature header
|
||||
- HMAC verification with `IDENFY_WEBHOOK_SECRET`
|
||||
- Follows iDenfy's webhook signature verification protocol
|
||||
|
||||
### 3. Payload Deserialization
|
||||
|
||||
#### Type Definitions in Heromodels Library
|
||||
|
||||
Webhook payload types are now defined in the `heromodels` library for better code organization and reusability:
|
||||
|
||||
- **Stripe Types**: Located in `heromodels::models::payment::stripe`
|
||||
- **iDenfy Types**: Located in `heromodels::models::identity::kyc`
|
||||
|
||||
#### Stripe Payload Structure
|
||||
```rust
|
||||
// From heromodels::models::payment::StripeWebhookEvent
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct StripeWebhookEvent {
|
||||
pub id: String,
|
||||
pub object: String,
|
||||
pub api_version: Option<String>,
|
||||
pub created: i64,
|
||||
pub data: StripeEventData,
|
||||
pub livemode: bool,
|
||||
pub pending_webhooks: i32,
|
||||
pub request: Option<StripeEventRequest>,
|
||||
#[serde(rename = "type")]
|
||||
pub event_type: String,
|
||||
}
|
||||
```
|
||||
|
||||
#### iDenfy Payload Structure
|
||||
```rust
|
||||
// From heromodels::models::identity::IdenfyWebhookEvent
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct IdenfyWebhookEvent {
|
||||
#[serde(rename = "clientId")]
|
||||
pub client_id: String,
|
||||
#[serde(rename = "scanRef")]
|
||||
pub scan_ref: String,
|
||||
pub status: String,
|
||||
pub platform: String,
|
||||
#[serde(rename = "startedAt")]
|
||||
pub started_at: String,
|
||||
#[serde(rename = "finishedAt")]
|
||||
pub finished_at: Option<String>,
|
||||
pub data: Option<IdenfyVerificationData>,
|
||||
// ... additional fields
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Script Execution
|
||||
|
||||
#### Script Names
|
||||
- **Stripe**: `stripe_webhook_received`
|
||||
- **iDenfy**: `idenfy_webhook_received`
|
||||
|
||||
#### Script Context
|
||||
The Rhai scripts will receive structured data:
|
||||
|
||||
```javascript
|
||||
// For Stripe webhooks
|
||||
let webhook_data = {
|
||||
"caller_id": "stripe",
|
||||
"circle_id": "circle_public_key",
|
||||
"event_type": "payment_intent.succeeded",
|
||||
"event_id": "evt_...",
|
||||
"created": 1234567890,
|
||||
"livemode": false,
|
||||
"data": { /* Stripe event data */ }
|
||||
};
|
||||
|
||||
// For iDenfy webhooks
|
||||
let webhook_data = {
|
||||
"caller_id": "idenfy",
|
||||
"circle_id": "circle_public_key",
|
||||
"final_decision": "APPROVED",
|
||||
"platform": "PC",
|
||||
"status": { /* iDenfy status data */ },
|
||||
"data": { /* iDenfy verification data */ }
|
||||
};
|
||||
```
|
||||
|
||||
## Implementation Structure
|
||||
|
||||
### Current File Structure
|
||||
```
|
||||
src/server/src/
|
||||
├── webhook/
|
||||
│ ├── mod.rs # Main webhook module with route configuration
|
||||
│ ├── handlers/
|
||||
│ │ ├── mod.rs # Handler module exports
|
||||
│ │ ├── common.rs # Common utilities and app state
|
||||
│ │ ├── stripe.rs # Stripe webhook handler
|
||||
│ │ └── idenfy.rs # iDenfy webhook handler
|
||||
│ ├── verifiers.rs # Signature verification for all providers
|
||||
│ └── types.rs # Local webhook types (config, errors, etc.)
|
||||
└── .env # Environment configuration
|
||||
```
|
||||
|
||||
### Heromodels Library Structure
|
||||
```
|
||||
heromodels/src/models/
|
||||
├── payment/
|
||||
│ ├── mod.rs # Payment module exports
|
||||
│ └── stripe.rs # Stripe webhook event types
|
||||
└── identity/
|
||||
├── mod.rs # Identity module exports
|
||||
└── kyc.rs # iDenfy KYC webhook event types
|
||||
```
|
||||
|
||||
### Key Architectural Changes
|
||||
- **Type Organization**: Webhook payload types moved to `heromodels` library for reusability
|
||||
- **Modular Handlers**: Separate handler files for each webhook provider
|
||||
- **Simplified Architecture**: Removed unnecessary dispatcher complexity
|
||||
- **Direct Script Execution**: Handlers directly use `RhaiDispatcher` for script execution
|
||||
|
||||
### Modified Files
|
||||
- `src/lib.rs` - Add webhook routes and module imports
|
||||
- `Cargo.toml` - Add heromodels dependency and webhook-related dependencies
|
||||
- `cmd/main.rs` - Load .env file and configure webhook secrets
|
||||
|
||||
### Dependencies
|
||||
```toml
|
||||
[dependencies]
|
||||
# Existing dependencies...
|
||||
|
||||
# Heromodels library for shared types
|
||||
heromodels = { path = "../../../db/heromodels" }
|
||||
|
||||
# For webhook signature verification
|
||||
hmac = "0.12"
|
||||
sha2 = "0.10"
|
||||
hex = { workspace = true }
|
||||
|
||||
# For environment variable loading
|
||||
dotenv = "0.15"
|
||||
|
||||
# For HTTP request handling
|
||||
bytes = "1.0"
|
||||
thiserror = { workspace = true }
|
||||
```
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Signature Verification
|
||||
- **Mandatory**: All webhook requests must have valid signatures
|
||||
- **Timing Attack Protection**: Use constant-time comparison for signatures
|
||||
- **Secret Management**: Webhook secrets loaded from environment variables only
|
||||
|
||||
### Error Handling
|
||||
- **No Information Leakage**: Generic error responses for invalid webhooks
|
||||
- **Logging**: Detailed logging for debugging (same as existing WebSocket errors)
|
||||
- **Graceful Degradation**: Webhook failures don't affect WebSocket functionality
|
||||
|
||||
### Request Validation
|
||||
- **Content-Type**: Verify appropriate content types
|
||||
- **Payload Size**: No explicit limits initially (as requested)
|
||||
- **Rate Limiting**: Consider future implementation
|
||||
|
||||
## Backward Compatibility
|
||||
|
||||
### WebSocket Functionality
|
||||
- **Zero Impact**: Existing WebSocket routes and functionality unchanged
|
||||
- **Authentication**: WebSocket authentication system remains independent
|
||||
- **Performance**: No performance impact on WebSocket connections
|
||||
|
||||
### Configuration
|
||||
- **Optional**: Webhook functionality only enabled when secrets are configured
|
||||
- **Graceful Fallback**: Server starts normally even without webhook configuration
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
### Unit Tests
|
||||
- Webhook signature verification for both providers
|
||||
- Payload deserialization
|
||||
- Error handling scenarios
|
||||
|
||||
### Integration Tests
|
||||
- End-to-end webhook processing
|
||||
- Script dispatch verification
|
||||
- Configuration loading
|
||||
|
||||
### Mock Testing
|
||||
- Simulated Stripe webhook calls
|
||||
- Simulated iDenfy webhook calls
|
||||
- Invalid signature scenarios
|
||||
|
||||
## Deployment Considerations
|
||||
|
||||
### Environment Setup
|
||||
```bash
|
||||
# .env file in src/server/
|
||||
STRIPE_WEBHOOK_SECRET=whsec_1234567890abcdef...
|
||||
IDENFY_WEBHOOK_SECRET=your_idenfy_webhook_secret
|
||||
REDIS_URL=redis://127.0.0.1/
|
||||
```
|
||||
|
||||
### Server Startup
|
||||
- Load .env file before server initialization
|
||||
- Validate webhook secrets if webhook endpoints are to be enabled
|
||||
- Log webhook endpoint availability
|
||||
|
||||
### Monitoring
|
||||
- Log webhook reception and processing
|
||||
- Track script execution success/failure rates
|
||||
- Monitor webhook signature verification failures
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
### Potential Additions
|
||||
- Additional webhook providers
|
||||
- Webhook retry mechanisms
|
||||
- Webhook event filtering
|
||||
- Rate limiting implementation
|
||||
- Webhook event queuing for high-volume scenarios
|
||||
|
||||
### Scalability Considerations
|
||||
- Webhook processing can be made asynchronous if needed
|
||||
- Multiple server instances can handle webhooks independently
|
||||
- Redis-based script execution provides natural load distribution
|
Reference in New Issue
Block a user