openid_client/
tokenset.rs

1use std::{cmp::max, collections::HashMap, num::Wrapping};
2
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5
6use crate::helpers::now;
7
8/// # TokenSetParams
9/// Argument to create new TokenSetParams
10#[derive(Debug, Default, Serialize, Deserialize)]
11pub struct TokenSetParams {
12    /// `access_token`
13    #[serde(skip_serializing_if = "Option::is_none")]
14    pub access_token: Option<String>,
15    /// `token_type`
16    #[serde(skip_serializing_if = "Option::is_none")]
17    pub token_type: Option<String>,
18    /// `id_token`
19    #[serde(skip_serializing_if = "Option::is_none")]
20    pub id_token: Option<String>,
21    /// `refresh_token`
22    #[serde(skip_serializing_if = "Option::is_none")]
23    pub refresh_token: Option<String>,
24    /// `expires_in` - Access token expiration in (seconds)
25    #[serde(skip_serializing_if = "Option::is_none")]
26    pub expires_in: Option<u64>,
27    /// `expires_at` - Access token expiration timestamp, represented as the number of seconds since the epoch
28    #[serde(skip_serializing_if = "Option::is_none")]
29    pub expires_at: Option<u64>,
30    /// `session_state`
31    #[serde(skip_serializing_if = "Option::is_none")]
32    pub session_state: Option<String>,
33    /// `scope`
34    #[serde(skip_serializing_if = "Option::is_none")]
35    pub scope: Option<String>,
36    /// `other`
37    #[serde(skip_serializing_if = "Option::is_none")]
38    pub other: Option<HashMap<String, Value>>,
39}
40
41/// # TokenSet
42/// Represents a set of tokens retrieved from either authorization callback or successful token endpoint grant call.
43/// - If there are other properties present, it will be stored in `other` field. Access it via [`TokenSet::get_other()`]
44#[derive(Debug, Serialize, Deserialize, Clone)]
45pub struct TokenSet {
46    #[serde(skip_serializing_if = "Option::is_none")]
47    access_token: Option<String>,
48    #[serde(skip_serializing_if = "Option::is_none")]
49    token_type: Option<String>,
50    #[serde(skip_serializing_if = "Option::is_none")]
51    id_token: Option<String>,
52    #[serde(skip_serializing_if = "Option::is_none")]
53    refresh_token: Option<String>,
54    #[serde(skip_serializing_if = "Option::is_none")]
55    expires_in: Option<u64>,
56    #[serde(skip_serializing_if = "Option::is_none")]
57    expires_at: Option<u64>,
58    #[serde(skip_serializing_if = "Option::is_none")]
59    session_state: Option<String>,
60    #[serde(skip_serializing_if = "Option::is_none")]
61    scope: Option<String>,
62    #[serde(skip_serializing_if = "Option::is_none", flatten)]
63    other: Option<HashMap<String, Value>>,
64    #[serde(skip_serializing, skip_deserializing, default = "default_now")]
65    pub(crate) now: fn() -> u64,
66}
67
68fn default_now() -> fn() -> u64 {
69    now
70}
71
72impl Default for TokenSet {
73    fn default() -> Self {
74        Self {
75            access_token: Default::default(),
76            token_type: Default::default(),
77            id_token: Default::default(),
78            refresh_token: Default::default(),
79            expires_in: Default::default(),
80            expires_at: Default::default(),
81            session_state: Default::default(),
82            scope: Default::default(),
83            other: Default::default(),
84            now,
85        }
86    }
87}
88
89impl TokenSet {
90    /// # Create a [TokenSet] instance
91    ///
92    /// - `expires_at` - Access token expiration timestamp, represented as the number of seconds since the epoch
93    pub fn new(params: TokenSetParams) -> Self {
94        let mut tokenset = Self {
95            access_token: params.access_token,
96            token_type: params.token_type,
97            id_token: params.id_token,
98            refresh_token: params.refresh_token,
99            expires_in: params.expires_in,
100            expires_at: params.expires_at,
101            session_state: params.session_state,
102            scope: params.scope,
103            other: params.other,
104            now,
105        };
106
107        if params.expires_at.is_none() && params.expires_in.is_some() {
108            if let Some(e) = params.expires_in {
109                tokenset.expires_at = Some((Wrapping((tokenset.now)()) + Wrapping(e)).0);
110            }
111        }
112
113        tokenset
114    }
115
116    /// Returns if the set is expired or not
117    pub fn expired(&self) -> bool {
118        let expires_in = self.get_expires_in_internal();
119
120        if let Some(e) = expires_in {
121            return e == 0;
122        }
123        false
124    }
125
126    /// Get claims from the id_token
127    /// - This method just decodes and returns the found claims. Does not validate
128    pub fn claims(&self) -> Option<HashMap<String, Value>> {
129        if let Some(id_token) = &self.id_token {
130            let id_token_components: Vec<&str> = id_token.split('.').collect();
131            let payload = id_token_components.get(1)?;
132
133            return match base64_url::decode(payload) {
134                Ok(decoded) => serde_json::from_slice::<HashMap<String, Value>>(&decoded).ok(),
135                Err(_) => None,
136            };
137        }
138        None
139    }
140
141    /// Gets the access token
142    pub fn get_access_token(&self) -> Option<String> {
143        self.access_token.clone()
144    }
145
146    /// Gets the access token type
147    pub fn get_token_type(&self) -> Option<String> {
148        self.token_type.clone()
149    }
150
151    /// Gets the raw id token
152    pub fn get_id_token(&self) -> Option<String> {
153        self.id_token.clone()
154    }
155
156    /// Gets the refresh token
157    pub fn get_refresh_token(&self) -> Option<String> {
158        self.refresh_token.clone()
159    }
160
161    /// Gets the expires in
162    pub fn get_expires_in(&self) -> Option<u64> {
163        self.expires_in
164    }
165
166    /// Gets the expires in (seconds)
167    pub fn get_expires_at(&self) -> Option<u64> {
168        self.expires_at
169    }
170
171    /// Gets the session state from OP
172    pub fn get_session_state(&self) -> Option<String> {
173        self.session_state.clone()
174    }
175
176    /// Gets the scope
177    pub fn get_scope(&self) -> Option<String> {
178        self.scope.clone()
179    }
180
181    /// Gets the other fields
182    pub fn get_other(&self) -> Option<HashMap<String, Value>> {
183        self.other.clone()
184    }
185
186    pub(self) fn get_expires_in_internal(&self) -> Option<u64> {
187        if let Some(e) = self.expires_at {
188            return Some(max((Wrapping(e) - Wrapping((self.now)())).0, 0));
189        }
190        None
191    }
192
193    /// Sets id_token
194    pub(crate) fn set_id_token(&mut self, token: Option<String>) {
195        self.id_token = token;
196    }
197
198    /// Sets session_state
199    pub(crate) fn set_session_state(&mut self, session_state: Option<String>) {
200        self.session_state = session_state;
201    }
202}
203
204#[cfg(test)]
205#[path = "./tests/tokenset_tests.rs"]
206mod tokenset_tests;