bitwarden_core/auth/
renew.rs

1use chrono::Utc;
2
3use super::login::LoginError;
4#[cfg(feature = "secrets")]
5use crate::{
6    auth::api::request::AccessTokenRequest,
7    client::ServiceAccountLoginMethod,
8    key_management::SymmetricKeyId,
9    secrets_manager::state::{self, ClientState},
10};
11use crate::{
12    auth::api::{request::ApiTokenRequest, response::IdentityTokenResponse},
13    client::{internal::InternalClient, LoginMethod, UserLoginMethod},
14    NotAuthenticatedError,
15};
16
17pub(crate) async fn renew_token(client: &InternalClient) -> Result<(), LoginError> {
18    const TOKEN_RENEW_MARGIN_SECONDS: i64 = 5 * 60;
19
20    let tokens = client
21        .tokens
22        .read()
23        .expect("RwLock is not poisoned")
24        .clone();
25    let login_method = client
26        .login_method
27        .read()
28        .expect("RwLock is not poisoned")
29        .clone();
30
31    if let (Some(expires), Some(login_method)) = (tokens.expires_on, login_method) {
32        if Utc::now().timestamp() < expires - TOKEN_RENEW_MARGIN_SECONDS {
33            return Ok(());
34        }
35
36        let config = client
37            .__api_configurations
38            .read()
39            .expect("RwLock is not poisoned")
40            .clone();
41
42        let res = match login_method.as_ref() {
43            LoginMethod::User(u) => match u {
44                UserLoginMethod::Username { client_id, .. } => {
45                    let refresh = tokens.refresh_token.ok_or(NotAuthenticatedError)?;
46
47                    crate::auth::api::request::RenewTokenRequest::new(refresh, client_id.to_owned())
48                        .send(&config)
49                        .await?
50                }
51                UserLoginMethod::ApiKey {
52                    client_id,
53                    client_secret,
54                    ..
55                } => {
56                    ApiTokenRequest::new(client_id, client_secret)
57                        .send(&config)
58                        .await?
59                }
60            },
61            #[cfg(feature = "secrets")]
62            LoginMethod::ServiceAccount(s) => match s {
63                ServiceAccountLoginMethod::AccessToken {
64                    access_token,
65                    state_file,
66                    ..
67                } => {
68                    let result = AccessTokenRequest::new(
69                        access_token.access_token_id,
70                        &access_token.client_secret,
71                    )
72                    .send(&config)
73                    .await?;
74
75                    if let (IdentityTokenResponse::Payload(r), Some(state_file)) =
76                        (&result, state_file)
77                    {
78                        let key_store = client.get_key_store();
79                        let ctx = key_store.context();
80                        #[allow(deprecated)]
81                        if let Ok(enc_key) = ctx.dangerous_get_symmetric_key(SymmetricKeyId::User) {
82                            let state =
83                                ClientState::new(r.access_token.clone(), enc_key.to_base64());
84                            _ = state::set(state_file, access_token, state);
85                        }
86                    }
87
88                    result
89                }
90            },
91        };
92
93        match res {
94            IdentityTokenResponse::Refreshed(r) => {
95                client.set_tokens(r.access_token, r.refresh_token, r.expires_in);
96                return Ok(());
97            }
98            IdentityTokenResponse::Authenticated(r) => {
99                client.set_tokens(r.access_token, r.refresh_token, r.expires_in);
100                return Ok(());
101            }
102            IdentityTokenResponse::Payload(r) => {
103                client.set_tokens(r.access_token, r.refresh_token, r.expires_in);
104                return Ok(());
105            }
106            _ => {
107                // We should never get here
108                return Err(LoginError::InvalidResponse);
109            }
110        }
111    }
112
113    Err(NotAuthenticatedError)?
114}