-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathcore.rs
More file actions
159 lines (146 loc) · 5.42 KB
/
core.rs
File metadata and controls
159 lines (146 loc) · 5.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
use crate::config::ClientConfig;
use crate::error::{OpClientError, Result};
use crate::http_signature::{jwk::Jwk, load_or_generate_key};
use ed25519_dalek::SigningKey;
use reqwest::{Client, Client as ReqwestClient};
/// Base trait for HTTP clients that provides access to the underlying reqwest client.
///
/// This trait is implemented by both authenticated and unauthenticated clients,
/// allowing generic code to work with either type.
pub trait BaseClient {
/// Returns a reference to the underlying reqwest HTTP client.
fn http_client(&self) -> &ReqwestClient;
}
/// An authenticated Open Payments client that can make signed HTTP requests.
///
/// This client automatically handles HTTP message signature creation for all requests
/// using the configured private key. It's used for operations that require authentication
/// such as creating payments, quotes and managing access tokens.
///
/// ## Example
///
/// ```rust,no_run
/// use open_payments::client::{AuthenticatedClient, ClientConfig, AuthenticatedResources, UnauthenticatedResources};
///
/// #[tokio::main]
/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
/// // In a real application, you would use actual file paths
/// let config = ClientConfig {
/// private_key_path: "path/to/private-key.pem".into(),
/// key_id: "my-key-id".to_string(),
/// jwks_path: Some("path/to/jwks.json".into()),
/// wallet_address_url: "https://rafiki.money/alice".into(),
/// };
///
/// // This would fail in a real scenario if the files don't exist
/// // but demonstrates the API usage
/// let _client = AuthenticatedClient::new(config)?;
/// Ok(())
/// }
/// ```
pub struct AuthenticatedOpenPaymentsClient {
/// The underlying HTTP client for making requests.
pub http_client: ReqwestClient,
/// Client configuration including key paths and identifiers.
pub config: ClientConfig,
/// The signing key used for HTTP message signatures.
pub signing_key: SigningKey,
}
impl BaseClient for AuthenticatedOpenPaymentsClient {
fn http_client(&self) -> &ReqwestClient {
&self.http_client
}
}
impl AuthenticatedOpenPaymentsClient {
/// Creates a new authenticated client with the given configuration.
///
/// This method will:
/// 1. Load or generate the signing key from the specified path
/// 2. Generate and save JWKS if a JWKS path is provided
/// 3. Create an HTTP client for making requests
///
/// # Arguments
///
/// * `config` - The client configuration containing key paths and identifiers
///
/// # Returns
///
/// Returns a configured authenticated client or an error if key loading fails.
///
/// # Errors
///
/// Returns an `OpClientError` with:
/// - `description`: Human-readable error message
/// - `status`: HTTP status text (for HTTP errors)
/// - `code`: HTTP status code (for HTTP errors)
/// - `validation_errors`: List of validation errors (if applicable)
/// - `details`: Additional error details (if applicable)
pub fn new(config: ClientConfig) -> Result<Self> {
let http_client = ReqwestClient::new();
let signing_key = load_or_generate_key(&config.private_key_path).map_err(|e| {
OpClientError::signature(format!("Failed to load or generate signing key: {e}"))
})?;
if let Some(ref jwks_path) = config.jwks_path {
let jwks_json = Jwk::generate_jwks_json(&signing_key, &config.key_id);
Jwk::save_jwks(&jwks_json, jwks_path).map_err(|e| {
OpClientError::signature(format!("Failed to save JWK to file: {e}"))
})?;
}
Ok(Self {
http_client,
config,
signing_key,
})
}
}
/// An unauthenticated Open Payments client for making public requests.
///
/// This client is used for operations that don't require authentication,
/// such as retrieving public wallet address information or accessing
/// publicly available resources e.g. public incoming payments.
///
/// ## Example
///
/// ```rust,no_run
/// use open_payments::client::{UnauthenticatedClient, UnauthenticatedResources};
///
/// #[tokio::main]
/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let client = UnauthenticatedClient::new();
/// // This would make an actual HTTP request in a real scenario
/// // but demonstrates the API usage
/// let _wallet_address = client.wallet_address().get("https://rafiki.money/alice").await?;
/// Ok(())
/// }
/// ```
pub struct UnauthenticatedOpenPaymentsClient {
pub http_client: ReqwestClient,
}
impl Default for UnauthenticatedOpenPaymentsClient {
fn default() -> Self {
Self::new()
}
}
impl UnauthenticatedOpenPaymentsClient {
/// Creates a new unauthenticated client.
///
/// This creates a simple HTTP client without any authentication configuration.
/// It's suitable for accessing public endpoints that don't require signatures.
pub fn new() -> Self {
Self {
http_client: ReqwestClient::new(),
}
}
}
impl BaseClient for UnauthenticatedOpenPaymentsClient {
fn http_client(&self) -> &ReqwestClient {
&self.http_client
}
}
impl BaseClient for Client {
fn http_client(&self) -> &ReqwestClient {
self
}
}
pub use self::AuthenticatedOpenPaymentsClient as AuthenticatedClient;
pub use self::UnauthenticatedOpenPaymentsClient as UnauthenticatedClient;