bitwarden_auth/registration/
post_keys_for_key_connector_registration.rs1use bitwarden_api_api::models::SetKeyConnectorKeyRequestModel;
4use bitwarden_core::key_management::account_cryptographic_state::WrappedAccountCryptographicState;
5use bitwarden_crypto::EncString;
6use bitwarden_encoding::B64;
7use tracing::{error, info};
8#[cfg(feature = "wasm")]
9use wasm_bindgen::prelude::*;
10
11use crate::registration::{RegistrationClient, RegistrationError};
12
13#[cfg_attr(
15 feature = "wasm",
16 derive(tsify::Tsify),
17 tsify(into_wasm_abi, from_wasm_abi)
18)]
19#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
20#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
21pub struct KeyConnectorRegistrationResult {
22 pub account_cryptographic_state: WrappedAccountCryptographicState,
24 pub key_connector_key: B64,
26 pub key_connector_key_wrapped_user_key: EncString,
28 pub user_key: B64,
30}
31
32#[cfg_attr(feature = "wasm", wasm_bindgen)]
33impl RegistrationClient {
34 pub async fn post_keys_for_key_connector_registration(
37 &self,
38 key_connector_url: String,
39 sso_org_identifier: String,
40 ) -> Result<KeyConnectorRegistrationResult, RegistrationError> {
41 let client = &self.client.internal;
42 let configuration = &client.get_api_configurations();
43 let key_connector_client = client.get_key_connector_client(key_connector_url);
44
45 internal_post_keys_for_key_connector_registration(
46 self,
47 &configuration.api_client,
48 &key_connector_client,
49 sso_org_identifier,
50 )
51 .await
52 }
53}
54
55async fn internal_post_keys_for_key_connector_registration(
56 registration_client: &RegistrationClient,
57 api_client: &bitwarden_api_api::apis::ApiClient,
58 key_connector_api_client: &bitwarden_api_key_connector::apis::ApiClient,
59 sso_org_identifier: String,
60) -> Result<KeyConnectorRegistrationResult, RegistrationError> {
61 info!("Initializing account cryptography");
63 let registration_crypto_result = registration_client
64 .client
65 .crypto()
66 .make_user_key_connector_registration()
67 .map_err(|_| RegistrationError::Crypto)?;
68
69 info!("Posting key connector key to key connector server");
70 let key_connector_key: B64 = registration_crypto_result.key_connector_key.into();
71 post_key_to_key_connector(key_connector_api_client, &key_connector_key).await?;
72
73 info!("Posting user account cryptographic state to server");
74 let request = SetKeyConnectorKeyRequestModel {
75 key_connector_key_wrapped_user_key: Some(
76 registration_crypto_result
77 .key_connector_key_wrapped_user_key
78 .to_string(),
79 ),
80 account_keys: Some(Box::new(registration_crypto_result.account_keys_request)),
81 ..SetKeyConnectorKeyRequestModel::new(sso_org_identifier.to_string())
82 };
83 api_client
84 .accounts_key_management_api()
85 .post_set_key_connector_key(Some(request))
86 .await
87 .map_err(|e| {
88 error!("Failed to post account cryptographic state to server: {e:?}");
89 RegistrationError::Api
90 })?;
91
92 info!("User initialized!");
93 Ok(KeyConnectorRegistrationResult {
96 account_cryptographic_state: registration_crypto_result.account_cryptographic_state,
97 key_connector_key,
98 key_connector_key_wrapped_user_key: registration_crypto_result
99 .key_connector_key_wrapped_user_key,
100 user_key: registration_crypto_result.user_key.to_encoded().into(),
101 })
102}
103
104async fn post_key_to_key_connector(
105 key_connector_api_client: &bitwarden_api_key_connector::apis::ApiClient,
106 key_connector_key: &B64,
107) -> Result<(), RegistrationError> {
108 let request =
109 bitwarden_api_key_connector::models::user_key_request_model::UserKeyKeyRequestModel {
110 key: key_connector_key.to_string(),
111 };
112
113 let result = if key_connector_api_client
114 .user_keys_api()
115 .get_user_key()
116 .await
117 .is_ok()
118 {
119 info!("User's key connector key exists, updating");
120 key_connector_api_client
121 .user_keys_api()
122 .put_user_key(request)
123 .await
124 } else {
125 info!("User's key connector key does not exist, creating");
126 key_connector_api_client
127 .user_keys_api()
128 .post_user_key(request)
129 .await
130 };
131
132 result.map_err(|e| {
133 error!("Failed to post key connector key to key connector server: {e:?}");
134 RegistrationError::KeyConnectorApi
135 })
136}
137
138#[cfg(test)]
139mod tests {
140 use bitwarden_api_api::apis::ApiClient;
141 use bitwarden_core::Client;
142
143 use super::*;
144
145 const TEST_SSO_ORG_IDENTIFIER: &str = "test-org";
146
147 #[tokio::test]
148 async fn test_post_keys_for_key_connector_registration_success() {
149 let client = Client::new(None);
150 let registration_client = RegistrationClient::new(client);
151
152 let api_client = ApiClient::new_mocked(|mock| {
153 mock.accounts_key_management_api
154 .expect_post_set_key_connector_key()
155 .once()
156 .returning(move |_body| Ok(()));
157 });
158
159 let key_connector_api_client =
160 bitwarden_api_key_connector::apis::ApiClient::new_mocked(|mock| {
161 mock.user_keys_api
162 .expect_get_user_key()
163 .once()
164 .returning(move || {
165 Err(bitwarden_api_key_connector::apis::Error::ResponseError(
166 bitwarden_api_key_connector::apis::ResponseContent {
167 status: reqwest::StatusCode::NOT_FOUND,
168 content: "Not Found".to_string(),
169 },
170 ))
171 });
172 mock.user_keys_api
173 .expect_post_user_key()
174 .once()
175 .returning(move |_body| Ok(()));
176 });
177
178 let result = internal_post_keys_for_key_connector_registration(
179 ®istration_client,
180 &api_client,
181 &key_connector_api_client,
182 TEST_SSO_ORG_IDENTIFIER.to_string(),
183 )
184 .await;
185 assert!(result.is_ok());
186
187 if let ApiClient::Mock(mut mock) = api_client {
189 mock.accounts_key_management_api.checkpoint();
190 }
191 if let bitwarden_api_key_connector::apis::ApiClient::Mock(mut mock) =
192 key_connector_api_client
193 {
194 mock.user_keys_api.checkpoint();
195 }
196 }
197
198 #[tokio::test]
199 async fn test_post_keys_for_key_connector_registration_key_connector_api_failure() {
200 let client = Client::new(None);
201 let registration_client = RegistrationClient::new(client);
202
203 let api_client = ApiClient::new_mocked(|mock| {
204 mock.accounts_key_management_api
206 .expect_post_set_key_connector_key()
207 .never();
208 });
209
210 let key_connector_api_client =
211 bitwarden_api_key_connector::apis::ApiClient::new_mocked(|mock| {
212 mock.user_keys_api
213 .expect_get_user_key()
214 .once()
215 .returning(move || {
216 Err(bitwarden_api_key_connector::apis::Error::ResponseError(
217 bitwarden_api_key_connector::apis::ResponseContent {
218 status: reqwest::StatusCode::NOT_FOUND,
219 content: "Not Found".to_string(),
220 },
221 ))
222 });
223 mock.user_keys_api
224 .expect_post_user_key()
225 .once()
226 .returning(move |_body| {
227 Err(bitwarden_api_key_connector::apis::Error::Serde(
228 serde_json::Error::io(std::io::Error::other("API error")),
229 ))
230 });
231 });
232
233 let result = internal_post_keys_for_key_connector_registration(
234 ®istration_client,
235 &api_client,
236 &key_connector_api_client,
237 TEST_SSO_ORG_IDENTIFIER.to_string(),
238 )
239 .await;
240
241 assert!(result.is_err());
242 assert!(matches!(
243 result.unwrap_err(),
244 RegistrationError::KeyConnectorApi
245 ));
246
247 if let ApiClient::Mock(mut mock) = api_client {
249 mock.accounts_key_management_api.checkpoint();
250 }
251 if let bitwarden_api_key_connector::apis::ApiClient::Mock(mut mock) =
252 key_connector_api_client
253 {
254 mock.user_keys_api.checkpoint();
255 }
256 }
257
258 #[tokio::test]
259 async fn test_post_keys_for_key_connector_registration_api_failure() {
260 let client = Client::new(None);
261 let registration_client = RegistrationClient::new(client);
262
263 let api_client = ApiClient::new_mocked(|mock| {
264 mock.accounts_key_management_api
265 .expect_post_set_key_connector_key()
266 .once()
267 .returning(move |_body| {
268 Err(serde_json::Error::io(std::io::Error::other("API error")).into())
269 });
270 });
271
272 let key_connector_api_client =
273 bitwarden_api_key_connector::apis::ApiClient::new_mocked(|mock| {
274 mock.user_keys_api
275 .expect_get_user_key()
276 .once()
277 .returning(move || {
278 Err(bitwarden_api_key_connector::apis::Error::ResponseError(
279 bitwarden_api_key_connector::apis::ResponseContent {
280 status: reqwest::StatusCode::NOT_FOUND,
281 content: "Not Found".to_string(),
282 },
283 ))
284 });
285 mock.user_keys_api
286 .expect_post_user_key()
287 .once()
288 .returning(move |_body| Ok(()));
289 });
290
291 let result = internal_post_keys_for_key_connector_registration(
292 ®istration_client,
293 &api_client,
294 &key_connector_api_client,
295 TEST_SSO_ORG_IDENTIFIER.to_string(),
296 )
297 .await;
298
299 assert!(result.is_err());
300 assert!(matches!(result.unwrap_err(), RegistrationError::Api));
301
302 if let ApiClient::Mock(mut mock) = api_client {
304 mock.accounts_key_management_api.checkpoint();
305 }
306 if let bitwarden_api_key_connector::apis::ApiClient::Mock(mut mock) =
307 key_connector_api_client
308 {
309 mock.user_keys_api.checkpoint();
310 }
311 }
312}