use http::header::ToStrError;
use snafu::prelude::*;
use strum::EnumMessage as _;
use crate::{
TokenType,
core::jwt::validator::JwtValidationError,
error::{ToRfc6750Error, TokenErrorCode, TokenValidationError},
validator::{
binding::{DPoPBindingError, MtlsBindingError},
extract::TokenExtractError,
},
};
#[derive(Debug, Snafu, strum::EnumMessage)]
#[snafu(visibility(pub(super)))]
#[non_exhaustive]
pub enum TokenBindingError {
#[strum(message = "The DPoP header is missing")]
MissingDPoPHeader,
#[strum(message = "The DPoP header value is invalid")]
DPoPHeaderNotString {
source: ToStrError,
},
#[snafu(display("Request has more than one DPoP header"))]
#[strum(message = "The request has more than one DPoP header")]
MultipleDPoPHeaders,
#[snafu(display("Token is DPoP-bound but was presented as Bearer"))]
#[strum(message = "The access token is DPoP-bound")]
DPoPRequiredForBoundToken,
#[snafu(display("DPoP-bound tokens are required"))]
#[strum(message = "DPoP is required to access this resource")]
DPoPRequired,
#[snafu(display("Unsupported cnf confirmation method: {method}"))]
#[strum(message = "The access token confirmation method is not supported")]
UnsupportedCnfMethod {
method: &'static str,
},
DPoPBinding {
source: DPoPBindingError,
},
MtlsBinding {
source: MtlsBindingError,
},
}
impl ToRfc6750Error for TokenBindingError {
fn attempted_scheme(&self) -> Option<TokenType> {
match self {
Self::MissingDPoPHeader
| Self::DPoPHeaderNotString { .. }
| Self::MultipleDPoPHeaders
| Self::DPoPBinding { .. } => Some(TokenType::DPoP),
Self::DPoPRequiredForBoundToken
| Self::DPoPRequired
| Self::UnsupportedCnfMethod { .. }
| Self::MtlsBinding { .. } => None,
}
}
fn token_error(&self) -> TokenValidationError {
match self {
Self::MissingDPoPHeader
| Self::DPoPHeaderNotString { .. }
| Self::MultipleDPoPHeaders => {
TokenValidationError::Client(TokenErrorCode::InvalidRequest)
}
Self::DPoPRequiredForBoundToken
| Self::DPoPRequired
| Self::UnsupportedCnfMethod { .. }
| Self::MtlsBinding { .. } => {
TokenValidationError::Client(TokenErrorCode::InvalidToken)
}
Self::DPoPBinding { source } => source.token_error(),
}
}
fn error_description(&self) -> Option<String> {
match self {
Self::DPoPBinding { source } => source.error_description(),
Self::MtlsBinding { source } => source.error_description(),
other => other.get_message().map(str::to_string),
}
}
}
#[derive(Debug, Snafu)]
#[snafu(visibility(pub(super)))]
#[non_exhaustive]
pub enum ValidateHeadersError {
#[snafu(display("Token presentation error"))]
Extract {
source: TokenExtractError,
},
#[snafu(display("Token binding error"))]
Binding {
token_type: TokenType,
source: TokenBindingError,
},
InvalidJwt {
token_type: TokenType,
source: JwtValidationError,
},
}
impl ToRfc6750Error for ValidateHeadersError {
fn attempted_scheme(&self) -> Option<TokenType> {
match self {
Self::Extract { source } => source.attempted_scheme(),
Self::Binding { token_type, .. } | Self::InvalidJwt { token_type, .. } => {
Some(*token_type)
}
}
}
fn token_error(&self) -> TokenValidationError {
match self {
Self::Extract { source } => source.token_error(),
Self::Binding { source, .. } => source.token_error(),
Self::InvalidJwt { source, .. } => source.token_error(),
}
}
fn error_description(&self) -> Option<String> {
match self {
Self::Extract { source } => source.error_description(),
Self::Binding { source, .. } => source.error_description(),
Self::InvalidJwt { source, .. } => source.error_description(),
}
}
}