bitwarden_auth/login/models/login_success_response.rs
1use std::fmt::Debug;
2
3use bitwarden_core::{key_management::MasterPasswordError, require};
4use bitwarden_policies::MasterPasswordPolicyResponse;
5
6use crate::login::{api::response::LoginSuccessApiResponse, models::UserDecryptionOptionsResponse};
7
8/// SDK response model for a successful login.
9/// This is the model that will be exposed to consuming applications.
10#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
11#[serde(rename_all = "camelCase")]
12#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
13#[cfg_attr(
14 feature = "wasm",
15 derive(tsify::Tsify),
16 tsify(into_wasm_abi, from_wasm_abi)
17)]
18pub struct LoginSuccessResponse {
19 /// The access token string.
20 pub access_token: String,
21
22 /// The duration in seconds until the token expires.
23 pub expires_in: u64,
24
25 /// The timestamp in milliseconds when the token expires.
26 /// We calculate this for more convenient token expiration handling.
27 pub expires_at: i64,
28
29 /// The scope of the access token.
30 /// OAuth 2.0 RFC reference: <https://datatracker.ietf.org/doc/html/rfc6749#section-3.3>
31 pub scope: String,
32
33 /// The type of the token.
34 /// This will be "Bearer" for send access tokens.
35 /// OAuth 2.0 RFC reference: <https://datatracker.ietf.org/doc/html/rfc6749#section-7.1>
36 pub token_type: String,
37
38 /// The optional refresh token string.
39 /// This token can be used to obtain new access tokens when the current one expires.
40 pub refresh_token: Option<String>,
41
42 /// The user key wrapped user private key.
43 /// Note: previously known as "private_key".
44 pub user_key_wrapped_user_private_key: Option<String>,
45
46 /// Two-factor authentication token for future requests.
47 pub two_factor_token: Option<String>,
48
49 /// Indicates whether an admin has reset the user's master password,
50 /// requiring them to set a new password upon next login.
51 pub force_password_reset: Option<bool>,
52
53 /// Indicates whether the user uses Key Connector and if the client should have a locally
54 /// configured Key Connector URL in their environment.
55 /// Note: This is currently only applicable for client_credential grant type logins and
56 /// is only expected to be relevant for the CLI
57 pub api_use_key_connector: Option<bool>,
58
59 /// The user's decryption options for unlocking their vault.
60 pub user_decryption_options: UserDecryptionOptionsResponse,
61
62 /// If the user is subject to an organization master password policy,
63 /// this field contains the requirements of that policy.
64 pub master_password_policy: Option<MasterPasswordPolicyResponse>,
65 // TODO: PM-30222 we can expose this once we have a trait to convert PrivateKeysResponseModel
66 // to WrappedAccountCryptographicState
67 // The user's account cryptographic keys (wrapped with the user key).
68 // pub wrapped_account_crypto_state: Option<WrappedAccountCryptographicState>,
69}
70
71impl TryFrom<LoginSuccessApiResponse> for LoginSuccessResponse {
72 type Error = MasterPasswordError;
73 fn try_from(response: LoginSuccessApiResponse) -> Result<Self, Self::Error> {
74 // We want to convert the expires_in from seconds to a millisecond timestamp to have a
75 // concrete time the token will expire. This makes it easier to build logic around a
76 // concrete time rather than a duration. We keep expires_in as well for backward
77 // compatibility and convenience.
78 let expires_at =
79 chrono::Utc::now().timestamp_millis() + (response.expires_in * 1000) as i64;
80
81 Ok(LoginSuccessResponse {
82 access_token: response.access_token,
83 expires_in: response.expires_in,
84 expires_at,
85 scope: response.scope,
86 token_type: response.token_type,
87 refresh_token: response.refresh_token,
88 user_key_wrapped_user_private_key: response.private_key,
89 two_factor_token: response.two_factor_token,
90 force_password_reset: response.force_password_reset,
91 api_use_key_connector: response.api_use_key_connector,
92 // User decryption options are required on successful login responses
93 user_decryption_options: require!(response.user_decryption_options).try_into()?,
94 master_password_policy: response.master_password_policy.map(|policy| policy.into()),
95 // TODO: PM-30222 - we can expose this once we have a trait to convert
96 // PrivateKeysResponseModel to WrappedAccountCryptographicState
97 // wrapped_account_crypto_state: response.account_keys.map(|keys| keys.into()),
98 })
99 }
100}