nautilus_deribit/websocket/
auth.rs1use std::{
19 sync::{
20 Arc,
21 atomic::{AtomicU64, Ordering},
22 },
23 time::Duration,
24};
25
26use nautilus_common::live::runtime::get_runtime;
27use nautilus_core::{UUID4, time::get_atomic_clock_realtime};
28
29use super::{
30 handler::HandlerCommand,
31 messages::{DeribitAuthParams, DeribitAuthResult, DeribitRefreshTokenParams},
32};
33use crate::common::{credential::Credential, rpc::DeribitJsonRpcRequest};
34
35pub const DEFAULT_SESSION_NAME: &str = "nautilus";
37
38#[derive(Debug, Clone)]
40pub struct AuthState {
41 pub access_token: String,
43 pub refresh_token: String,
45 pub expires_in: u64,
47 pub obtained_at: u64,
49 pub scope: String,
51}
52
53impl AuthState {
54 #[must_use]
56 pub fn from_auth_result(result: &DeribitAuthResult, obtained_at: u64) -> Self {
57 Self {
58 access_token: result.access_token.clone(),
59 refresh_token: result.refresh_token.clone(),
60 expires_in: result.expires_in,
61 obtained_at,
62 scope: result.scope.clone(),
63 }
64 }
65
66 #[must_use]
68 pub fn expires_at_ms(&self) -> u64 {
69 self.obtained_at + (self.expires_in * 1000)
70 }
71
72 #[must_use]
74 pub fn is_expired(&self, current_time_ms: u64) -> bool {
75 current_time_ms + 60_000 >= self.expires_at_ms()
77 }
78
79 #[must_use]
81 pub fn is_session_scoped(&self) -> bool {
82 self.scope.starts_with("session:")
83 }
84}
85
86pub fn send_auth_request(
99 credential: &Credential,
100 scope: Option<String>,
101 cmd_tx: &tokio::sync::mpsc::UnboundedSender<HandlerCommand>,
102 request_id_counter: &Arc<AtomicU64>,
103) {
104 let timestamp = get_atomic_clock_realtime().get_time_ms();
105 let nonce = UUID4::new().to_string();
106 let signature = credential.sign_ws_auth(timestamp, &nonce, "");
107
108 let auth_params = DeribitAuthParams {
109 grant_type: "client_signature".to_string(),
110 client_id: credential.api_key.to_string(),
111 timestamp,
112 signature,
113 nonce,
114 data: String::new(),
115 scope,
116 };
117
118 let request_id = request_id_counter.fetch_add(1, Ordering::Relaxed);
119 let request = DeribitJsonRpcRequest::new(request_id, "public/auth", auth_params);
120
121 if let Ok(payload) = serde_json::to_string(&request) {
122 let _ = cmd_tx.send(HandlerCommand::Authenticate { payload });
123 }
124}
125
126pub fn spawn_token_refresh_task(
132 expires_in: u64,
133 refresh_token: String,
134 cmd_tx: tokio::sync::mpsc::UnboundedSender<HandlerCommand>,
135 request_id_counter: Arc<AtomicU64>,
136) {
137 let refresh_delay_secs = (expires_in as f64 * 0.8) as u64;
139
140 get_runtime().spawn(async move {
141 tracing::debug!(
142 "Token refresh scheduled in {}s (token expires in {}s)",
143 refresh_delay_secs,
144 expires_in
145 );
146 tokio::time::sleep(Duration::from_secs(refresh_delay_secs)).await;
147
148 tracing::info!("Refreshing authentication token...");
149 let refresh_params = DeribitRefreshTokenParams {
150 grant_type: "refresh_token".to_string(),
151 refresh_token,
152 };
153
154 let request_id = request_id_counter.fetch_add(1, Ordering::Relaxed);
155 let request = DeribitJsonRpcRequest::new(request_id, "public/auth", refresh_params);
156
157 if let Ok(payload) = serde_json::to_string(&request) {
158 let _ = cmd_tx.send(HandlerCommand::Authenticate { payload });
159 }
160 });
161}