Skip to main content

shared/domain/refresh_token/
model.rs

1use chrono::{DateTime, Utc};
2use mongodb::bson::{self, Bson};
3use serde::{Deserialize, Serialize};
4use sqlx::prelude::FromRow;
5
6use super::super::serde::{
7    deserialize_datetime, deserialize_object_id, deserialize_object_id_as_string,
8    deserialize_option_datetime, serialize_object_id_as_string,
9};
10use super::ports::query::IntoBsonDocument;
11
12#[derive(Default, Clone, Debug, PartialEq, Eq, Deserialize, Serialize, FromRow)]
13pub struct RefreshToken {
14    #[serde(
15        rename = "_id",
16        default,
17        skip_serializing_if = "Option::is_none",
18        deserialize_with = "deserialize_object_id_as_string"
19    )]
20    pub id: Option<String>,
21
22    #[sqlx(rename = "userId")]
23    #[serde(
24        rename = "userId",
25        default,
26        serialize_with = "serialize_object_id_as_string",
27        deserialize_with = "deserialize_object_id"
28    )]
29    pub user_id: String,
30
31    #[sqlx(rename = "issuedAt")]
32    #[serde(rename = "issuedAt", deserialize_with = "deserialize_datetime")]
33    pub issued_at: DateTime<Utc>,
34    #[sqlx(rename = "expiresAt")]
35    #[serde(rename = "expiresAt", deserialize_with = "deserialize_datetime")]
36    pub expires_at: DateTime<Utc>,
37    #[sqlx(rename = "usedAt")]
38    #[serde(rename = "usedAt", deserialize_with = "deserialize_option_datetime")]
39    pub used_at: Option<DateTime<Utc>>,
40
41    pub jti: String,
42}
43
44impl RefreshToken {
45    pub fn new(jti: &str) -> Self {
46        RefreshToken {
47            jti: jti.to_string(),
48            issued_at: chrono::Utc::now(),
49            ..Default::default()
50        }
51    }
52}
53impl RefreshToken {
54    pub fn with_user_id(mut self, id: &str) -> Self {
55        self.user_id = id.into();
56        self
57    }
58    pub fn with_expire_at(mut self, expires_in: i64) -> Self {
59        self.expires_at = chrono::Utc::now() + chrono::Duration::seconds(expires_in);
60        self
61    }
62}
63
64impl IntoBsonDocument for RefreshToken {
65    fn into_bson_document(self) -> Result<bson::Document, bson::ser::Error> {
66        let mut doc = bson::to_document(&self)?;
67
68        for key in &["expiresAt", "issuedAt", "usedAt"] {
69            if let Some(Bson::String(s)) = doc.get(*key).cloned()
70                && let Ok(dt) = DateTime::parse_from_rfc3339(&s)
71            {
72                doc.insert(
73                    *key,
74                    Bson::DateTime(bson::DateTime::from_millis(dt.timestamp_millis())),
75                );
76            }
77        }
78
79        Ok(doc)
80    }
81}
82
83#[derive(Debug, Serialize, Deserialize, Default)]
84pub struct IssuedTokens {
85    #[serde(rename = "accessToken")]
86    pub access_token: String,
87
88    #[serde(rename = "refreshToken")]
89    pub refresh_token: String,
90
91    #[serde(rename = "refreshTokenJti")]
92    pub refresh_token_jti: String,
93}
94impl IssuedTokens {
95    pub fn with_access_token(mut self, access_token: &str) -> Self {
96        self.access_token = access_token.into();
97        self
98    }
99
100    pub fn with_refresh_token(mut self, refresh_token: &str) -> Self {
101        self.refresh_token = refresh_token.into();
102        self
103    }
104
105    pub fn with_jti(mut self, jti: uuid::Uuid) -> Self {
106        self.refresh_token_jti = jti.to_string();
107        self
108    }
109}