actix_web_httpauth/extractors/
errors.rs

1use std::{error::Error, fmt};
2
3use actix_web::{http::StatusCode, HttpResponse, ResponseError};
4
5use crate::headers::www_authenticate::{Challenge, WwwAuthenticate};
6
7/// Authentication error returned by authentication extractors.
8///
9/// Different extractors may extend `AuthenticationError` implementation in order to provide access
10/// inner challenge fields.
11#[derive(Debug)]
12pub struct AuthenticationError<C: Challenge> {
13    challenge: C,
14    status_code: StatusCode,
15}
16
17impl<C: Challenge> AuthenticationError<C> {
18    /// Creates new authentication error from the provided `challenge`.
19    ///
20    /// By default returned error will resolve into the `HTTP 401` status code.
21    pub fn new(challenge: C) -> AuthenticationError<C> {
22        AuthenticationError {
23            challenge,
24            status_code: StatusCode::UNAUTHORIZED,
25        }
26    }
27
28    /// Returns mutable reference to the inner challenge instance.
29    pub fn challenge_mut(&mut self) -> &mut C {
30        &mut self.challenge
31    }
32
33    /// Returns mutable reference to the inner status code.
34    ///
35    /// Can be used to override returned status code, but by default this lib tries to stick to the
36    /// RFC, so it might be unreasonable.
37    pub fn status_code_mut(&mut self) -> &mut StatusCode {
38        &mut self.status_code
39    }
40}
41
42impl<C: Challenge> fmt::Display for AuthenticationError<C> {
43    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44        fmt::Display::fmt(&self.status_code, f)
45    }
46}
47
48impl<C: Challenge + 'static> Error for AuthenticationError<C> {}
49
50impl<C: Challenge + 'static> ResponseError for AuthenticationError<C> {
51    fn status_code(&self) -> StatusCode {
52        self.status_code
53    }
54
55    fn error_response(&self) -> HttpResponse {
56        HttpResponse::build(self.status_code())
57            .insert_header(WwwAuthenticate(self.challenge.clone()))
58            .finish()
59    }
60}
61
62#[cfg(test)]
63mod tests {
64    use actix_web::Error;
65
66    use super::*;
67    use crate::headers::www_authenticate::basic::Basic;
68
69    #[test]
70    fn test_status_code_is_preserved_across_error_conversions() {
71        let ae = AuthenticationError::new(Basic::default());
72        let expected = ae.status_code;
73
74        // Converting the AuthenticationError into a ResponseError should preserve the status code.
75        let err = Error::from(ae);
76        let res_err = err.as_response_error();
77        assert_eq!(expected, res_err.status_code());
78    }
79}