Mirror of https://github.com/roostorg/osprey
github.com/roostorg/osprey
1use std::time::Instant;
2
3use serde::{self, Deserialize};
4
5/// Represents an auth token for oauth
6#[derive(Eq, PartialEq, Deserialize, Debug, Clone)]
7pub struct Token {
8 access_token: String,
9 /// How long (in seconds) this token is valid.
10 #[serde(rename = "expires_in")]
11 pub expires_in_secs: u64,
12 pub token_type: String,
13
14 /// Timestamp of when we created this instance, and when `expires_in` takes effect.
15 #[serde(skip, default = "Instant::now")]
16 pub created_at: Instant,
17}
18
19/// Manually implement this so we don't print the `access_token`
20impl std::fmt::Display for Token {
21 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
22 write!(
23 f,
24 "Token: (type: {}, expires_in: {}s, alive_for: {}s)",
25 self.token_type,
26 self.expires_in_secs,
27 self.created_at.elapsed().as_secs(),
28 )
29 }
30}
31
32impl Token {
33 pub fn new(
34 access_token: String,
35 expires_in_secs: u64,
36 token_type: String,
37 created_at: Instant,
38 ) -> Self {
39 Self {
40 access_token,
41 expires_in_secs,
42 token_type,
43 created_at,
44 }
45 }
46
47 pub fn from_goauth(token: goauth::auth::Token, created_at: Instant) -> Self {
48 Self {
49 access_token: token.access_token().to_string(),
50 expires_in_secs: token.expires_in() as u64,
51 token_type: token.token_type().to_string(),
52 created_at,
53 }
54 }
55
56 pub fn is_expired(&self) -> bool {
57 let secs_since_created = self.created_at.elapsed().as_secs();
58 secs_since_created > self.expires_in_secs
59 }
60
61 /// Returns an active duration of how much time is left before the token expires.
62 /// `0` is returned if expired.
63 pub fn ttl_secs(&self) -> u64 {
64 let secs_since_created = self.created_at.elapsed().as_secs();
65
66 self.expires_in_secs.saturating_sub(secs_since_created)
67 }
68
69 /// Returns a header value representation of the token
70 pub fn to_header_rep(&self) -> String {
71 format!("{} {}", self.token_type, self.access_token)
72 }
73}
74
75#[cfg(test)]
76mod tests {
77 use std::time::Duration;
78
79 use super::*;
80
81 #[test]
82 fn test_token_expired() {
83 let token = Token {
84 access_token: "test.Sample".to_string(),
85 expires_in_secs: 3,
86 token_type: "Bearer".to_string(),
87 created_at: Instant::now() - Duration::from_secs(4),
88 };
89
90 assert!(token.is_expired());
91 assert_eq!(token.ttl_secs(), 0, "time remaining should be 0");
92 }
93
94 #[test]
95 fn test_token_not_expired() {
96 let token = Token {
97 access_token: "test.Sample".to_string(),
98 expires_in_secs: 30,
99 token_type: "Bearer".to_string(),
100 created_at: Instant::now() - Duration::from_secs(1),
101 };
102
103 assert!(!token.is_expired());
104 let ttl = token.ttl_secs();
105 assert!(ttl >= 20 && ttl <= 29, "time remaining should be < expires");
106 }
107}