bitwarden_auth/send_access/
access_token_request.rs1#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
5#[serde(rename_all = "camelCase", deny_unknown_fields)]
6#[bitwarden_ffi::wasm_record]
7pub struct SendPasswordCredentials {
8 pub password_hash_b64: String,
10}
11
12#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
15#[bitwarden_ffi::wasm_record]
16pub struct SendEmailCredentials {
17 pub email: String,
19}
20
21#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
23#[bitwarden_ffi::wasm_record]
24pub struct SendEmailOtpCredentials {
25 pub email: String,
27 pub otp: String,
29}
30
31#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
33#[bitwarden_ffi::wasm_record]
34#[serde(untagged)]
38pub enum SendAccessCredentials {
39 #[allow(missing_docs)]
40 Password(SendPasswordCredentials),
41 #[allow(missing_docs)]
48 EmailOtp(SendEmailOtpCredentials),
49 #[allow(missing_docs)]
50 Email(SendEmailCredentials),
51}
52
53#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
55#[serde(rename_all = "camelCase", deny_unknown_fields)]
56#[bitwarden_ffi::wasm_record]
57pub struct SendAccessTokenRequest {
58 pub send_id: String,
60
61 #[serde(default, skip_serializing_if = "Option::is_none")]
62 #[cfg_attr(feature = "wasm", tsify(optional))]
63 pub send_access_credentials: Option<SendAccessCredentials>,
65}
66
67#[cfg(test)]
68mod tests {
69 use super::*;
70
71 mod send_access_token_request_tests {
72 use serde_json::{from_str, to_string};
73
74 use super::*;
75
76 #[test]
77 fn deserialize_camelcase_request() {
78 let json = r#"
79 {
80 "sendId": "abc123",
81 "sendAccessCredentials": { "passwordHashB64": "ha$h" }
82 }"#;
83
84 let req: SendAccessTokenRequest = from_str(json).unwrap();
85 assert_eq!(req.send_id, "abc123");
86
87 let creds = req.send_access_credentials.expect("expected Some");
88 match creds {
89 SendAccessCredentials::Password(p) => assert_eq!(p.password_hash_b64, "ha$h"),
90 _ => panic!("expected Password variant"),
91 }
92 }
93
94 #[test]
95 fn serialize_camelcase_request_with_credentials() {
96 let req = SendAccessTokenRequest {
97 send_id: "abc123".into(),
98 send_access_credentials: Some(SendAccessCredentials::Password(
99 SendPasswordCredentials {
100 password_hash_b64: "ha$h".into(),
101 },
102 )),
103 };
104 let json = to_string(&req).unwrap();
105 assert_eq!(
106 json,
107 r#"{"sendId":"abc123","sendAccessCredentials":{"passwordHashB64":"ha$h"}}"#
108 );
109 }
110
111 #[test]
112 fn serialize_omits_optional_credentials_when_none() {
113 let req = SendAccessTokenRequest {
114 send_id: "abc123".into(),
115 send_access_credentials: None,
116 };
117 let json = to_string(&req).unwrap();
118 assert_eq!(json, r#"{"sendId":"abc123"}"#);
119 }
120
121 #[test]
122 fn roundtrip_camel_in_to_camel_out() {
123 let in_json = r#"
124 {
125 "sendId": "abc123",
126 "sendAccessCredentials": { "passwordHashB64": "ha$h" }
127 }"#;
128
129 let req: SendAccessTokenRequest = from_str(in_json).unwrap();
130 let out_json = to_string(&req).unwrap();
131 assert_eq!(
132 out_json,
133 r#"{"sendId":"abc123","sendAccessCredentials":{"passwordHashB64":"ha$h"}}"#
134 );
135 }
136
137 #[test]
138 fn snakecase_top_level_keys_are_rejected() {
139 let json = r#"
140 {
141 "send_id": "abc123",
142 "sendAccessCredentials": { "passwordHashB64": "ha$h" }
143 }"#;
144 let err = from_str::<SendAccessTokenRequest>(json).unwrap_err();
145 let msg = err.to_string();
146 assert!(
147 msg.contains("unknown field") && msg.contains("send_id"),
148 "unexpected: {msg}"
149 );
150 }
151
152 #[test]
153 fn extra_top_level_key_is_rejected() {
154 let json = r#"
155 {
156 "sendId": "abc123",
157 "sendAccessCredentials": { "passwordHashB64": "ha$h" },
158 "extra": "nope"
159 }"#;
160 let err = from_str::<SendAccessTokenRequest>(json).unwrap_err();
161 let msg = err.to_string();
162 assert!(
163 msg.contains("unknown field") && msg.contains("extra"),
164 "unexpected: {msg}"
165 );
166 }
167
168 #[test]
169 fn snakecase_nested_keys_are_rejected() {
170 let json = r#"
171 {
172 "sendId": "abc123",
173 "sendAccessCredentials": { "password_hash_b64": "ha$h" }
174 }"#;
175
176 let err = serde_json::from_str::<SendAccessTokenRequest>(json).unwrap_err();
177 let msg = err.to_string();
178 assert!(
179 msg.contains("did not match any variant"),
180 "unexpected: {msg}"
181 );
182 }
183
184 #[test]
185 fn extra_nested_key_is_rejected() {
186 let json = r#"
187 {
188 "sendId": "abc123",
189 "sendAccessCredentials": {
190 "passwordHashB64": "ha$h",
191 "extra": "nope"
192 }
193 }"#;
194 let err = from_str::<SendAccessTokenRequest>(json).unwrap_err();
195 let msg = err.to_string();
196 assert!(
197 msg.contains("did not match any variant"),
198 "unexpected: {msg}"
199 );
200 }
201 }
202
203 mod send_access_credentials_tests {
204 use super::*;
205
206 mod send_access_password_credentials_tests {
207 use serde_json::{from_str, to_string};
208
209 use super::*;
210
211 #[test]
212 fn deserialize_struct_camelcase_from_ts() {
213 let json = r#"{ "passwordHashB64": "ha$h" }"#;
214 let s: SendPasswordCredentials = from_str(json).unwrap();
215 assert_eq!(s.password_hash_b64, "ha$h");
216 }
217
218 #[test]
219 fn serialize_struct_camelcase_to_wire() {
220 let s = SendPasswordCredentials {
221 password_hash_b64: "ha$h".into(),
222 };
223 let json = to_string(&s).unwrap();
224 assert_eq!(json, r#"{"passwordHashB64":"ha$h"}"#);
225 }
226
227 #[test]
228 fn roundtrip_struct_camel_in_to_camel_out() {
229 let in_json = r#"{ "passwordHashB64": "ha$h" }"#;
230 let parsed: SendPasswordCredentials = from_str(in_json).unwrap();
231 let out_json = to_string(&parsed).unwrap();
232 assert_eq!(out_json, r#"{"passwordHashB64":"ha$h"}"#);
233 }
234
235 #[test]
236 fn deserialize_enum_variant_from_json() {
237 let json = r#"{"passwordHashB64":"ha$h"}"#;
238 let creds: SendAccessCredentials = from_str(json).unwrap();
239 match creds {
240 SendAccessCredentials::Password(password_creds) => {
241 assert_eq!(password_creds.password_hash_b64, "ha$h");
242 }
243 _ => panic!("Expected Password variant"),
244 }
245 }
246
247 #[test]
248 fn serialize_enum_variant_to_json() {
249 let creds = SendAccessCredentials::Password(SendPasswordCredentials {
250 password_hash_b64: "ha$h".into(),
251 });
252 let json = to_string(&creds).unwrap();
253 assert_eq!(json, r#"{"passwordHashB64":"ha$h"}"#);
254 }
255
256 #[test]
257 fn roundtrip_enum_variant_through_json() {
258 let in_json = r#"{"passwordHashB64":"ha$h"}"#;
259 let creds: SendAccessCredentials = from_str(in_json).unwrap();
260 let out_json = to_string(&creds).unwrap();
261 assert_eq!(out_json, r#"{"passwordHashB64":"ha$h"}"#);
262 }
263 }
264
265 mod send_access_email_credentials_tests {
266 use serde_json::{from_str, to_string};
267
268 use super::*;
269
270 #[test]
271 fn deserialize_struct_camelcase_from_ts() {
272 let json = r#"{ "email": "[email protected]" }"#;
273 let s: SendEmailCredentials = from_str(json).unwrap();
274 assert_eq!(s.email, "[email protected]");
275 }
276
277 #[test]
278 fn serialize_struct_camelcase_to_wire() {
279 let s = SendEmailCredentials {
280 email: "[email protected]".into(),
281 };
282 let json = to_string(&s).unwrap();
283 assert_eq!(json, r#"{"email":"[email protected]"}"#);
284 }
285
286 #[test]
287 fn roundtrip_struct_camel_in_to_camel_out() {
288 let in_json = r#"{ "email": "[email protected]" }"#;
289 let parsed: SendEmailCredentials = from_str(in_json).unwrap();
290 let out_json = to_string(&parsed).unwrap();
291 assert_eq!(out_json, r#"{"email":"[email protected]"}"#);
292 }
293
294 #[test]
295 fn deserialize_enum_variant_from_json() {
296 let json = r#"{"email":"[email protected]"}"#;
297 let creds: SendAccessCredentials = from_str(json).unwrap();
298 match creds {
299 SendAccessCredentials::Email(email_creds) => {
300 assert_eq!(email_creds.email, "[email protected]");
301 }
302 _ => panic!("Expected Email variant"),
303 }
304 }
305
306 #[test]
307 fn serialize_enum_variant_to_json() {
308 let creds = SendAccessCredentials::Email(SendEmailCredentials {
309 email: "[email protected]".into(),
310 });
311 let json = to_string(&creds).unwrap();
312 assert_eq!(json, r#"{"email":"[email protected]"}"#);
313 }
314
315 #[test]
316 fn roundtrip_enum_variant_through_json() {
317 let in_json = r#"{"email":"[email protected]"}"#;
318 let creds: SendAccessCredentials = from_str(in_json).unwrap();
319 let out_json = to_string(&creds).unwrap();
320 assert_eq!(out_json, r#"{"email":"[email protected]"}"#);
321 }
322 }
323
324 mod send_access_email_otp_credentials_tests {
325 use serde_json::{from_str, to_string};
326
327 use super::*;
328
329 #[test]
330 fn deserialize_struct_camelcase_from_ts() {
331 let json = r#"{ "email": "[email protected]", "otp": "123456" }"#;
332 let s: SendEmailOtpCredentials = from_str(json).unwrap();
333 assert_eq!(s.email, "[email protected]");
334 assert_eq!(s.otp, "123456");
335 }
336
337 #[test]
338 fn serialize_struct_camelcase_to_wire() {
339 let s = SendEmailOtpCredentials {
340 email: "[email protected]".into(),
341 otp: "123456".into(),
342 };
343 let json = to_string(&s).unwrap();
344 assert_eq!(json, r#"{"email":"[email protected]","otp":"123456"}"#);
345 }
346
347 #[test]
348 fn roundtrip_struct_camel_in_to_camel_out() {
349 let in_json = r#"{ "email": "[email protected]", "otp": "123456" }"#;
350 let parsed: SendEmailOtpCredentials = from_str(in_json).unwrap();
351 let out_json = to_string(&parsed).unwrap();
352 assert_eq!(out_json, r#"{"email":"[email protected]","otp":"123456"}"#);
353 }
354
355 #[test]
356 fn deserialize_enum_variant_from_json() {
357 let json = r#"{"email":"[email protected]","otp":"123456"}"#;
358 let creds: SendAccessCredentials = from_str(json).unwrap();
359 match creds {
360 SendAccessCredentials::EmailOtp(otp_creds) => {
361 assert_eq!(otp_creds.email, "[email protected]");
362 assert_eq!(otp_creds.otp, "123456");
363 }
364 _ => panic!("Expected EmailOtp variant"),
365 }
366 }
367
368 #[test]
369 fn serialize_enum_variant_to_json() {
370 let creds = SendAccessCredentials::EmailOtp(SendEmailOtpCredentials {
371 email: "[email protected]".into(),
372 otp: "123456".into(),
373 });
374 let json = to_string(&creds).unwrap();
375 assert_eq!(json, r#"{"email":"[email protected]","otp":"123456"}"#);
376 }
377
378 #[test]
379 fn roundtrip_enum_variant_through_json() {
380 let in_json = r#"{"email":"[email protected]","otp":"123456"}"#;
381 let creds: SendAccessCredentials = from_str(in_json).unwrap();
382 let out_json = to_string(&creds).unwrap();
383 assert_eq!(out_json, r#"{"email":"[email protected]","otp":"123456"}"#);
384 }
385 }
386 }
387}