1use crate::error::{EMSError, Result};
4use async_trait::async_trait;
5use reqwest::header::HeaderMap;
6use rusty_common::SmartString;
7use std::time::Duration;
8
9#[derive(Debug, Clone)]
11pub struct AuthenticationContext {
12 pub method: SmartString,
14 pub path: SmartString,
16 pub query_params: Option<Vec<(SmartString, SmartString)>>,
18 pub body: Option<SmartString>,
20 pub timestamp: Option<u64>,
22 pub metadata: Option<SmartString>,
24}
25
26impl AuthenticationContext {
27 pub fn new(method: impl Into<SmartString>, path: impl Into<SmartString>) -> Self {
29 Self {
30 method: method.into(),
31 path: path.into(),
32 query_params: None,
33 body: None,
34 timestamp: None,
35 metadata: None,
36 }
37 }
38
39 #[must_use]
41 pub fn with_query_params(mut self, params: Vec<(SmartString, SmartString)>) -> Self {
42 self.query_params = Some(params);
43 self
44 }
45
46 pub fn with_body(mut self, body: impl Into<SmartString>) -> Self {
48 self.body = Some(body.into());
49 self
50 }
51
52 #[must_use]
54 pub const fn with_timestamp(mut self, timestamp: u64) -> Self {
55 self.timestamp = Some(timestamp);
56 self
57 }
58
59 pub fn with_metadata(mut self, metadata: impl Into<SmartString>) -> Self {
61 self.metadata = Some(metadata.into());
62 self
63 }
64}
65
66#[derive(Debug, Clone)]
68pub struct AuthenticationResult {
69 pub headers: HeaderMap,
71 pub signed_query: Option<SmartString>,
73 pub ws_auth_message: Option<SmartString>,
75 pub validity_duration: Option<Duration>,
77}
78
79impl AuthenticationResult {
80 #[must_use]
82 pub const fn new(headers: HeaderMap) -> Self {
83 Self {
84 headers,
85 signed_query: None,
86 ws_auth_message: None,
87 validity_duration: None,
88 }
89 }
90
91 pub fn with_signed_query(mut self, query: impl Into<SmartString>) -> Self {
93 self.signed_query = Some(query.into());
94 self
95 }
96
97 pub fn with_ws_auth_message(mut self, message: impl Into<SmartString>) -> Self {
99 self.ws_auth_message = Some(message.into());
100 self
101 }
102
103 #[must_use]
105 pub const fn with_validity_duration(mut self, duration: Duration) -> Self {
106 self.validity_duration = Some(duration);
107 self
108 }
109}
110
111#[async_trait]
113pub trait AuthenticationManager: Send + Sync {
114 async fn authenticate_rest_request(
116 &self,
117 context: &AuthenticationContext,
118 ) -> Result<AuthenticationResult>;
119
120 async fn authenticate_websocket(&self) -> Result<AuthenticationResult>;
122
123 async fn authenticate_websocket_trading(
125 &self,
126 timestamp: Option<u64>,
127 ) -> Result<AuthenticationResult> {
128 let _ = timestamp;
129 Err(EMSError::not_supported(
130 self.exchange_name(),
131 "WebSocket trading authentication",
132 ))
133 }
134
135 fn is_authentication_valid(&self) -> bool;
137
138 fn time_until_expiration(&self) -> Option<Duration>;
140
141 async fn refresh_authentication(&self) -> Result<()> {
143 Ok(()) }
145
146 fn exchange_name(&self) -> &str;
148
149 fn api_key(&self) -> &str;
151
152 fn supports_websocket_trading(&self) -> bool {
154 false
155 }
156
157 fn requires_refresh(&self) -> bool {
159 false
160 }
161
162 fn refresh_interval(&self) -> Option<Duration> {
164 None
165 }
166}
167
168#[async_trait]
170pub trait AuthenticationAdapter: AuthenticationManager {
171 fn underlying_auth(&self) -> &dyn std::any::Any;
173}
174
175#[derive(Debug, Clone)]
177pub enum WebSocketAuthMessage {
178 Simple(SmartString),
180 Jwt(SmartString),
182 Custom {
184 message: SmartString,
186 metadata: SmartString,
188 },
189}
190
191#[derive(Debug, Clone, Copy, PartialEq, Eq)]
193pub enum AuthenticationMethod {
194 HmacSha256,
196 Ed25519,
198 Ecdsa,
200 JwtHmac,
202 Custom,
204}
205
206#[derive(Debug, Clone)]
208pub struct AuthenticationRequirements {
209 pub rest_required: bool,
211 pub websocket_required: bool,
213 pub websocket_trading_required: bool,
215 pub method: AuthenticationMethod,
217 pub has_expiration: bool,
219 pub default_validity: Option<Duration>,
221}
222
223impl AuthenticationRequirements {
224 #[must_use]
226 pub const fn hmac_based() -> Self {
227 Self {
228 rest_required: true,
229 websocket_required: false,
230 websocket_trading_required: false,
231 method: AuthenticationMethod::HmacSha256,
232 has_expiration: false,
233 default_validity: None,
234 }
235 }
236
237 #[must_use]
239 pub const fn jwt_based() -> Self {
240 Self {
241 rest_required: true,
242 websocket_required: true,
243 websocket_trading_required: false,
244 method: AuthenticationMethod::JwtHmac,
245 has_expiration: true,
246 default_validity: Some(Duration::from_secs(3600)), }
248 }
249
250 #[must_use]
252 pub const fn ed25519_based() -> Self {
253 Self {
254 rest_required: true,
255 websocket_required: true,
256 websocket_trading_required: true,
257 method: AuthenticationMethod::Ed25519,
258 has_expiration: false,
259 default_validity: None,
260 }
261 }
262}