bitwarden_auth/send_access/
access_token_request.rs1#[cfg(feature = "wasm")]
2use tsify::Tsify;
3
4#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
8#[serde(rename_all = "camelCase", deny_unknown_fields)]
9#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
10pub struct SendPasswordCredentials {
11 pub password_hash_b64: String,
13}
14
15#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
18#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
19pub struct SendEmailCredentials {
20 pub email: String,
22}
23
24#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
26#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
27pub struct SendEmailOtpCredentials {
28 pub email: String,
30 pub otp: String,
32}
33
34#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
36#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
37#[serde(untagged)]
41pub enum SendAccessCredentials {
42 #[allow(missing_docs)]
43 Password(SendPasswordCredentials),
44 #[allow(missing_docs)]
45 Email(SendEmailCredentials),
46 #[allow(missing_docs)]
47 EmailOtp(SendEmailOtpCredentials),
48}
49
50#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
52#[serde(rename_all = "camelCase", deny_unknown_fields)]
53#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
54pub struct SendAccessTokenRequest {
55 pub send_id: String,
57
58 #[serde(default, skip_serializing_if = "Option::is_none")]
59 #[cfg_attr(feature = "wasm", tsify(optional))]
60 pub send_access_credentials: Option<SendAccessCredentials>,
62}
63
64#[cfg(test)]
65mod tests {
66 use super::*;
67
68 mod send_access_token_request_tests {
69 use serde_json::{from_str, to_string};
70
71 use super::*;
72
73 #[test]
74 fn deserialize_camelcase_request() {
75 let json = r#"
76 {
77 "sendId": "abc123",
78 "sendAccessCredentials": { "passwordHashB64": "ha$h" }
79 }"#;
80
81 let req: SendAccessTokenRequest = from_str(json).unwrap();
82 assert_eq!(req.send_id, "abc123");
83
84 let creds = req.send_access_credentials.expect("expected Some");
85 match creds {
86 SendAccessCredentials::Password(p) => assert_eq!(p.password_hash_b64, "ha$h"),
87 _ => panic!("expected Password variant"),
88 }
89 }
90
91 #[test]
92 fn serialize_camelcase_request_with_credentials() {
93 let req = SendAccessTokenRequest {
94 send_id: "abc123".into(),
95 send_access_credentials: Some(SendAccessCredentials::Password(
96 SendPasswordCredentials {
97 password_hash_b64: "ha$h".into(),
98 },
99 )),
100 };
101 let json = to_string(&req).unwrap();
102 assert_eq!(
103 json,
104 r#"{"sendId":"abc123","sendAccessCredentials":{"passwordHashB64":"ha$h"}}"#
105 );
106 }
107
108 #[test]
109 fn serialize_omits_optional_credentials_when_none() {
110 let req = SendAccessTokenRequest {
111 send_id: "abc123".into(),
112 send_access_credentials: None,
113 };
114 let json = to_string(&req).unwrap();
115 assert_eq!(json, r#"{"sendId":"abc123"}"#);
116 }
117
118 #[test]
119 fn roundtrip_camel_in_to_camel_out() {
120 let in_json = r#"
121 {
122 "sendId": "abc123",
123 "sendAccessCredentials": { "passwordHashB64": "ha$h" }
124 }"#;
125
126 let req: SendAccessTokenRequest = from_str(in_json).unwrap();
127 let out_json = to_string(&req).unwrap();
128 assert_eq!(
129 out_json,
130 r#"{"sendId":"abc123","sendAccessCredentials":{"passwordHashB64":"ha$h"}}"#
131 );
132 }
133
134 #[test]
135 fn snakecase_top_level_keys_are_rejected() {
136 let json = r#"
137 {
138 "send_id": "abc123",
139 "sendAccessCredentials": { "passwordHashB64": "ha$h" }
140 }"#;
141 let err = from_str::<SendAccessTokenRequest>(json).unwrap_err();
142 let msg = err.to_string();
143 assert!(
144 msg.contains("unknown field") && msg.contains("send_id"),
145 "unexpected: {msg}"
146 );
147 }
148
149 #[test]
150 fn extra_top_level_key_is_rejected() {
151 let json = r#"
152 {
153 "sendId": "abc123",
154 "sendAccessCredentials": { "passwordHashB64": "ha$h" },
155 "extra": "nope"
156 }"#;
157 let err = from_str::<SendAccessTokenRequest>(json).unwrap_err();
158 let msg = err.to_string();
159 assert!(
160 msg.contains("unknown field") && msg.contains("extra"),
161 "unexpected: {msg}"
162 );
163 }
164
165 #[test]
166 fn snakecase_nested_keys_are_rejected() {
167 let json = r#"
168 {
169 "sendId": "abc123",
170 "sendAccessCredentials": { "password_hash_b64": "ha$h" }
171 }"#;
172
173 let err = serde_json::from_str::<SendAccessTokenRequest>(json).unwrap_err();
174 let msg = err.to_string();
175 assert!(
176 msg.contains("did not match any variant"),
177 "unexpected: {msg}"
178 );
179 }
180
181 #[test]
182 fn extra_nested_key_is_rejected() {
183 let json = r#"
184 {
185 "sendId": "abc123",
186 "sendAccessCredentials": {
187 "passwordHashB64": "ha$h",
188 "extra": "nope"
189 }
190 }"#;
191 let err = from_str::<SendAccessTokenRequest>(json).unwrap_err();
192 let msg = err.to_string();
193 assert!(
194 msg.contains("did not match any variant"),
195 "unexpected: {msg}"
196 );
197 }
198 }
199
200 mod send_access_credentials_tests {
201 use super::*;
202
203 mod send_access_password_credentials_tests {
204 use serde_json::{from_str, to_string};
205
206 use super::*;
207 #[test]
208 fn deserializes_camelcase_from_ts() {
209 let json = r#"{ "passwordHashB64": "ha$h" }"#;
210 let s: SendPasswordCredentials = from_str(json).unwrap();
211 assert_eq!(s.password_hash_b64, "ha$h");
212 }
213
214 #[test]
215 fn serializes_camelcase_to_wire() {
216 let s = SendPasswordCredentials {
217 password_hash_b64: "ha$h".into(),
218 };
219 let json = to_string(&s).unwrap();
220 assert_eq!(json, r#"{"passwordHashB64":"ha$h"}"#);
221 }
222
223 #[test]
224 fn roundtrip_camel_in_to_camel_out() {
225 let in_json = r#"{ "passwordHashB64": "ha$h" }"#;
226 let parsed: SendPasswordCredentials = from_str(in_json).unwrap();
227 let out_json = to_string(&parsed).unwrap();
228 assert_eq!(out_json, r#"{"passwordHashB64":"ha$h"}"#);
229 }
230 }
231
232 #[test]
233 fn serialize_email_credentials() {
234 let creds = SendAccessCredentials::Email(SendEmailCredentials {
235 email: "[email protected]".into(),
236 });
237 let json = serde_json::to_string(&creds).unwrap();
238 assert_eq!(json, r#"{"email":"[email protected]"}"#);
239 }
240
241 #[test]
242 fn serialize_email_otp_credentials() {
243 let creds = SendAccessCredentials::EmailOtp(SendEmailOtpCredentials {
244 email: "[email protected]".into(),
245 otp: "123456".into(),
246 });
247 let json = serde_json::to_string(&creds).unwrap();
248 assert_eq!(json, r#"{"email":"[email protected]","otp":"123456"}"#);
249 }
250 }
251}