bitwarden_auth/login/login_via_password/
password_login_error.rs1use bitwarden_core::key_management::MasterPasswordError;
2use bitwarden_error::bitwarden_error;
3use thiserror::Error;
4
5use crate::login::api::response::{
6 InvalidGrantError, LoginErrorApiResponse, OAuth2ErrorApiResponse, PasswordInvalidGrantError,
7};
8
9#[bitwarden_error(flat)]
14#[derive(Debug, Error)]
15pub enum PasswordLoginError {
16 #[error("Invalid username or password provided.")]
26 InvalidUsernameOrPassword,
27
28 #[error(transparent)]
36 PasswordAuthenticationDataDerivation(#[from] MasterPasswordError),
37
38 #[error("Unknown password login error: {0}")]
52 Unknown(String),
53}
54
55impl From<LoginErrorApiResponse> for PasswordLoginError {
61 fn from(error: LoginErrorApiResponse) -> Self {
62 match error {
63 LoginErrorApiResponse::OAuth2Error(oauth_error) => match oauth_error {
64 OAuth2ErrorApiResponse::InvalidGrant { error_description } => {
65 match error_description {
66 Some(InvalidGrantError::Password(
67 PasswordInvalidGrantError::InvalidUsernameOrPassword,
68 )) => Self::InvalidUsernameOrPassword,
69 Some(InvalidGrantError::Unknown(error_code)) => {
70 Self::Unknown(format!("Invalid grant - unknown error: {error_code}"))
71 }
72 None => {
73 Self::Unknown("Invalid grant with no error description".to_string())
74 }
75 }
76 }
77 OAuth2ErrorApiResponse::InvalidRequest { error_description } => {
78 let desc = error_description.unwrap_or("no error description".to_string());
79 Self::Unknown(format!("Invalid request: {desc}"))
80 }
81 OAuth2ErrorApiResponse::InvalidClient { error_description } => {
82 let desc = error_description.unwrap_or("no error description".to_string());
83 Self::Unknown(format!("Invalid client: {desc}"))
84 }
85 OAuth2ErrorApiResponse::UnauthorizedClient { error_description } => {
86 let desc = error_description.unwrap_or("no error description".to_string());
87 Self::Unknown(format!("Unauthorized client: {desc}"))
88 }
89 OAuth2ErrorApiResponse::UnsupportedGrantType { error_description } => {
90 let desc = error_description.unwrap_or("no error description".to_string());
91 Self::Unknown(format!("Unsupported grant type: {desc}"))
92 }
93 OAuth2ErrorApiResponse::InvalidScope { error_description } => {
94 let desc = error_description.unwrap_or("no error description".to_string());
95 Self::Unknown(format!("Invalid scope: {desc}"))
96 }
97 OAuth2ErrorApiResponse::InvalidTarget { error_description } => {
98 let desc = error_description.unwrap_or("no error description".to_string());
99 Self::Unknown(format!("Invalid target: {desc}"))
100 }
101 },
102 LoginErrorApiResponse::UnexpectedError(msg) => {
103 Self::Unknown(format!("Unexpected error: {msg}"))
104 }
105 }
106 }
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112
113 const ERROR_DESC_NO_DESCRIPTION: &str = "no error description";
115 const TEST_ERROR_DESC: &str = "Test error description";
116
117 mod from_login_error_api_response {
118 use super::*;
119
120 #[test]
121 fn invalid_grant_with_invalid_username_or_password() {
122 let api_error =
123 LoginErrorApiResponse::OAuth2Error(OAuth2ErrorApiResponse::InvalidGrant {
124 error_description: Some(InvalidGrantError::Password(
125 PasswordInvalidGrantError::InvalidUsernameOrPassword,
126 )),
127 });
128
129 let result: PasswordLoginError = api_error.into();
130
131 assert!(matches!(
132 result,
133 PasswordLoginError::InvalidUsernameOrPassword
134 ));
135 }
136
137 #[test]
138 fn invalid_grant_with_unknown_error() {
139 let api_error =
140 LoginErrorApiResponse::OAuth2Error(OAuth2ErrorApiResponse::InvalidGrant {
141 error_description: Some(InvalidGrantError::Unknown(
142 "unknown_error_code".to_string(),
143 )),
144 });
145
146 let result: PasswordLoginError = api_error.into();
147
148 match result {
149 PasswordLoginError::Unknown(msg) => {
150 assert_eq!(msg, "Invalid grant - unknown error: unknown_error_code");
151 }
152 _ => panic!("Expected Unknown variant"),
153 }
154 }
155
156 #[test]
157 fn invalid_grant_with_no_error_description() {
158 let api_error =
159 LoginErrorApiResponse::OAuth2Error(OAuth2ErrorApiResponse::InvalidGrant {
160 error_description: None,
161 });
162
163 let result: PasswordLoginError = api_error.into();
164
165 match result {
166 PasswordLoginError::Unknown(msg) => {
167 assert_eq!(msg, "Invalid grant with no error description");
168 }
169 _ => panic!("Expected Unknown variant"),
170 }
171 }
172
173 #[test]
174 fn invalid_request_with_error_description() {
175 let api_error =
176 LoginErrorApiResponse::OAuth2Error(OAuth2ErrorApiResponse::InvalidRequest {
177 error_description: Some(TEST_ERROR_DESC.to_string()),
178 });
179
180 let result: PasswordLoginError = api_error.into();
181
182 match result {
183 PasswordLoginError::Unknown(msg) => {
184 assert_eq!(msg, format!("Invalid request: {TEST_ERROR_DESC}"));
185 }
186 _ => panic!("Expected Unknown variant"),
187 }
188 }
189
190 #[test]
191 fn invalid_request_without_error_description() {
192 let api_error =
193 LoginErrorApiResponse::OAuth2Error(OAuth2ErrorApiResponse::InvalidRequest {
194 error_description: None,
195 });
196
197 let result: PasswordLoginError = api_error.into();
198
199 match result {
200 PasswordLoginError::Unknown(msg) => {
201 assert_eq!(msg, format!("Invalid request: {ERROR_DESC_NO_DESCRIPTION}"));
202 }
203 _ => panic!("Expected Unknown variant"),
204 }
205 }
206
207 #[test]
208 fn invalid_client_with_error_description() {
209 let api_error =
210 LoginErrorApiResponse::OAuth2Error(OAuth2ErrorApiResponse::InvalidClient {
211 error_description: Some(TEST_ERROR_DESC.to_string()),
212 });
213
214 let result: PasswordLoginError = api_error.into();
215
216 match result {
217 PasswordLoginError::Unknown(msg) => {
218 assert_eq!(msg, format!("Invalid client: {TEST_ERROR_DESC}"));
219 }
220 _ => panic!("Expected Unknown variant"),
221 }
222 }
223
224 #[test]
225 fn invalid_client_without_error_description() {
226 let api_error =
227 LoginErrorApiResponse::OAuth2Error(OAuth2ErrorApiResponse::InvalidClient {
228 error_description: None,
229 });
230
231 let result: PasswordLoginError = api_error.into();
232
233 match result {
234 PasswordLoginError::Unknown(msg) => {
235 assert_eq!(msg, format!("Invalid client: {ERROR_DESC_NO_DESCRIPTION}"));
236 }
237 _ => panic!("Expected Unknown variant"),
238 }
239 }
240
241 #[test]
242 fn unauthorized_client_with_error_description() {
243 let api_error =
244 LoginErrorApiResponse::OAuth2Error(OAuth2ErrorApiResponse::UnauthorizedClient {
245 error_description: Some(TEST_ERROR_DESC.to_string()),
246 });
247
248 let result: PasswordLoginError = api_error.into();
249
250 match result {
251 PasswordLoginError::Unknown(msg) => {
252 assert_eq!(msg, format!("Unauthorized client: {TEST_ERROR_DESC}"));
253 }
254 _ => panic!("Expected Unknown variant"),
255 }
256 }
257
258 #[test]
259 fn unauthorized_client_without_error_description() {
260 let api_error =
261 LoginErrorApiResponse::OAuth2Error(OAuth2ErrorApiResponse::UnauthorizedClient {
262 error_description: None,
263 });
264
265 let result: PasswordLoginError = api_error.into();
266
267 match result {
268 PasswordLoginError::Unknown(msg) => {
269 assert_eq!(
270 msg,
271 format!("Unauthorized client: {ERROR_DESC_NO_DESCRIPTION}")
272 );
273 }
274 _ => panic!("Expected Unknown variant"),
275 }
276 }
277
278 #[test]
279 fn unsupported_grant_type_with_error_description() {
280 let api_error =
281 LoginErrorApiResponse::OAuth2Error(OAuth2ErrorApiResponse::UnsupportedGrantType {
282 error_description: Some(TEST_ERROR_DESC.to_string()),
283 });
284
285 let result: PasswordLoginError = api_error.into();
286
287 match result {
288 PasswordLoginError::Unknown(msg) => {
289 assert_eq!(msg, format!("Unsupported grant type: {TEST_ERROR_DESC}"));
290 }
291 _ => panic!("Expected Unknown variant"),
292 }
293 }
294
295 #[test]
296 fn unsupported_grant_type_without_error_description() {
297 let api_error =
298 LoginErrorApiResponse::OAuth2Error(OAuth2ErrorApiResponse::UnsupportedGrantType {
299 error_description: None,
300 });
301
302 let result: PasswordLoginError = api_error.into();
303
304 match result {
305 PasswordLoginError::Unknown(msg) => {
306 assert_eq!(
307 msg,
308 format!("Unsupported grant type: {ERROR_DESC_NO_DESCRIPTION}")
309 );
310 }
311 _ => panic!("Expected Unknown variant"),
312 }
313 }
314
315 #[test]
316 fn invalid_scope_with_error_description() {
317 let api_error =
318 LoginErrorApiResponse::OAuth2Error(OAuth2ErrorApiResponse::InvalidScope {
319 error_description: Some(TEST_ERROR_DESC.to_string()),
320 });
321
322 let result: PasswordLoginError = api_error.into();
323
324 match result {
325 PasswordLoginError::Unknown(msg) => {
326 assert_eq!(msg, format!("Invalid scope: {TEST_ERROR_DESC}"));
327 }
328 _ => panic!("Expected Unknown variant"),
329 }
330 }
331
332 #[test]
333 fn invalid_scope_without_error_description() {
334 let api_error =
335 LoginErrorApiResponse::OAuth2Error(OAuth2ErrorApiResponse::InvalidScope {
336 error_description: None,
337 });
338
339 let result: PasswordLoginError = api_error.into();
340
341 match result {
342 PasswordLoginError::Unknown(msg) => {
343 assert_eq!(msg, format!("Invalid scope: {ERROR_DESC_NO_DESCRIPTION}"));
344 }
345 _ => panic!("Expected Unknown variant"),
346 }
347 }
348
349 #[test]
350 fn invalid_target_with_error_description() {
351 let api_error =
352 LoginErrorApiResponse::OAuth2Error(OAuth2ErrorApiResponse::InvalidTarget {
353 error_description: Some(TEST_ERROR_DESC.to_string()),
354 });
355
356 let result: PasswordLoginError = api_error.into();
357
358 match result {
359 PasswordLoginError::Unknown(msg) => {
360 assert_eq!(msg, format!("Invalid target: {TEST_ERROR_DESC}"));
361 }
362 _ => panic!("Expected Unknown variant"),
363 }
364 }
365
366 #[test]
367 fn invalid_target_without_error_description() {
368 let api_error =
369 LoginErrorApiResponse::OAuth2Error(OAuth2ErrorApiResponse::InvalidTarget {
370 error_description: None,
371 });
372
373 let result: PasswordLoginError = api_error.into();
374
375 match result {
376 PasswordLoginError::Unknown(msg) => {
377 assert_eq!(msg, format!("Invalid target: {ERROR_DESC_NO_DESCRIPTION}"));
378 }
379 _ => panic!("Expected Unknown variant"),
380 }
381 }
382
383 #[test]
384 fn unexpected_error() {
385 let api_error = LoginErrorApiResponse::UnexpectedError("Network timeout".to_string());
386
387 let result: PasswordLoginError = api_error.into();
388
389 match result {
390 PasswordLoginError::Unknown(msg) => {
391 assert_eq!(msg, "Unexpected error: Network timeout");
392 }
393 _ => panic!("Expected Unknown variant"),
394 }
395 }
396 }
397}