mockforge_http/auth/
token_lifecycle.rs1use chrono::Utc;
10use serde::{Deserialize, Serialize};
11use std::collections::HashMap;
12use std::sync::Arc;
13use tokio::sync::RwLock;
14use sha2::{Digest, Sha256};
15
16use mockforge_core::Error;
17
18#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct RevokedToken {
21 pub token_id: String,
23 pub user_id: Option<String>,
25 pub revoked_at: i64,
27 pub reason: String,
29 pub expires_at: Option<i64>,
31}
32
33#[derive(Debug, Clone)]
35pub struct TokenRevocationStore {
36 revoked_tokens: Arc<RwLock<HashMap<String, RevokedToken>>>,
38 user_revoked_tokens: Arc<RwLock<HashMap<String, Vec<String>>>>,
40}
41
42impl TokenRevocationStore {
43 pub fn new() -> Self {
45 Self {
46 revoked_tokens: Arc::new(RwLock::new(HashMap::new())),
47 user_revoked_tokens: Arc::new(RwLock::new(HashMap::new())),
48 }
49 }
50
51 pub async fn revoke_token(
53 &self,
54 token_id: String,
55 user_id: Option<String>,
56 reason: String,
57 expires_at: Option<i64>,
58 ) {
59 let revoked = RevokedToken {
60 token_id: token_id.clone(),
61 user_id: user_id.clone(),
62 revoked_at: Utc::now().timestamp(),
63 reason,
64 expires_at,
65 };
66
67 let mut tokens = self.revoked_tokens.write().await;
68 tokens.insert(token_id.clone(), revoked);
69
70 if let Some(uid) = user_id {
71 let mut user_tokens = self.user_revoked_tokens.write().await;
72 user_tokens.entry(uid).or_insert_with(Vec::new).push(token_id);
73 }
74 }
75
76 pub async fn revoke_user_tokens(&self, user_id: String, reason: String) {
78 let mut user_tokens = self.user_revoked_tokens.write().await;
79 if let Some(token_ids) = user_tokens.get(&user_id) {
80 let mut tokens = self.revoked_tokens.write().await;
81 for token_id in token_ids {
82 if let Some(revoked) = tokens.get_mut(token_id) {
83 revoked.revoked_at = Utc::now().timestamp();
84 revoked.reason = reason.clone();
85 }
86 }
87 }
88 }
89
90 pub async fn is_revoked(&self, token_id: &str) -> Option<RevokedToken> {
92 let tokens = self.revoked_tokens.read().await;
93 tokens.get(token_id).cloned()
94 }
95
96 pub async fn get_revocation_status(&self, token_id: &str) -> Option<RevokedToken> {
98 self.is_revoked(token_id).await
99 }
100
101 pub async fn cleanup_expired(&self) {
103 let now = Utc::now().timestamp();
104 let mut tokens = self.revoked_tokens.write().await;
105 tokens.retain(|_, revoked| {
106 revoked.expires_at.map_or(true, |exp| exp > now)
107 });
108 }
109}
110
111impl Default for TokenRevocationStore {
112 fn default() -> Self {
113 Self::new()
114 }
115}
116
117#[derive(Debug, Clone)]
119pub struct KeyRotationState {
120 active_keys: Arc<RwLock<HashMap<String, ActiveKey>>>,
122 grace_period_seconds: i64,
124}
125
126#[derive(Debug, Clone)]
128pub struct ActiveKey {
129 pub kid: String,
131 pub created_at: i64,
133 pub inactive_at: Option<i64>,
135 pub is_primary: bool,
137}
138
139impl KeyRotationState {
140 pub fn new(grace_period_seconds: i64) -> Self {
142 Self {
143 active_keys: Arc::new(RwLock::new(HashMap::new())),
144 grace_period_seconds,
145 }
146 }
147
148 pub async fn add_key(&self, kid: String, is_primary: bool) {
150 let mut keys = self.active_keys.write().await;
151 keys.insert(
152 kid.clone(),
153 ActiveKey {
154 kid,
155 created_at: Utc::now().timestamp(),
156 inactive_at: None,
157 is_primary,
158 },
159 );
160 }
161
162 pub async fn rotate_key(&self, new_kid: String) -> Result<(), Error> {
164 let mut keys = self.active_keys.write().await;
165
166 for key in keys.values_mut() {
168 key.is_primary = false;
169 key.inactive_at = Some(Utc::now().timestamp() + self.grace_period_seconds);
171 }
172
173 keys.insert(
175 new_kid.clone(),
176 ActiveKey {
177 kid: new_kid,
178 created_at: Utc::now().timestamp(),
179 inactive_at: None,
180 is_primary: true,
181 },
182 );
183
184 Ok(())
185 }
186
187 pub async fn get_active_keys(&self) -> Vec<ActiveKey> {
189 let now = Utc::now().timestamp();
190 let keys = self.active_keys.read().await;
191 keys.values()
192 .filter(|key| {
193 key.inactive_at.map_or(true, |inactive_at| inactive_at > now)
194 })
195 .cloned()
196 .collect()
197 }
198
199 pub async fn get_primary_key(&self) -> Option<ActiveKey> {
201 let keys = self.active_keys.read().await;
202 keys.values()
203 .find(|key| key.is_primary)
204 .cloned()
205 }
206
207 pub async fn cleanup_old_keys(&self) {
209 let now = Utc::now().timestamp();
210 let mut keys = self.active_keys.write().await;
211 keys.retain(|_, key| {
212 key.inactive_at.map_or(true, |inactive_at| inactive_at > now)
213 });
214 }
215}
216
217#[derive(Debug, Clone)]
219pub struct ClockSkewState {
220 skew_seconds: Arc<RwLock<i64>>,
222 apply_to_issuance: bool,
224 apply_to_validation: bool,
226}
227
228impl ClockSkewState {
229 pub fn new() -> Self {
231 Self {
232 skew_seconds: Arc::new(RwLock::new(0)),
233 apply_to_issuance: true,
234 apply_to_validation: true,
235 }
236 }
237
238 pub async fn set_skew(&self, skew_seconds: i64) {
240 let mut skew = self.skew_seconds.write().await;
241 *skew = skew_seconds;
242 }
243
244 pub async fn get_skew(&self) -> i64 {
246 let skew = self.skew_seconds.read().await;
247 *skew
248 }
249
250 pub async fn get_adjusted_time(&self) -> i64 {
252 let skew = self.skew_seconds.read().await;
253 Utc::now().timestamp() + *skew
254 }
255
256 pub async fn apply_issuance_skew(&self, timestamp: i64) -> i64 {
258 if self.apply_to_issuance {
259 let skew = self.skew_seconds.read().await;
260 timestamp + *skew
261 } else {
262 timestamp
263 }
264 }
265
266 pub async fn apply_validation_skew(&self, timestamp: i64) -> i64 {
268 if self.apply_to_validation {
269 let skew = self.skew_seconds.read().await;
270 timestamp - *skew
271 } else {
272 timestamp
273 }
274 }
275}
276
277impl Default for ClockSkewState {
278 fn default() -> Self {
279 Self::new()
280 }
281}
282
283#[derive(Debug, Clone)]
285pub struct TokenLifecycleManager {
286 pub revocation: TokenRevocationStore,
288 pub key_rotation: KeyRotationState,
290 pub clock_skew: ClockSkewState,
292}
293
294impl TokenLifecycleManager {
295 pub fn new(grace_period_seconds: i64) -> Self {
297 Self {
298 revocation: TokenRevocationStore::new(),
299 key_rotation: KeyRotationState::new(grace_period_seconds),
300 clock_skew: ClockSkewState::new(),
301 }
302 }
303}
304
305impl Default for TokenLifecycleManager {
306 fn default() -> Self {
307 Self::new(3600) }
309}
310
311pub fn extract_token_id(token: &str) -> String {
313 let mut hasher = Sha256::new();
316 hasher.update(token.as_bytes());
317 format!("{:x}", hasher.finalize())
318}
319