r_token/memory.rs
1use crate::RTokenError;
2use crate::models::RTokenInfo;
3use chrono::Utc;
4use std::{
5 collections::HashMap,
6 sync::{
7 Arc, Mutex,
8 atomic::{AtomicU64, Ordering},
9 },
10};
11
12/// ## 日本語
13///
14/// 現在時刻の Unix epoch ミリ秒を返します。
15///
16/// ## English
17///
18/// Returns the current Unix epoch milliseconds.
19fn now_ms() -> u64 {
20 u64::try_from(Utc::now().timestamp_millis()).unwrap_or(0)
21}
22
23/// ## 日本語
24///
25/// `now_ms + ttl_seconds` をミリ秒で安全に加算します(飽和演算)。
26///
27/// ## English
28///
29/// Computes `now_ms + ttl_seconds` in milliseconds with saturation.
30fn add_ttl_ms(now_ms: u64, ttl_seconds: u64) -> u64 {
31 let ttl_ms = (ttl_seconds as u128).saturating_mul(1000);
32 (now_ms as u128)
33 .saturating_add(ttl_ms)
34 .min(u64::MAX as u128) as u64
35}
36
37/// ## 日本語
38///
39/// 自動掃除を実行する最小間隔(ミリ秒)。
40///
41/// ## English
42///
43/// Minimum interval for automatic pruning (milliseconds).
44const PRUNE_INTERVAL_MS: u64 = 60_000;
45/// ## 日本語
46///
47/// 自動掃除を試みる最小ストアサイズ。
48///
49/// ## English
50///
51/// Minimum store size that triggers auto-pruning.
52const PRUNE_MIN_SIZE: usize = 1024;
53
54/// ## 日本語
55///
56/// 認証 token の発行・保存・失効を行うマネージャです。
57///
58/// actix-web のアプリケーション state(例:`web::Data<RTokenManager>`)に保持する想定で、
59/// 内部では `Arc<Mutex<...>>` を使って状態を共有します。そのため `Clone` は同じストアへの
60/// ハンドルを増やすだけです。
61///
62/// token は UUID v4 文字列として生成され、次と紐づきます:
63/// - ユーザー ID(`String`)
64/// - 有効期限(Unix epoch ミリ秒)
65///
66/// ## English
67///
68/// Issues, stores, and revokes authentication tokens.
69///
70/// This type is designed to be stored in actix-web application state
71/// (e.g. `web::Data<RTokenManager>`). Internally it uses an `Arc<Mutex<...>>`,
72/// so `Clone` creates another handle to the same shared store.
73///
74/// Tokens are generated as UUID v4 strings. Each token is associated with:
75/// - a user id (`String`)
76/// - an expiration timestamp (Unix epoch milliseconds)
77#[derive(Clone)]
78pub struct RTokenManager {
79 /// ## 日本語
80 ///
81 /// インメモリの token ストア。
82 ///
83 /// ## English
84 ///
85 /// In-memory token store.
86 store: Arc<Mutex<HashMap<String, RTokenInfo>>>,
87 /// ## 日本語
88 ///
89 /// 最後に自動掃除を実行した時刻(ミリ秒)。
90 ///
91 /// ## English
92 ///
93 /// The last time auto-pruning ran (milliseconds).
94 last_prune_ms: Arc<AtomicU64>,
95}
96
97impl RTokenManager {
98 /// ## 日本語
99 ///
100 /// 空のマネージャを作成します。
101 ///
102 /// ## English
103 ///
104 /// Creates an empty manager.
105 pub fn new() -> Self {
106 Self {
107 store: Arc::new(Mutex::new(HashMap::new())),
108 last_prune_ms: Arc::new(AtomicU64::new(0)),
109 }
110 }
111
112 /// ## 日本語
113 ///
114 /// 指定ユーザー ID の新しい token を発行します。
115 ///
116 /// `expire_time` は TTL(秒)として扱います。保存された有効期限が現在時刻より過去であれば、
117 /// token は無効とみなされます。
118 ///
119 /// 内部 mutex が poisoned の場合は [`RTokenError::MutexPoisoned`] を返します。
120 ///
121 /// ## English
122 ///
123 /// Issues a new token for the given user id.
124 ///
125 /// `expire_time` is treated as TTL in seconds. The token will be considered invalid
126 /// once the stored expiration timestamp is earlier than the current time.
127 ///
128 /// Returns [`RTokenError::MutexPoisoned`] if the internal mutex is poisoned.
129 pub fn login(&self, id: &str, expire_time: u64) -> Result<String, RTokenError> {
130 // 日本語: token は UUID v4 文字列で生成する
131 // English: Tokens are generated as UUID v4 strings
132 let token = uuid::Uuid::new_v4().to_string();
133
134 // 日本語: expire_time は秒 TTL として扱い、現在時刻から期限 (ms) を計算する
135 // English: Treat expire_time as TTL seconds and compute deadline in milliseconds
136 let expire_time = add_ttl_ms(now_ms(), expire_time);
137
138 // 日本語: token と紐づく情報(user_id / expire_at / roles)を作る
139 // English: Build token info (user_id / expire_at / roles)
140 let info = RTokenInfo {
141 user_id: id.to_string(),
142 expire_at: expire_time,
143 roles: Vec::new(),
144 };
145
146 // 日本語: mutex をロックしてストアに保存する(poisoned はライブラリのエラーに変換)
147 // English: Lock the store mutex and insert (map poisoned to library error)
148 let now_ms = now_ms();
149 let mut store = self.store.lock().map_err(|_| RTokenError::MutexPoisoned)?;
150 store.insert(token.clone(), info);
151 self.maybe_prune(&mut store, now_ms);
152 Ok(token)
153 }
154
155 #[cfg(feature = "rbac")]
156 /// ## 日本語
157 ///
158 /// 指定ユーザー ID と役割(roles)を紐づけた新しい token を発行します(RBAC 有効時)。
159 ///
160 /// `expire_time` は TTL(秒)として扱います。
161 ///
162 /// ## English
163 ///
164 /// Issues a new token for the given user id and roles (RBAC enabled).
165 ///
166 /// `expire_time` is treated as TTL in seconds.
167 pub fn login_with_roles(
168 &self,
169 id: &str,
170 expire_time: u64,
171 role: impl Into<Vec<String>>,
172 ) -> Result<String, RTokenError> {
173 // 日本語: token は UUID v4 文字列で生成する
174 // English: Tokens are generated as UUID v4 strings
175 let token = uuid::Uuid::new_v4().to_string();
176
177 // 日本語: expire_time は秒 TTL として扱い、現在時刻から期限 (ms) を計算する
178 // English: Treat expire_time as TTL seconds and compute deadline in milliseconds
179 let expire_time = add_ttl_ms(now_ms(), expire_time);
180
181 // 日本語: roles を含む token 情報を作って保存する
182 // English: Build token info including roles and store it
183 let info = RTokenInfo {
184 user_id: id.to_string(),
185 expire_at: expire_time,
186 roles: role.into(),
187 };
188 let now_ms = now_ms();
189 let mut store = self.store.lock().map_err(|_| RTokenError::MutexPoisoned)?;
190 store.insert(token.clone(), info);
191 self.maybe_prune(&mut store, now_ms);
192 Ok(token)
193 }
194
195 // pub fn set_role(&self, token: &str, role: impl Into<Vec<String>>) -> Result<(), RTokenError> {
196 #[cfg(feature = "rbac")]
197 /// ## 日本語
198 ///
199 /// 既存 token の roles を更新します(RBAC 有効時)。
200 ///
201 /// token が存在しない場合でも成功として扱います(冪等)。
202 ///
203 /// ## English
204 ///
205 /// Updates roles for an existing token (RBAC enabled).
206 ///
207 /// This operation is idempotent: if the token does not exist, it is treated as success.
208 pub fn set_roles(&self, token: &str, roles: impl Into<Vec<String>>) -> Result<(), RTokenError> {
209 // 日本語: まずストアをロックする
210 // English: Lock the store first
211 let mut store = self.store.lock().map_err(|_| RTokenError::MutexPoisoned)?;
212 if let Some(info) = store.get_mut(token) {
213 // 日本語: token が存在する場合のみ roles を更新する
214 // English: Update roles only when the token exists
215 info.roles = roles.into();
216 }
217 Ok(())
218 }
219
220 #[cfg(feature = "rbac")]
221 /// ## 日本語
222 ///
223 /// token に紐づく roles を返します(RBAC 有効時)。
224 ///
225 /// token が存在しない場合は `Ok(None)` を返します。
226 ///
227 /// ## English
228 ///
229 /// Returns roles associated with a token (RBAC enabled).
230 ///
231 /// Returns `Ok(None)` if the token does not exist.
232 pub fn get_roles(&self, token: &str) -> Result<Option<Vec<String>>, RTokenError> {
233 // 日本語: 読み取りのためストアをロックする
234 // English: Lock the store for reading
235 let store = self.store.lock().map_err(|_| RTokenError::MutexPoisoned)?;
236 // 日本語: Vec を返すため clone する(ストア内部を露出しない)
237 // English: Clone the Vec to avoid exposing internal storage
238 Ok(store.get(token).map(|info| info.roles.clone()))
239 }
240
241 /// ## 日本語
242 ///
243 /// token をインメモリストアから削除して失効させます。
244 ///
245 /// この操作は冪等です。存在しない token を削除しても成功として扱います。
246 /// 内部 mutex が poisoned の場合は [`RTokenError::MutexPoisoned`] を返します。
247 ///
248 /// ## English
249 ///
250 /// Revokes a token by removing it from the in-memory store.
251 ///
252 /// This operation is idempotent: removing a non-existing token is treated as success.
253 /// Returns [`RTokenError::MutexPoisoned`] if the internal mutex is poisoned.
254 pub fn logout(&self, token: &str) -> Result<(), RTokenError> {
255 // 日本語: remove は「存在しない token」でも何もしないため冪等
256 // English: remove is idempotent (no-op for non-existing tokens)
257 self.store
258 .lock()
259 .map_err(|_| RTokenError::MutexPoisoned)?
260 .remove(token);
261 Ok(())
262 }
263
264 /// ## 日本語
265 ///
266 /// token に保存されている有効期限(Unix epoch ミリ秒)を返します。
267 ///
268 /// token が存在しない場合は `Ok(None)` を返します。本メソッドは token の期限切れ判定は
269 /// 行いません。
270 ///
271 /// ## English
272 ///
273 /// Returns the stored expiration timestamp for a token (milliseconds since Unix epoch).
274 ///
275 /// Returns `Ok(None)` if the token does not exist. This method does not validate
276 /// whether the token has already expired.
277 pub fn expires_at(&self, token: &str) -> Result<Option<u64>, RTokenError> {
278 // 日本語: token の存在確認のみ(期限切れ判定はしない)
279 // English: Only checks existence (does not validate expiration)
280 let store = self.store.lock().map_err(|_| RTokenError::MutexPoisoned)?;
281 Ok(store.get(token).map(|info| info.expire_at))
282 }
283
284 /// ## 日本語
285 ///
286 /// token の残り TTL(秒)を返します。
287 ///
288 /// 返り値:
289 /// - token が存在しない:`Ok(None)`
290 /// - token がすでに期限切れ:`Ok(Some(0))`(本メソッドでは削除しません)
291 ///
292 /// ## English
293 ///
294 /// Returns the remaining TTL in seconds for a token.
295 ///
296 /// Returns:
297 /// - `Ok(None)` when the token does not exist
298 /// - `Ok(Some(0))` when the token is already expired (it is not removed here)
299 pub fn ttl_seconds(&self, token: &str) -> Result<Option<i64>, RTokenError> {
300 // 日本語: 現在時刻 (ms) と保存された expire_at (ms) の差から残り秒数を計算する
301 // English: Compute remaining seconds from now_ms and stored expire_at (milliseconds)
302 let now_ms = now_ms();
303 let store = self.store.lock().map_err(|_| RTokenError::MutexPoisoned)?;
304 let Some(expire_at) = store.get(token).map(|info| info.expire_at) else {
305 return Ok(None);
306 };
307
308 if expire_at <= now_ms {
309 return Ok(Some(0));
310 }
311
312 let remaining_ms = expire_at - now_ms;
313 // 日本語: ms を秒に変換(端数は切り上げ)
314 // English: Convert ms to seconds (ceil)
315 let remaining_seconds = remaining_ms.div_ceil(1000) as i64;
316 Ok(Some(remaining_seconds))
317 }
318
319 /// ## 日本語
320 ///
321 /// token の有効期限を `now + ttl_seconds` に延長します。
322 ///
323 /// 返り値:
324 /// - token が存在し、期限切れでない:`Ok(true)`
325 /// - token が存在しない、または期限切れ:`Ok(false)`(期限切れの場合は削除します)
326 ///
327 /// ## English
328 ///
329 /// Extends a token's lifetime to `now + ttl_seconds`.
330 ///
331 /// Returns:
332 /// - `Ok(true)` if the token exists and is not expired
333 /// - `Ok(false)` if the token does not exist or is expired (expired tokens are removed)
334 pub fn renew(&self, token: &str, ttl_seconds: u64) -> Result<bool, RTokenError> {
335 // 日本語: now + ttl_seconds で新しい expire_at (ms) を計算する
336 // English: Compute new expire_at (ms) as now + ttl_seconds
337 let expire_at = add_ttl_ms(now_ms(), ttl_seconds);
338
339 // 日本語: 対象 token を更新するためストアをロックする
340 // English: Lock the store to update the token
341 let mut store = self.store.lock().map_err(|_| RTokenError::MutexPoisoned)?;
342 let Some(info) = store.get_mut(token) else {
343 return Ok(false);
344 };
345
346 // 日本語: 期限切れは renew 失敗として扱い、ストアから削除する
347 // English: Treat expired token as failure and remove it from store
348 if info.expire_at < now_ms() {
349 store.remove(token);
350 return Ok(false);
351 }
352
353 // 日本語: 有効な token の期限を更新する
354 // English: Update expiration for valid token
355 info.expire_at = expire_at;
356 Ok(true)
357 }
358
359 /// ## 日本語
360 ///
361 /// 同じユーザー(および roles)に対して新しい token を発行し、古い token を失効させます。
362 ///
363 /// 新しい token の TTL は「現在から `ttl_seconds`」になります。
364 ///
365 /// 古い token が存在しない、または期限切れの場合は `Ok(None)` を返します(期限切れの場合は
366 /// 削除します)。
367 ///
368 /// ## English
369 ///
370 /// Issues a new token for the same user (and roles) and revokes the old token.
371 ///
372 /// The new token will have a lifetime of `ttl_seconds` from now.
373 ///
374 /// Returns `Ok(None)` if the old token does not exist or is expired (expired tokens
375 /// are removed).
376 pub fn rotate(&self, token: &str, ttl_seconds: u64) -> Result<Option<String>, RTokenError> {
377 // 日本語: 新 token の期限を now + ttl_seconds で計算する
378 // English: Compute new token expiration as now + ttl_seconds
379 let expire_at = add_ttl_ms(now_ms(), ttl_seconds);
380
381 // 日本語: old token の情報を参照して新 token に引き継ぐため clone する
382 // English: Clone old info so we can reuse it for the new token
383 let mut store = self.store.lock().map_err(|_| RTokenError::MutexPoisoned)?;
384 let Some(info) = store.get(token).cloned() else {
385 return Ok(None);
386 };
387
388 // 日本語: old token が期限切れなら削除して None を返す
389 // English: If old token expired, remove it and return None
390 if info.expire_at < now_ms() {
391 store.remove(token);
392 return Ok(None);
393 }
394
395 // 日本語: 新 token を生成して情報を引き継ぐ(user_id / roles)
396 // English: Generate a new token and carry over user_id / roles
397 let new_token = uuid::Uuid::new_v4().to_string();
398 let new_info = RTokenInfo {
399 user_id: info.user_id,
400 expire_at,
401 roles: info.roles,
402 };
403
404 // 日本語: old token を削除し、新 token を追加する
405 // English: Remove old token and insert the new token
406 store.remove(token);
407 store.insert(new_token.clone(), new_info);
408 Ok(Some(new_token))
409 }
410
411 /// ## 日本語
412 ///
413 /// インメモリストアから期限切れの token を削除し、削除した件数を返します。
414 ///
415 /// ## English
416 ///
417 /// Removes expired tokens from the in-memory store and returns how many were removed.
418 pub fn prune_expired(&self) -> Result<usize, RTokenError> {
419 // 日本語: retain を使って期限切れのエントリを一括削除する
420 // English: Use retain to bulk-remove expired entries
421 let now = now_ms();
422 let mut store = self.store.lock().map_err(|_| RTokenError::MutexPoisoned)?;
423
424 let original_len = store.len();
425 store.retain(|_token, info| info.expire_at >= now);
426 Ok(original_len - store.len())
427 }
428
429 /// ## 日本語
430 ///
431 /// token を検証し、有効であれば紐づくユーザー ID を返します。
432 ///
433 /// 振る舞い:
434 /// - token が存在し、期限切れでない:`Ok(Some(user_id))`
435 /// - token が存在しない、または期限切れ:`Ok(None)`
436 /// - 期限切れ token は検証時にストアから削除されます
437 ///
438 /// ## English
439 ///
440 /// Validates a token and returns the associated user id if present.
441 ///
442 /// Behavior:
443 /// - Returns `Ok(Some(user_id))` when the token exists and is not expired.
444 /// - Returns `Ok(None)` when the token does not exist or is expired.
445 /// - Expired tokens are removed from the in-memory store during validation.
446 pub fn validate(&self, token: &str) -> Result<Option<String>, RTokenError> {
447 #[cfg(feature = "rbac")]
448 {
449 Ok(self
450 .validate_with_roles(token)?
451 .map(|(user_id, _roles)| user_id))
452 }
453
454 #[cfg(not(feature = "rbac"))]
455 {
456 // 日本語: 検証時は期限切れを掃除するため書き込みロックを取る
457 // English: Take a write lock so we can remove expired tokens during validation
458 let mut store = self.store.lock().map_err(|_| RTokenError::MutexPoisoned)?;
459 let Some(info) = store.get(token) else {
460 return Ok(None);
461 };
462
463 // 日本語: 期限切れなら削除して無効扱いにする
464 // English: If expired, remove and treat as invalid
465 if info.expire_at < now_ms() {
466 store.remove(token);
467 return Ok(None);
468 }
469
470 // 日本語: 有効 token の user_id を返す
471 // English: Return user_id for a valid token
472 Ok(Some(info.user_id.clone()))
473 }
474 }
475
476 #[cfg(feature = "rbac")]
477 /// ## 日本語
478 ///
479 /// token を検証し、ユーザー ID と roles を返します(RBAC 有効時)。
480 ///
481 /// 期限切れの扱いは [`RTokenManager::validate`] と同じです。
482 ///
483 /// ## English
484 ///
485 /// Validates a token and returns both user id and roles (RBAC enabled).
486 ///
487 /// This has the same expiration behavior as [`RTokenManager::validate`].
488 pub fn validate_with_roles(
489 &self,
490 token: &str,
491 ) -> Result<Option<(String, Vec<String>)>, RTokenError> {
492 // 日本語: 検証時に期限切れの削除があり得るため書き込みロックを取る
493 // English: Take a write lock because we may remove expired tokens
494 let mut store = self.store.lock().map_err(|_| RTokenError::MutexPoisoned)?;
495 let Some(info) = store.get(token) else {
496 return Ok(None);
497 };
498
499 // 日本語: 期限切れなら削除して無効扱いにする
500 // English: If expired, remove and treat as invalid
501 if info.expire_at < now_ms() {
502 store.remove(token);
503 return Ok(None);
504 }
505
506 // 日本語: user_id と roles を返す(clone して内部を露出しない)
507 // English: Return user_id and roles (clone to avoid exposing internals)
508 Ok(Some((info.user_id.clone(), info.roles.clone())))
509 }
510
511 /// ## 日本語
512 ///
513 /// 条件を満たす場合のみ期限切れ token を掃除します。
514 ///
515 /// ## English
516 ///
517 /// Prunes expired tokens only when the thresholds are met.
518 fn maybe_prune(&self, store: &mut HashMap<String, RTokenInfo>, now_ms: u64) {
519 if store.len() < PRUNE_MIN_SIZE {
520 return;
521 }
522
523 let last = self.last_prune_ms.load(Ordering::Relaxed);
524 if now_ms.saturating_sub(last) < PRUNE_INTERVAL_MS {
525 return;
526 }
527
528 self.last_prune_ms.store(now_ms, Ordering::Relaxed);
529 store.retain(|_token, info| info.expire_at >= now_ms);
530 }
531}
532
533impl Default for RTokenManager {
534 fn default() -> Self {
535 Self::new()
536 }
537}
538
539/// ## 日本語
540///
541/// actix-web / axum から抽出される認証済みユーザーコンテキストです。
542///
543/// 抽出が成功した場合:
544/// - `id` は [`RTokenManager::login`] に渡したユーザー ID
545/// - `token` はリクエストに含まれていた token の生文字列
546///
547/// token は `Authorization` header から読み取ります。次の形式に対応します:
548/// - `Authorization: <token>`
549/// - `Authorization: Bearer <token>`
550///
551/// ## English
552///
553/// An authenticated request context extracted from actix-web / axum.
554///
555/// If extraction succeeds, `id` is the user id previously passed to
556/// [`RTokenManager::login`], and `token` is the original token from the request.
557///
558/// The token is read from `Authorization` header. Both of the following formats
559/// are accepted:
560/// - `Authorization: <token>`
561/// - `Authorization: Bearer <token>`
562#[cfg(any(feature = "actix", feature = "axum"))]
563#[derive(Debug)]
564pub struct RUser {
565 /// ## 日本語
566 ///
567 /// token に紐づくユーザー ID。
568 ///
569 /// ## English
570 ///
571 /// The user id associated with the token.
572 pub id: String,
573
574 /// ## 日本語
575 ///
576 /// リクエストに含まれていた token の生文字列。
577 ///
578 /// ## English
579 ///
580 /// The raw token string from the request.
581 pub token: String,
582 #[cfg(feature = "rbac")]
583 /// ## 日本語
584 ///
585 /// token に紐づく roles(RBAC 有効時)。
586 ///
587 /// ## English
588 ///
589 /// Roles associated with the token (RBAC enabled).
590 pub roles: Vec<String>,
591}
592
593#[cfg(feature = "rbac")]
594impl RUser {
595 /// ## 日本語
596 ///
597 /// 指定した role を持つかどうかを返します。
598 ///
599 /// ## English
600 ///
601 /// Returns whether the user has the given role.
602 pub fn has_role(&self, role: &str) -> bool {
603 self.roles.iter().any(|r| r == role)
604 }
605}
606
607/// ## 日本語
608///
609/// actix-web のリクエストから [`RUser`] を抽出します。
610///
611/// 失敗時:
612/// - 500:`app_data` にマネージャが無い、または mutex が poisoned
613/// - 401:token が無い/無効/期限切れ
614///
615/// ## English
616///
617/// Extracts [`RUser`] from an actix-web request.
618///
619/// Failure modes:
620/// - 500: manager is missing from `app_data`, or mutex is poisoned
621/// - 401: token is missing, invalid, or expired
622#[cfg(feature = "actix")]
623impl actix_web::FromRequest for RUser {
624 type Error = actix_web::Error;
625 type Future = std::pin::Pin<Box<dyn std::future::Future<Output = Result<Self, Self::Error>>>>;
626
627 fn from_request(
628 req: &actix_web::HttpRequest,
629 _payload: &mut actix_web::dev::Payload,
630 ) -> Self::Future {
631 use actix_web::web;
632
633 let manager = match req.app_data::<web::Data<RTokenManager>>() {
634 Some(m) => m.clone(),
635 None => {
636 return Box::pin(async {
637 Err(actix_web::error::ErrorInternalServerError(
638 "Token manager not found",
639 ))
640 });
641 }
642 };
643 let token = match crate::extract_token_from_request(req) {
644 Some(token) => token,
645 None => {
646 return Box::pin(async {
647 Err(actix_web::error::ErrorUnauthorized("Unauthorized"))
648 });
649 }
650 };
651
652 Box::pin(async move {
653 #[cfg(feature = "rbac")]
654 {
655 let token_for_check = token.clone();
656 let manager = manager.clone();
657 let user_info = actix_web::rt::task::spawn_blocking(move || {
658 manager.validate_with_roles(&token_for_check)
659 })
660 .await
661 .map_err(|_| actix_web::error::ErrorInternalServerError("Mutex poisoned"))?
662 .map_err(|_| actix_web::error::ErrorInternalServerError("Mutex poisoned"))?;
663
664 if let Some((user_id, roles)) = user_info {
665 return Ok(RUser {
666 id: user_id,
667 token,
668 roles,
669 });
670 }
671
672 Err(actix_web::error::ErrorUnauthorized("Invalid token"))
673 }
674
675 #[cfg(not(feature = "rbac"))]
676 {
677 let token_for_check = token.clone();
678 let manager = manager.clone();
679 let user_id =
680 actix_web::rt::task::spawn_blocking(move || manager.validate(&token_for_check))
681 .await
682 .map_err(|_| actix_web::error::ErrorInternalServerError("Mutex poisoned"))?
683 .map_err(|_| {
684 actix_web::error::ErrorInternalServerError("Mutex poisoned")
685 })?;
686
687 if let Some(user_id) = user_id {
688 return Ok(RUser { id: user_id, token });
689 }
690
691 Err(actix_web::error::ErrorUnauthorized("Invalid token"))
692 }
693 })
694 }
695}