1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3
4#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
7pub struct OAuth1Token {
8 pub oauth_token: String,
9 pub oauth_token_secret: String,
10 #[serde(skip_serializing_if = "Option::is_none")]
11 pub mfa_token: Option<String>,
12 #[serde(skip_serializing_if = "Option::is_none")]
13 pub mfa_expiration_timestamp: Option<DateTime<Utc>>,
14 #[serde(default = "default_domain")]
15 pub domain: String,
16}
17
18fn default_domain() -> String {
19 "garmin.com".to_string()
20}
21
22impl OAuth1Token {
23 pub fn new(oauth_token: String, oauth_token_secret: String) -> Self {
24 Self {
25 oauth_token,
26 oauth_token_secret,
27 mfa_token: None,
28 mfa_expiration_timestamp: None,
29 domain: default_domain(),
30 }
31 }
32
33 pub fn with_mfa(mut self, mfa_token: String, expiration: Option<DateTime<Utc>>) -> Self {
34 self.mfa_token = Some(mfa_token);
35 self.mfa_expiration_timestamp = expiration;
36 self
37 }
38}
39
40#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
43pub struct OAuth2Token {
44 pub scope: String,
45 pub jti: String,
46 pub token_type: String,
47 pub access_token: String,
48 pub refresh_token: String,
49 pub expires_in: i64,
50 #[serde(default)]
51 pub expires_at: i64,
52 pub refresh_token_expires_in: i64,
53 #[serde(default)]
54 pub refresh_token_expires_at: i64,
55}
56
57impl OAuth2Token {
58 pub fn is_expired(&self) -> bool {
60 let now = Utc::now().timestamp();
61 self.expires_at < now
62 }
63
64 pub fn is_refresh_expired(&self) -> bool {
66 let now = Utc::now().timestamp();
67 self.refresh_token_expires_at < now
68 }
69
70 pub fn authorization_header(&self) -> String {
72 format!("{} {}", self.token_type, self.access_token)
73 }
74}
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79
80 #[test]
81 fn test_oauth1_token_new() {
82 let token = OAuth1Token::new(
83 "test_oauth_token".to_string(),
84 "test_oauth_secret".to_string(),
85 );
86
87 assert_eq!(token.oauth_token, "test_oauth_token");
88 assert_eq!(token.oauth_token_secret, "test_oauth_secret");
89 assert_eq!(token.domain, "garmin.com");
90 assert!(token.mfa_token.is_none());
91 assert!(token.mfa_expiration_timestamp.is_none());
92 }
93
94 #[test]
95 fn test_oauth1_token_with_mfa() {
96 let expiration = Utc::now();
97 let token = OAuth1Token::new(
98 "test_oauth_token".to_string(),
99 "test_oauth_secret".to_string(),
100 )
101 .with_mfa("mfa_token_123".to_string(), Some(expiration));
102
103 assert_eq!(token.mfa_token, Some("mfa_token_123".to_string()));
104 assert_eq!(token.mfa_expiration_timestamp, Some(expiration));
105 }
106
107 #[test]
108 fn test_oauth1_token_serialization() {
109 let token = OAuth1Token::new(
110 "test_oauth_token".to_string(),
111 "test_oauth_secret".to_string(),
112 );
113
114 let json = serde_json::to_string(&token).unwrap();
115 let deserialized: OAuth1Token = serde_json::from_str(&json).unwrap();
116
117 assert_eq!(token, deserialized);
118 }
119
120 #[test]
121 fn test_oauth2_token_is_expired() {
122 let expired_token = OAuth2Token {
123 scope: "test".to_string(),
124 jti: "jti123".to_string(),
125 token_type: "Bearer".to_string(),
126 access_token: "access123".to_string(),
127 refresh_token: "refresh123".to_string(),
128 expires_in: 3600,
129 expires_at: 0, refresh_token_expires_in: 86400,
131 refresh_token_expires_at: Utc::now().timestamp() + 86400,
132 };
133
134 assert!(expired_token.is_expired());
135 }
136
137 #[test]
138 fn test_oauth2_token_not_expired() {
139 let valid_token = OAuth2Token {
140 scope: "test".to_string(),
141 jti: "jti123".to_string(),
142 token_type: "Bearer".to_string(),
143 access_token: "access123".to_string(),
144 refresh_token: "refresh123".to_string(),
145 expires_in: 3600,
146 expires_at: Utc::now().timestamp() + 3600, refresh_token_expires_in: 86400,
148 refresh_token_expires_at: Utc::now().timestamp() + 86400,
149 };
150
151 assert!(!valid_token.is_expired());
152 }
153
154 #[test]
155 fn test_oauth2_token_refresh_expired() {
156 let token = OAuth2Token {
157 scope: "test".to_string(),
158 jti: "jti123".to_string(),
159 token_type: "Bearer".to_string(),
160 access_token: "access123".to_string(),
161 refresh_token: "refresh123".to_string(),
162 expires_in: 3600,
163 expires_at: Utc::now().timestamp() + 3600,
164 refresh_token_expires_in: 86400,
165 refresh_token_expires_at: 0, };
167
168 assert!(token.is_refresh_expired());
169 }
170
171 #[test]
172 fn test_oauth2_token_authorization_header() {
173 let token = OAuth2Token {
174 scope: "test".to_string(),
175 jti: "jti123".to_string(),
176 token_type: "Bearer".to_string(),
177 access_token: "my_access_token".to_string(),
178 refresh_token: "refresh123".to_string(),
179 expires_in: 3600,
180 expires_at: Utc::now().timestamp() + 3600,
181 refresh_token_expires_in: 86400,
182 refresh_token_expires_at: Utc::now().timestamp() + 86400,
183 };
184
185 assert_eq!(token.authorization_header(), "Bearer my_access_token");
186 }
187
188 #[test]
189 fn test_oauth2_token_serialization() {
190 let token = OAuth2Token {
191 scope: "test".to_string(),
192 jti: "jti123".to_string(),
193 token_type: "Bearer".to_string(),
194 access_token: "access123".to_string(),
195 refresh_token: "refresh123".to_string(),
196 expires_in: 3600,
197 expires_at: 1700000000,
198 refresh_token_expires_in: 86400,
199 refresh_token_expires_at: 1700086400,
200 };
201
202 let json = serde_json::to_string(&token).unwrap();
203 let deserialized: OAuth2Token = serde_json::from_str(&json).unwrap();
204
205 assert_eq!(token, deserialized);
206 }
207}