1use chrono::{DateTime, Utc};
8use serde::{Deserialize, Serialize};
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
26pub struct RefreshTokenData {
27 pub user_data: serde_json::Value,
29 pub expiry: DateTime<Utc>,
31 pub created: DateTime<Utc>,
33}
34
35impl RefreshTokenData {
36 pub fn is_expired(&self) -> bool {
38 Utc::now() > self.expiry
39 }
40}
41
42#[derive(Debug, Clone, Serialize, Deserialize)]
66pub struct Token {
67 pub access_token: String,
69 pub token_type: String,
71 #[serde(skip_serializing_if = "Option::is_none")]
73 pub refresh_token: Option<String>,
74 pub expires_at: i64,
76 pub created_at: i64,
78}
79
80impl Token {
81 pub fn expires_in(&self) -> i64 {
85 self.expires_at - Utc::now().timestamp()
86 }
87}
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92 use chrono::Duration;
93
94 #[test]
95 fn test_token_fields() {
96 let now = Utc::now();
97 let expires_at = (now + Duration::hours(1)).timestamp();
98 let created_at = now.timestamp();
99
100 let token = Token {
101 access_token: "test.access.token".to_string(),
102 token_type: "Bearer".to_string(),
103 refresh_token: Some("test-refresh-token".to_string()),
104 expires_at,
105 created_at,
106 };
107
108 assert_eq!(token.access_token, "test.access.token");
109 assert_eq!(token.token_type, "Bearer");
110 assert_eq!(token.refresh_token, Some("test-refresh-token".to_string()));
111 assert_eq!(token.expires_at, expires_at);
112 assert_eq!(token.created_at, created_at);
113 }
114
115 #[test]
116 fn test_token_expires_in() {
117 let future_token = Token {
119 access_token: String::new(),
120 token_type: String::new(),
121 refresh_token: None,
122 expires_at: (Utc::now() + Duration::minutes(30)).timestamp(),
123 created_at: Utc::now().timestamp(),
124 };
125 let expires_in = future_token.expires_in();
126 assert!(
128 (expires_in - 1800).abs() <= 5,
129 "Future expiry: expires_in={}, expected ~1800",
130 expires_in
131 );
132
133 let past_token = Token {
135 access_token: String::new(),
136 token_type: String::new(),
137 refresh_token: None,
138 expires_at: (Utc::now() - Duration::minutes(30)).timestamp(),
139 created_at: Utc::now().timestamp(),
140 };
141 let expires_in = past_token.expires_in();
142 assert!(
143 (expires_in + 1800).abs() <= 5,
144 "Past expiry: expires_in={}, expected ~-1800",
145 expires_in
146 );
147 }
148
149 #[test]
150 fn test_refresh_token_data_is_expired() {
151 let valid = RefreshTokenData {
153 user_data: serde_json::json!({"user_id": "abc"}),
154 expiry: Utc::now() + Duration::hours(1),
155 created: Utc::now(),
156 };
157 assert!(
158 !valid.is_expired(),
159 "Token with future expiry should not be expired"
160 );
161
162 let expired = RefreshTokenData {
164 user_data: serde_json::json!({"user_id": "abc"}),
165 expiry: Utc::now() - Duration::hours(1),
166 created: Utc::now() - Duration::hours(2),
167 };
168 assert!(
169 expired.is_expired(),
170 "Token with past expiry should be expired"
171 );
172 }
173}