actix_web_middleware_keycloak_auth/
errors.rs

1// actix-web-middleware-keycloak-auth
2//
3// Copyright: 2020, David Sferruzza
4// License: MIT
5
6use actix_web::body::BoxBody;
7use actix_web::http::StatusCode;
8use actix_web::{HttpResponse, ResponseError};
9
10use super::Role;
11
12/// An authentication error
13#[derive(Debug, Clone, PartialEq, Eq)]
14pub enum AuthError {
15    /// The `Authorization` header is missing
16    NoAuthorizationHeader,
17    /// The value of the `Authorization` header is not `Bearer [JWT]`
18    InvalidAuthorizationHeader,
19    /// The JWT is invalid (bad structure, wrong signature, ...)
20    InvalidJwt(String),
21    /// The JWT does not contain expected claims
22    DecodeError(String),
23    /// The JWT contains role claims that does not have the expected type/structure
24    RoleParsingError(String),
25    /// The JWT does not contain some required roles
26    MissingRoles(Vec<Role>),
27}
28
29impl ResponseError for AuthError {
30    fn status_code(&self) -> StatusCode {
31        match self {
32            Self::MissingRoles(_) => StatusCode::FORBIDDEN,
33            Self::InvalidAuthorizationHeader => StatusCode::BAD_REQUEST,
34            _ => StatusCode::UNAUTHORIZED,
35        }
36    }
37
38    fn error_response(&self) -> HttpResponse {
39        HttpResponse::new(self.status_code()).set_body(BoxBody::new(self.to_string()))
40    }
41}
42
43impl std::fmt::Display for AuthError {
44    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45        match self {
46            Self::NoAuthorizationHeader => f.write_str("No bearer token was provided"),
47            Self::InvalidAuthorizationHeader => {
48                f.write_str("Authorization header value is invalid (cannot convert it into string)")
49            }
50            Self::InvalidJwt(e) => write!(f, "Invalid JWT token ({})", e),
51            Self::DecodeError(e) => write!(f, "Error while decoding JWT token ({})", e),
52            Self::RoleParsingError(e) => write!(
53                f,
54                "Error while parsing Keycloak roles from JWT token ({})",
55                e
56            ),
57            Self::MissingRoles(roles) => {
58                write!(
59                    f,
60                    "JWT token is missing roles: {}",
61                    &roles
62                        .iter()
63                        .map(|r| r.to_string())
64                        .collect::<Vec<String>>()
65                        .join(", ")
66                )
67            }
68        }
69    }
70}
71
72impl AuthError {
73    /// Build a HTTP response from an authentication error
74    pub fn to_response(&self, detailed_responses: bool) -> HttpResponse {
75        if detailed_responses {
76            self.error_response()
77        } else {
78            HttpResponse::build(self.status_code()).body(self.status_code().to_string())
79        }
80    }
81}
82
83/// An error that happened while trying to extract and parse an unstructured claim
84#[derive(Debug)]
85pub enum ClaimError {
86    /// The claim cannot be found
87    NotFound(String),
88    /// The claim cannot be parsed as the provided/inferred type
89    ParseError(String, serde_json::Error),
90}
91
92impl std::fmt::Display for ClaimError {
93    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
94        match self {
95            Self::NotFound(key) => write!(f, "Claim '{}' was not found", key),
96            Self::ParseError(key, err) => write!(f, "Parsing claim '{}' failed: {}", key, err),
97        }
98    }
99}