bitwarden_core/auth/
renew.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
use chrono::Utc;

#[cfg(feature = "secrets")]
use crate::{
    auth::api::request::AccessTokenRequest,
    client::ServiceAccountLoginMethod,
    secrets_manager::state::{self, ClientState},
};
use crate::{
    auth::api::{request::ApiTokenRequest, response::IdentityTokenResponse},
    client::{internal::InternalClient, LoginMethod, UserLoginMethod},
    error::{Error, Result},
};

pub(crate) async fn renew_token(client: &InternalClient) -> Result<()> {
    const TOKEN_RENEW_MARGIN_SECONDS: i64 = 5 * 60;

    let tokens = client
        .tokens
        .read()
        .expect("RwLock is not poisoned")
        .clone();
    let login_method = client
        .login_method
        .read()
        .expect("RwLock is not poisoned")
        .clone();

    if let (Some(expires), Some(login_method)) = (tokens.expires_on, login_method) {
        if Utc::now().timestamp() < expires - TOKEN_RENEW_MARGIN_SECONDS {
            return Ok(());
        }

        let config = client
            .__api_configurations
            .read()
            .expect("RwLock is not poisoned")
            .clone();

        let res = match login_method.as_ref() {
            LoginMethod::User(u) => match u {
                UserLoginMethod::Username { client_id, .. } => {
                    let refresh = tokens.refresh_token.ok_or(Error::NotAuthenticated)?;

                    crate::auth::api::request::RenewTokenRequest::new(refresh, client_id.to_owned())
                        .send(&config)
                        .await?
                }
                UserLoginMethod::ApiKey {
                    client_id,
                    client_secret,
                    ..
                } => {
                    ApiTokenRequest::new(client_id, client_secret)
                        .send(&config)
                        .await?
                }
            },
            #[cfg(feature = "secrets")]
            LoginMethod::ServiceAccount(s) => match s {
                ServiceAccountLoginMethod::AccessToken {
                    access_token,
                    state_file,
                    ..
                } => {
                    let result = AccessTokenRequest::new(
                        access_token.access_token_id,
                        &access_token.client_secret,
                    )
                    .send(&config)
                    .await?;

                    if let (IdentityTokenResponse::Payload(r), Some(state_file), Ok(enc_settings)) =
                        (&result, state_file, client.get_encryption_settings())
                    {
                        if let Ok(enc_key) = enc_settings.get_key(&None) {
                            let state =
                                ClientState::new(r.access_token.clone(), enc_key.to_base64());
                            _ = state::set(state_file, access_token, state);
                        }
                    }

                    result
                }
            },
        };

        match res {
            IdentityTokenResponse::Refreshed(r) => {
                client.set_tokens(r.access_token, r.refresh_token, r.expires_in);
                return Ok(());
            }
            IdentityTokenResponse::Authenticated(r) => {
                client.set_tokens(r.access_token, r.refresh_token, r.expires_in);
                return Ok(());
            }
            IdentityTokenResponse::Payload(r) => {
                client.set_tokens(r.access_token, r.refresh_token, r.expires_in);
                return Ok(());
            }
            _ => {
                // We should never get here
                return Err(Error::InvalidResponse);
            }
        }
    }

    Err(Error::NotAuthenticated)
}