#[derive(Debug, thiserror::Error)]
pub enum AuthError {
#[error("missing authentication token")]
MissingToken,
#[error("invalid token format: {0}")]
InvalidFormat(String),
#[error("invalid token signature: {0}")]
InvalidSignature(String),
#[error("token has expired")]
Expired,
#[error("invalid audience")]
InvalidAudience,
#[error("invalid domain: got '{domain}', expected '{expected}'")]
InvalidDomain { domain: String, expected: String },
#[error("token missing email claim")]
MissingEmail,
#[error("failed to fetch JWKS: {0}")]
JwksFetchError(String),
#[error("no matching key for kid '{0}'")]
NoMatchingKey(String),
}
impl AuthError {
pub fn is_client_error(&self) -> bool {
matches!(
self,
AuthError::MissingToken
| AuthError::InvalidFormat(_)
| AuthError::InvalidSignature(_)
| AuthError::Expired
| AuthError::InvalidAudience
| AuthError::InvalidDomain { .. }
| AuthError::MissingEmail
| AuthError::NoMatchingKey(_)
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_auth_error_display() {
let e = AuthError::MissingToken;
assert_eq!(e.to_string(), "missing authentication token");
}
#[test]
fn test_auth_error_invalid_domain_display() {
let e = AuthError::InvalidDomain {
domain: "other.com".to_string(),
expected: "banyan.com".to_string(),
};
assert_eq!(
e.to_string(),
"invalid domain: got 'other.com', expected 'banyan.com'"
);
}
#[test]
fn test_is_client_error() {
assert!(AuthError::MissingToken.is_client_error());
assert!(AuthError::Expired.is_client_error());
assert!(!AuthError::JwksFetchError("err".into()).is_client_error());
}
}