rskit_auth/password/
reset.rs1use std::time::Duration;
2
3use base64::Engine;
4use chrono::{DateTime, Utc};
5use rand::RngCore;
6
7#[derive(Debug, Clone)]
9pub struct ResetTokenGenerator {
10 pub ttl: Duration,
12}
13
14impl ResetTokenGenerator {
15 pub const fn new(ttl: Duration) -> Self {
17 Self { ttl }
18 }
19
20 pub fn generate(&self) -> (String, DateTime<Utc>) {
24 let mut bytes = [0u8; 32];
25 rand::rng().fill_bytes(&mut bytes);
26 let token = base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(bytes);
27 let expires_at = Utc::now() + chrono::Duration::from_std(self.ttl).unwrap_or_default();
28 (token, expires_at)
29 }
30}
31
32#[cfg(test)]
33mod tests {
34 use super::*;
35
36 #[test]
37 fn generates_unique_tokens() {
38 let generator = ResetTokenGenerator::new(Duration::from_mins(5));
39 let (t1, _) = generator.generate();
40 let (t2, _) = generator.generate();
41 assert_ne!(t1, t2);
42 }
43
44 #[test]
45 fn expiry_is_in_the_future() {
46 let generator = ResetTokenGenerator::new(Duration::from_mins(5));
47 let (_, exp) = generator.generate();
48 assert!(exp > Utc::now());
49 }
50}