bitwarden_core/auth/
renew.rs1use 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 return Err(LoginError::InvalidResponse);
137 }
138 }
139 }
140
141 Err(NotAuthenticatedError)?
142}