actix_firebase_auth/
impls.rs

1use actix_web::error::ErrorUnauthorized;
2use actix_web::http::StatusCode;
3use actix_web::{FromRequest, HttpRequest, dev, http::header::Header, web};
4use actix_web::{HttpResponse, ResponseError};
5use actix_web_httpauth::headers::authorization::{Authorization, Bearer};
6use futures::future::{Ready, err, ok};
7use tracing::debug;
8
9use crate::jwk::{PublicKeysError, VerificationError};
10use crate::{Error, FirebaseAuth, FirebaseUser};
11
12fn status_code_from_http_err(err: &reqwest::Error) -> StatusCode {
13    let code = err.status().map(|s| s.as_u16()).unwrap_or_else(|| 500);
14    StatusCode::from_u16(code).unwrap_or(StatusCode::BAD_GATEWAY) // Use BAD_GATEWAY for upstream fetch failures
15}
16
17impl ResponseError for Error {
18    fn error_response(&self) -> HttpResponse {
19        HttpResponse::build(self.status_code()).json(self.to_string())
20    }
21
22    fn status_code(&self) -> StatusCode {
23        match self {
24            Error::PublicKeysError(err) => match err {
25                PublicKeysError::FetchPublicKeys(http_err)
26                | PublicKeysError::PublicKeyParseError(http_err) => {
27                    status_code_from_http_err(http_err)
28                }
29
30                PublicKeysError::MissingCacheControlHeader
31                | PublicKeysError::MissingMaxAgeDirective
32                | PublicKeysError::EmptyMaxAgeDirective
33                | PublicKeysError::InvalidMaxAgeValue => {
34                    // Indicates a misconfigured or invalid response from the identity provider
35                    StatusCode::INTERNAL_SERVER_ERROR
36                }
37            },
38
39            Error::VerificationError(err) => match err {
40                VerificationError::InvalidSignature => {
41                    // Token is invalid or tampered with
42                    StatusCode::UNAUTHORIZED
43                }
44                VerificationError::InvalidKeyAlgorithm => {
45                    // Token uses unsupported algorithm – client bug or attacker
46                    StatusCode::BAD_REQUEST
47                }
48                VerificationError::InvalidToken => {
49                    // Token is malformed or structurally invalid
50                    StatusCode::BAD_REQUEST
51                }
52                VerificationError::NoKidHeader => {
53                    // Token doesn't specify which key was used to sign – malformed
54                    StatusCode::BAD_REQUEST
55                }
56                VerificationError::NoMatchingKid => {
57                    // Token specifies a `kid` for which we have no key – likely expired key
58                    StatusCode::UNAUTHORIZED
59                }
60                VerificationError::CannotDecodePublicKeys => {
61                    // Server failed to decode key set
62                    StatusCode::INTERNAL_SERVER_ERROR
63                }
64                VerificationError::CannotDecodeJwt(_) => {
65                    // Server failed to decode key set
66                    StatusCode::UNAUTHORIZED
67                }
68            },
69        }
70    }
71}
72
73fn get_bearer_token(header: &str) -> Option<String> {
74    let prefix_len = "Bearer ".len();
75
76    match header.len() {
77        l if l < prefix_len => None,
78        _ => Some(header[prefix_len..].to_string()),
79    }
80}
81
82impl FromRequest for FirebaseUser {
83    type Error = actix_web::Error;
84    type Future = Ready<Result<Self, Self::Error>>;
85
86    fn from_request(req: &HttpRequest, _: &mut dev::Payload) -> Self::Future {
87        let firebase_auth = req
88            .app_data::<web::Data<FirebaseAuth>>()
89            .expect("must initialize FirebaseAuth in Application Data");
90
91        let bearer = match Authorization::<Bearer>::parse(req) {
92            Err(e) => return err(e.into()),
93            Ok(v) => get_bearer_token(&v.to_string()).unwrap_or_default(),
94        };
95
96        debug!("Got bearer token {}", bearer);
97
98        match firebase_auth.verify(&bearer) {
99            Err(e) => err(ErrorUnauthorized(format!("Failed to verify Token {}", e))),
100            Ok(user) => ok(user),
101        }
102    }
103}