bitwarden_core/auth/
renew.rs

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