use actix_web::error::InternalError;
use actix_web::http::{header, StatusCode};
use actix_web::{dev, http::header::Header, web, FromRequest, HttpRequest};
use actix_web::{HttpResponse, ResponseError};
use actix_web_httpauth::headers::authorization::{Authorization, Bearer};
use futures::future::{err, ok, Ready};
use crate::jwk::{PublicKeysError, VerificationError};
use crate::{Error, FirebaseAuth, FirebaseUser};
fn status_code_from_http_err(err: &reqwest::Error) -> StatusCode {
let code = err.status().map_or_else(|| 500, |s| s.as_u16());
StatusCode::from_u16(code).unwrap_or(StatusCode::BAD_GATEWAY) }
impl ResponseError for Error {
fn error_response(&self) -> HttpResponse {
HttpResponse::build(self.status_code()).json(self.to_string())
}
fn status_code(&self) -> StatusCode {
match self {
Error::PublicKeysError(err) => match err {
PublicKeysError::FetchPublicKeys(http_err)
| PublicKeysError::PublicKeyParseError(http_err) => {
status_code_from_http_err(http_err)
}
PublicKeysError::MissingCacheControlHeader
| PublicKeysError::MissingMaxAgeDirective
| PublicKeysError::EmptyMaxAgeDirective
| PublicKeysError::InvalidMaxAgeValue => {
StatusCode::INTERNAL_SERVER_ERROR
}
},
Error::VerificationError(err) => match err {
VerificationError::InvalidSignature => {
StatusCode::UNAUTHORIZED
}
VerificationError::InvalidKeyAlgorithm => {
StatusCode::BAD_REQUEST
}
VerificationError::InvalidToken => {
StatusCode::BAD_REQUEST
}
VerificationError::NoKidHeader => {
StatusCode::BAD_REQUEST
}
VerificationError::NoMatchingKid => {
StatusCode::UNAUTHORIZED
}
VerificationError::CannotDecodePublicKeys => {
StatusCode::INTERNAL_SERVER_ERROR
}
VerificationError::CannotDecodeJwt(_) => {
StatusCode::UNAUTHORIZED
}
},
#[cfg(feature = "idp")]
Error::IdpError(err) => err.error_response().status(),
}
}
}
impl FromRequest for FirebaseUser {
type Error = actix_web::Error;
type Future = Ready<Result<Self, Self::Error>>;
fn from_request(req: &HttpRequest, _: &mut dev::Payload) -> Self::Future {
let firebase_auth = req
.app_data::<web::Data<FirebaseAuth>>()
.expect("FirebaseAuth should be initialized in application data");
let bearer = match Authorization::<Bearer>::parse(req) {
Ok(header) => header.into_scheme(),
Err(_) => {
return err(missing_or_malformed_auth_header());
}
};
let id_token = bearer.token();
match firebase_auth.verify(id_token) {
Ok(user) => ok(user),
Err(crate::Error::VerificationError(
VerificationError::CannotDecodePublicKeys,
)) => err(internal_token_verification_error()),
Err(other) => err(invalid_token_error(&other)),
}
}
}
fn internal_token_verification_error() -> actix_web::Error {
let response = HttpResponse::InternalServerError()
.body("Internal error during token verification");
InternalError::from_response("token_verification_failure", response).into()
}
fn missing_or_malformed_auth_header() -> actix_web::Error {
unauthorized_with_www_authenticate(
"invalid_request",
"Authorization header missing or not using Bearer scheme",
"Authorization header is missing or malformed",
)
}
fn invalid_token_error(err: &crate::Error) -> actix_web::Error {
unauthorized_with_www_authenticate(
"invalid_token",
&err.to_string(),
format!("Failed to verify Firebase ID token: {err}"),
)
}
fn unauthorized_with_www_authenticate(
www_error_code: &str,
www_error_description: &str,
body: impl Into<String>,
) -> actix_web::Error {
let header_value = format!(
r#"Bearer realm="firebase", error="{www_error_code}", error_description="{www_error_description}""#
);
let response = HttpResponse::Unauthorized()
.insert_header((header::WWW_AUTHENTICATE, header_value))
.body(body.into());
InternalError::from_response("auth_error", response).into()
}