use std::{str::Utf8Error, sync::Arc};
use headers::authorization::InvalidBearerToken;
use http::{header::ToStrError, StatusCode};
use mas_http::{catch_http_codes, form_urlencoded_request, json_request, json_response};
use mas_jose::{
claims::ClaimError,
jwa::InvalidAlgorithm,
jwt::{JwtDecodeError, JwtSignatureError, NoKeyWorked},
};
use oauth2_types::{
errors::ClientErrorCode, oidc::ProviderMetadataVerificationError, pkce::CodeChallengeError,
};
use serde::{Deserialize, Serialize};
use thiserror::Error;
pub use tower::BoxError;
#[derive(Debug, Error)]
#[error(transparent)]
pub enum Error {
Discovery(#[from] DiscoveryError),
Jwks(#[from] JwksError),
Registration(#[from] RegistrationError),
Authorization(#[from] AuthorizationError),
TokenAuthorizationCode(#[from] TokenAuthorizationCodeError),
TokenClientCredentials(#[from] TokenRequestError),
TokenRefresh(#[from] TokenRefreshError),
TokenRevoke(#[from] TokenRevokeError),
UserInfo(#[from] UserInfoError),
Introspection(#[from] IntrospectionError),
AccountManagement(#[from] AccountManagementError),
}
#[derive(Debug, Error)]
pub enum DiscoveryError {
#[error(transparent)]
IntoUrl(#[from] url::ParseError),
#[error(transparent)]
IntoHttp(#[from] http::Error),
#[error(transparent)]
Http(#[from] HttpError),
#[error(transparent)]
FromJson(#[from] serde_json::Error),
#[error(transparent)]
Validation(#[from] ProviderMetadataVerificationError),
#[error(transparent)]
Service(BoxError),
#[error("Discovery is disabled for this provider")]
Disabled,
}
impl<S> From<json_response::Error<S>> for DiscoveryError
where
S: Into<DiscoveryError>,
{
fn from(err: json_response::Error<S>) -> Self {
match err {
json_response::Error::Deserialize { inner } => inner.into(),
json_response::Error::Service { inner } => inner.into(),
}
}
}
impl<S> From<catch_http_codes::Error<S, Option<ErrorBody>>> for DiscoveryError
where
S: Into<BoxError>,
{
fn from(err: catch_http_codes::Error<S, Option<ErrorBody>>) -> Self {
match err {
catch_http_codes::Error::HttpError { status_code, inner } => {
Self::Http(HttpError::new(status_code, inner))
}
catch_http_codes::Error::Service { inner } => Self::Service(inner.into()),
}
}
}
#[derive(Debug, Error)]
pub enum RegistrationError {
#[error(transparent)]
IntoHttp(#[from] http::Error),
#[error(transparent)]
Json(#[from] serde_json::Error),
#[error(transparent)]
Http(#[from] HttpError),
#[error("missing client secret in response")]
MissingClientSecret,
#[error(transparent)]
Service(BoxError),
}
impl<S> From<json_request::Error<S>> for RegistrationError
where
S: Into<RegistrationError>,
{
fn from(err: json_request::Error<S>) -> Self {
match err {
json_request::Error::Serialize { inner } => inner.into(),
json_request::Error::Service { inner } => inner.into(),
}
}
}
impl<S> From<json_response::Error<S>> for RegistrationError
where
S: Into<RegistrationError>,
{
fn from(err: json_response::Error<S>) -> Self {
match err {
json_response::Error::Deserialize { inner } => inner.into(),
json_response::Error::Service { inner } => inner.into(),
}
}
}
impl<S> From<catch_http_codes::Error<S, Option<ErrorBody>>> for RegistrationError
where
S: Into<BoxError>,
{
fn from(err: catch_http_codes::Error<S, Option<ErrorBody>>) -> Self {
match err {
catch_http_codes::Error::HttpError { status_code, inner } => {
HttpError::new(status_code, inner).into()
}
catch_http_codes::Error::Service { inner } => Self::Service(inner.into()),
}
}
}
#[derive(Debug, Error)]
pub enum PushedAuthorizationError {
#[error(transparent)]
UrlEncoded(#[from] serde_urlencoded::ser::Error),
#[error(transparent)]
IntoHttp(#[from] http::Error),
#[error(transparent)]
Credentials(#[from] CredentialsError),
#[error(transparent)]
Http(#[from] HttpError),
#[error(transparent)]
Json(#[from] serde_json::Error),
#[error(transparent)]
Service(BoxError),
}
impl<S> From<form_urlencoded_request::Error<S>> for PushedAuthorizationError
where
S: Into<PushedAuthorizationError>,
{
fn from(err: form_urlencoded_request::Error<S>) -> Self {
match err {
form_urlencoded_request::Error::Serialize { inner } => inner.into(),
form_urlencoded_request::Error::Service { inner } => inner.into(),
}
}
}
impl<S> From<json_response::Error<S>> for PushedAuthorizationError
where
S: Into<PushedAuthorizationError>,
{
fn from(err: json_response::Error<S>) -> Self {
match err {
json_response::Error::Deserialize { inner } => inner.into(),
json_response::Error::Service { inner } => inner.into(),
}
}
}
impl<S> From<catch_http_codes::Error<S, Option<ErrorBody>>> for PushedAuthorizationError
where
S: Into<BoxError>,
{
fn from(err: catch_http_codes::Error<S, Option<ErrorBody>>) -> Self {
match err {
catch_http_codes::Error::HttpError { status_code, inner } => {
HttpError::new(status_code, inner).into()
}
catch_http_codes::Error::Service { inner } => Self::Service(inner.into()),
}
}
}
#[derive(Debug, Error)]
pub enum AuthorizationError {
#[error(transparent)]
Pkce(#[from] CodeChallengeError),
#[error(transparent)]
UrlEncoded(#[from] serde_urlencoded::ser::Error),
#[error(transparent)]
PushedAuthorization(#[from] PushedAuthorizationError),
}
#[derive(Debug, Error)]
pub enum TokenRequestError {
#[error(transparent)]
IntoHttp(#[from] http::Error),
#[error(transparent)]
Credentials(#[from] CredentialsError),
#[error(transparent)]
UrlEncoded(#[from] serde_urlencoded::ser::Error),
#[error(transparent)]
Http(#[from] HttpError),
#[error(transparent)]
Json(#[from] serde_json::Error),
#[error(transparent)]
Service(BoxError),
}
impl<S> From<form_urlencoded_request::Error<S>> for TokenRequestError
where
S: Into<TokenRequestError>,
{
fn from(err: form_urlencoded_request::Error<S>) -> Self {
match err {
form_urlencoded_request::Error::Serialize { inner } => inner.into(),
form_urlencoded_request::Error::Service { inner } => inner.into(),
}
}
}
impl<S> From<json_response::Error<S>> for TokenRequestError
where
S: Into<TokenRequestError>,
{
fn from(err: json_response::Error<S>) -> Self {
match err {
json_response::Error::Deserialize { inner } => inner.into(),
json_response::Error::Service { inner } => inner.into(),
}
}
}
impl<S> From<catch_http_codes::Error<S, Option<ErrorBody>>> for TokenRequestError
where
S: Into<BoxError>,
{
fn from(err: catch_http_codes::Error<S, Option<ErrorBody>>) -> Self {
match err {
catch_http_codes::Error::HttpError { status_code, inner } => {
HttpError::new(status_code, inner).into()
}
catch_http_codes::Error::Service { inner } => Self::Service(inner.into()),
}
}
}
#[derive(Debug, Error)]
pub enum TokenAuthorizationCodeError {
#[error(transparent)]
Token(#[from] TokenRequestError),
#[error(transparent)]
IdToken(#[from] IdTokenError),
}
#[derive(Debug, Error)]
pub enum TokenRefreshError {
#[error(transparent)]
Token(#[from] TokenRequestError),
#[error(transparent)]
IdToken(#[from] IdTokenError),
}
#[derive(Debug, Error)]
pub enum TokenRevokeError {
#[error(transparent)]
IntoHttp(#[from] http::Error),
#[error(transparent)]
Credentials(#[from] CredentialsError),
#[error(transparent)]
UrlEncoded(#[from] serde_urlencoded::ser::Error),
#[error(transparent)]
Json(#[from] serde_json::Error),
#[error(transparent)]
Http(#[from] HttpError),
#[error(transparent)]
Service(BoxError),
}
impl<S> From<form_urlencoded_request::Error<S>> for TokenRevokeError
where
S: Into<TokenRevokeError>,
{
fn from(err: form_urlencoded_request::Error<S>) -> Self {
match err {
form_urlencoded_request::Error::Serialize { inner } => inner.into(),
form_urlencoded_request::Error::Service { inner } => inner.into(),
}
}
}
impl<S> From<catch_http_codes::Error<S, Option<ErrorBody>>> for TokenRevokeError
where
S: Into<BoxError>,
{
fn from(err: catch_http_codes::Error<S, Option<ErrorBody>>) -> Self {
match err {
catch_http_codes::Error::HttpError { status_code, inner } => {
HttpError::new(status_code, inner).into()
}
catch_http_codes::Error::Service { inner } => Self::Service(inner.into()),
}
}
}
#[derive(Debug, Error)]
pub enum UserInfoError {
#[error(transparent)]
Discovery(#[from] Arc<DiscoveryError>),
#[error("missing UserInfo support")]
MissingUserInfoSupport,
#[error("missing token")]
MissingToken,
#[error("missing client metadata")]
MissingClientMetadata,
#[error(transparent)]
Token(#[from] InvalidBearerToken),
#[error(transparent)]
IntoHttp(#[from] http::Error),
#[error("missing response content-type")]
MissingResponseContentType,
#[error("could not decoded response content-type: {0}")]
DecodeResponseContentType(#[from] ToStrError),
#[error("invalid response content-type")]
InvalidResponseContentTypeValue,
#[error("unexpected response content-type {got:?}, expected {expected:?}")]
UnexpectedResponseContentType {
expected: String,
got: String,
},
#[error(transparent)]
FromUtf8(#[from] Utf8Error),
#[error(transparent)]
Json(#[from] serde_json::Error),
#[error(transparent)]
IdToken(#[from] IdTokenError),
#[error(transparent)]
Http(#[from] HttpError),
#[error(transparent)]
Service(BoxError),
}
impl<S> From<catch_http_codes::Error<S, Option<ErrorBody>>> for UserInfoError
where
S: Into<BoxError>,
{
fn from(err: catch_http_codes::Error<S, Option<ErrorBody>>) -> Self {
match err {
catch_http_codes::Error::HttpError { status_code, inner } => {
HttpError::new(status_code, inner).into()
}
catch_http_codes::Error::Service { inner } => Self::Service(inner.into()),
}
}
}
#[derive(Debug, Error)]
pub enum IntrospectionError {
#[error(transparent)]
IntoHttp(#[from] http::Error),
#[error(transparent)]
Credentials(#[from] CredentialsError),
#[error(transparent)]
Token(#[from] InvalidBearerToken),
#[error(transparent)]
UrlEncoded(#[from] serde_urlencoded::ser::Error),
#[error(transparent)]
Json(#[from] serde_json::Error),
#[error(transparent)]
Http(#[from] HttpError),
#[error(transparent)]
Service(BoxError),
}
impl<S> From<form_urlencoded_request::Error<S>> for IntrospectionError
where
S: Into<IntrospectionError>,
{
fn from(err: form_urlencoded_request::Error<S>) -> Self {
match err {
form_urlencoded_request::Error::Serialize { inner } => inner.into(),
form_urlencoded_request::Error::Service { inner } => inner.into(),
}
}
}
impl<S> From<json_response::Error<S>> for IntrospectionError
where
S: Into<IntrospectionError>,
{
fn from(err: json_response::Error<S>) -> Self {
match err {
json_response::Error::Deserialize { inner } => inner.into(),
json_response::Error::Service { inner } => inner.into(),
}
}
}
impl<S> From<catch_http_codes::Error<S, Option<ErrorBody>>> for IntrospectionError
where
S: Into<BoxError>,
{
fn from(err: catch_http_codes::Error<S, Option<ErrorBody>>) -> Self {
match err {
catch_http_codes::Error::HttpError { status_code, inner } => {
HttpError::new(status_code, inner).into()
}
catch_http_codes::Error::Service { inner } => Self::Service(inner.into()),
}
}
}
#[derive(Debug, Error)]
pub enum JwksError {
#[error(transparent)]
IntoHttp(#[from] http::Error),
#[error(transparent)]
Json(#[from] serde_json::Error),
#[error(transparent)]
Service(BoxError),
}
impl<S> From<json_response::Error<S>> for JwksError
where
S: Into<BoxError>,
{
fn from(err: json_response::Error<S>) -> Self {
match err {
json_response::Error::Service { inner } => Self::Service(inner.into()),
json_response::Error::Deserialize { inner } => Self::Json(inner),
}
}
}
#[derive(Debug, Error)]
pub enum JwtVerificationError {
#[error(transparent)]
JwtDecode(#[from] JwtDecodeError),
#[error(transparent)]
JwtSignature(#[from] NoKeyWorked),
#[error(transparent)]
Claim(#[from] ClaimError),
#[error("wrong signature alg")]
WrongSignatureAlg,
}
#[derive(Debug, Error)]
pub enum IdTokenError {
#[error("ID token is missing")]
MissingIdToken,
#[error("Authorization ID token is missing")]
MissingAuthIdToken,
#[error(transparent)]
Jwt(#[from] JwtVerificationError),
#[error(transparent)]
Claim(#[from] ClaimError),
#[error("wrong subject identifier")]
WrongSubjectIdentifier,
#[error("wrong authentication time")]
WrongAuthTime,
}
#[derive(Debug, Clone, Error)]
#[error("{status}: {body:?}")]
pub struct HttpError {
pub status: StatusCode,
pub body: Option<ErrorBody>,
}
impl HttpError {
#[must_use]
pub fn new(status: StatusCode, body: Option<ErrorBody>) -> Self {
Self { status, body }
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ErrorBody {
pub error: ClientErrorCode,
pub error_description: Option<String>,
}
#[derive(Debug, Error)]
pub enum CredentialsError {
#[error("unsupported authentication method")]
UnsupportedMethod,
#[error("no private key was found for the given algorithm")]
NoPrivateKeyFound,
#[error("invalid algorithm: {0}")]
InvalidSigningAlgorithm(#[from] InvalidAlgorithm),
#[error(transparent)]
JwtClaims(#[from] ClaimError),
#[error("Wrong algorithm for key")]
JwtWrongAlgorithm,
#[error(transparent)]
JwtSignature(#[from] JwtSignatureError),
#[error(transparent)]
Custom(BoxError),
}
#[derive(Debug, Error)]
pub enum AccountManagementError {
#[error(transparent)]
UrlEncoded(#[from] serde_urlencoded::ser::Error),
}