use std::borrow::Cow;
use std::fmt;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum ErrorCode {
InvalidRequest,
InvalidClient,
InvalidGrant,
UnauthorizedClient,
UnsupportedGrantType,
InvalidScope,
AccessDenied,
UnsupportedResponseType,
ServerError,
TemporarilyUnavailable,
AuthorizationPending,
SlowDown,
ExpiredToken,
InvalidAuthorizationDetails,
InvalidTarget,
#[cfg(feature = "consent")]
InsufficientUserAuthentication,
#[cfg(feature = "dpop")]
InvalidDpopProof,
#[cfg(feature = "par")]
InvalidRequestUri,
#[cfg(feature = "jar")]
InvalidRequestObject,
#[cfg(feature = "jar")]
RequestNotSupported,
}
impl ErrorCode {
pub fn as_str(self) -> &'static str {
match self {
ErrorCode::InvalidRequest => "invalid_request",
ErrorCode::InvalidClient => "invalid_client",
ErrorCode::InvalidGrant => "invalid_grant",
ErrorCode::UnauthorizedClient => "unauthorized_client",
ErrorCode::UnsupportedGrantType => "unsupported_grant_type",
ErrorCode::InvalidScope => "invalid_scope",
ErrorCode::AccessDenied => "access_denied",
ErrorCode::UnsupportedResponseType => "unsupported_response_type",
ErrorCode::ServerError => "server_error",
ErrorCode::TemporarilyUnavailable => "temporarily_unavailable",
ErrorCode::AuthorizationPending => "authorization_pending",
ErrorCode::SlowDown => "slow_down",
ErrorCode::ExpiredToken => "expired_token",
ErrorCode::InvalidAuthorizationDetails => "invalid_authorization_details",
ErrorCode::InvalidTarget => "invalid_target",
#[cfg(feature = "consent")]
ErrorCode::InsufficientUserAuthentication => "insufficient_user_authentication",
#[cfg(feature = "dpop")]
ErrorCode::InvalidDpopProof => "invalid_dpop_proof",
#[cfg(feature = "par")]
ErrorCode::InvalidRequestUri => "invalid_request_uri",
#[cfg(feature = "jar")]
ErrorCode::InvalidRequestObject => "invalid_request_object",
#[cfg(feature = "jar")]
ErrorCode::RequestNotSupported => "request_not_supported",
}
}
pub fn http_status(self) -> u16 {
match self {
ErrorCode::InvalidClient => 401,
ErrorCode::ServerError => 500,
ErrorCode::TemporarilyUnavailable => 503,
ErrorCode::InvalidRequest => 400,
ErrorCode::InvalidGrant => 400,
ErrorCode::UnauthorizedClient => 400,
ErrorCode::UnsupportedGrantType => 400,
ErrorCode::InvalidScope => 400,
ErrorCode::AccessDenied => 400,
ErrorCode::UnsupportedResponseType => 400,
ErrorCode::AuthorizationPending => 400,
ErrorCode::SlowDown => 400,
ErrorCode::ExpiredToken => 400,
ErrorCode::InvalidTarget => 400,
ErrorCode::InvalidAuthorizationDetails => 400,
#[cfg(feature = "consent")]
ErrorCode::InsufficientUserAuthentication => 400,
#[cfg(feature = "dpop")]
ErrorCode::InvalidDpopProof => 400,
#[cfg(feature = "par")]
ErrorCode::InvalidRequestUri => 400,
#[cfg(feature = "jar")]
ErrorCode::InvalidRequestObject => 400,
#[cfg(feature = "jar")]
ErrorCode::RequestNotSupported => 400,
}
}
}
impl fmt::Display for ErrorCode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ErrorResponse {
pub error: ErrorCode,
#[serde(skip_serializing_if = "Option::is_none")]
pub error_description: Option<Cow<'static, str>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error_uri: Option<Cow<'static, str>>,
}
impl ErrorResponse {
pub fn new(error: ErrorCode) -> Self {
ErrorResponse {
error,
error_description: None,
error_uri: None,
}
}
pub fn with_description(mut self, description: impl Into<Cow<'static, str>>) -> Self {
self.error_description = Some(description.into());
self
}
pub fn with_uri(mut self, uri: impl Into<Cow<'static, str>>) -> Self {
self.error_uri = Some(uri.into());
self
}
pub fn http_status(&self) -> u16 {
self.error.http_status()
}
}
impl fmt::Display for ErrorResponse {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.error_description {
Some(d) => write!(f, "{}: {}", self.error, d),
None => f.write_str(self.error.as_str()),
}
}
}
impl std::error::Error for ErrorResponse {}
#[cfg(test)]
#[path = "tests/error.rs"]
mod tests;