bitwarden_auth/login/models/
login_success_response.rs1use std::fmt::Debug;
2
3use bitwarden_core::{
4 MissingFieldError,
5 key_management::{
6 MasterPasswordError,
7 account_cryptographic_state::{
8 AccountKeysResponseParseError, WrappedAccountCryptographicState,
9 },
10 },
11 require,
12};
13use bitwarden_policies::MasterPasswordPolicyData;
14use thiserror::Error;
15
16use crate::login::{api::response::LoginSuccessApiResponse, models::UserDecryptionOptionsResponse};
17
18#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
21#[serde(rename_all = "camelCase")]
22#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
23#[bitwarden_ffi::wasm_record]
24pub struct LoginSuccessResponse {
25 pub access_token: String,
27
28 pub expires_in: u64,
30
31 pub expires_at: i64,
34
35 pub scope: String,
38
39 pub token_type: String,
43
44 pub refresh_token: Option<String>,
47
48 pub user_key_wrapped_user_private_key: Option<String>,
51
52 pub two_factor_token: Option<String>,
54
55 pub force_password_reset: Option<bool>,
58
59 pub api_use_key_connector: Option<bool>,
64
65 pub user_decryption_options: UserDecryptionOptionsResponse,
67
68 pub master_password_policy: Option<MasterPasswordPolicyData>,
71
72 pub wrapped_account_crypto_state: Option<WrappedAccountCryptographicState>,
74}
75
76impl TryFrom<LoginSuccessApiResponse> for LoginSuccessResponse {
77 type Error = LoginResponseError;
78 fn try_from(response: LoginSuccessApiResponse) -> Result<Self, Self::Error> {
79 let expires_at =
84 chrono::Utc::now().timestamp_millis() + (response.expires_in * 1000) as i64;
85
86 Ok(LoginSuccessResponse {
87 access_token: response.access_token,
88 expires_in: response.expires_in,
89 expires_at,
90 scope: response.scope,
91 token_type: response.token_type,
92 refresh_token: response.refresh_token,
93 user_key_wrapped_user_private_key: response.private_key,
94 two_factor_token: response.two_factor_token,
95 force_password_reset: response.force_password_reset,
96 api_use_key_connector: response.api_use_key_connector,
97 user_decryption_options: require!(response.user_decryption_options).try_into()?,
99 master_password_policy: response.master_password_policy.map(|policy| policy.into()),
100 wrapped_account_crypto_state: response
101 .account_keys
102 .as_ref()
103 .map(TryInto::try_into)
104 .transpose()?,
105 })
106 }
107}
108
109#[derive(Debug, Error)]
111pub enum LoginResponseError {
112 #[error(transparent)]
114 MasterPassword(#[from] MasterPasswordError),
115
116 #[error("Failed to parse account keys: {0}")]
118 AccountKeys(#[from] AccountKeysResponseParseError),
119}
120
121impl From<MissingFieldError> for LoginResponseError {
122 fn from(value: MissingFieldError) -> Self {
123 LoginResponseError::MasterPassword(value.into())
124 }
125}