use displaydoc::Display;
use jsonwebtoken::Algorithm;
use jsonwebtoken::errors::Error as JWTError;
use jsonwebtoken::errors::ErrorKind;
use jsonwebtoken::jwk::KeyAlgorithm;
use serde::Deserialize;
use serde::Serialize;
use thiserror::Error;
use tower::BoxError;
#[derive(Debug, Display, Error)]
pub(crate) enum AuthenticationError {
CannotConvertToString,
InvalidJWTPrefix(String, String),
MissingJWTToken(String, String),
InvalidHeader(String, JWTError),
CannotCreateDecodingKey(JWTError),
JWKHasNoAlgorithm,
CannotDecodeJWT(JWTError),
CannotInsertClaimsIntoContext(BoxError),
CannotFindKID(String),
CannotFindSuitableKey(Algorithm, Option<String>),
InvalidIssuer { expected: String, token: String },
InvalidAudience { actual: String, expected: String },
UnsupportedKeyAlgorithm(KeyAlgorithm),
}
fn jwt_error_to_reason(jwt_err: &JWTError) -> &'static str {
let kind = jwt_err.kind();
match kind {
ErrorKind::InvalidToken => "INVALID_TOKEN",
ErrorKind::InvalidSignature => "INVALID_SIGNATURE",
ErrorKind::InvalidEcdsaKey => "INVALID_ECDSA_KEY",
ErrorKind::InvalidRsaKey(_) => "INVALID_RSA_KEY",
ErrorKind::RsaFailedSigning => "RSA_FAILED_SIGNING",
ErrorKind::InvalidAlgorithmName => "INVALID_ALGORITHM_NAME",
ErrorKind::InvalidKeyFormat => "INVALID_KEY_FORMAT",
ErrorKind::MissingRequiredClaim(_) => "MISSING_REQUIRED_CLAIM",
ErrorKind::ExpiredSignature => "EXPIRED_SIGNATURE",
ErrorKind::InvalidIssuer => "INVALID_ISSUER",
ErrorKind::InvalidAudience => "INVALID_AUDIENCE",
ErrorKind::InvalidSubject => "INVALID_SUBJECT",
ErrorKind::ImmatureSignature => "IMMATURE_SIGNATURE",
ErrorKind::InvalidAlgorithm => "INVALID_ALGORITHM",
ErrorKind::MissingAlgorithm => "MISSING_ALGORITHM",
ErrorKind::Base64(_) => "BASE64_ERROR",
ErrorKind::Json(_) => "JSON_ERROR",
ErrorKind::Utf8(_) => "UTF8_ERROR",
_ => "UNKNOWN_ERROR",
}
}
impl AuthenticationError {
pub(super) fn code(&self) -> &'static str {
match self {
AuthenticationError::CannotConvertToString => "CANNOT_CONVERT_TO_STRING",
AuthenticationError::InvalidJWTPrefix(_, _) => "INVALID_PREFIX",
AuthenticationError::MissingJWTToken(_, _) => "MISSING_JWT",
AuthenticationError::InvalidHeader(_, _) => "INVALID_HEADER",
AuthenticationError::CannotCreateDecodingKey(_) => "CANNOT_CREATE_DECODING_KEY",
AuthenticationError::JWKHasNoAlgorithm => "JWK_HAS_NO_ALGORITHM",
AuthenticationError::CannotDecodeJWT(_) => "CANNOT_DECODE_JWT",
AuthenticationError::CannotInsertClaimsIntoContext(_) => {
"CANNOT_INSERT_CLAIMS_INTO_CONTEXT"
}
AuthenticationError::CannotFindKID(_) => "CANNOT_FIND_KID",
AuthenticationError::CannotFindSuitableKey(_, _) => "CANNOT_FIND_SUITABLE_KEY",
AuthenticationError::InvalidIssuer { .. } => "INVALID_ISSUER",
AuthenticationError::InvalidAudience { .. } => "INVALID_AUDIENCE",
AuthenticationError::UnsupportedKeyAlgorithm(_) => "UNSUPPORTED_KEY_ALGORITHM",
}
}
fn reason(&self) -> Option<String> {
match self {
AuthenticationError::InvalidHeader(_, jwt_err)
| AuthenticationError::CannotCreateDecodingKey(jwt_err)
| AuthenticationError::CannotDecodeJWT(jwt_err) => {
Some(jwt_error_to_reason(jwt_err).into())
}
_ => None,
}
}
pub(super) fn as_context_object(&self) -> ErrorContext {
ErrorContext {
message: self.to_string(),
code: self.code().into(),
reason: self.reason(),
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub(super) struct ErrorContext {
pub(super) message: String,
pub(super) code: String,
pub(super) reason: Option<String>,
}
#[derive(Error, Debug)]
pub(crate) enum Error {
#[error("header_value_prefix must not contain whitespace")]
BadHeaderValuePrefix,
}