crab-vault-auth 0.2.14

The auth sdk for crab vault.
Documentation
use std::{string::FromUtf8Error, sync::Arc};

use axum::{
    http::StatusCode,
    response::{IntoResponse, Response},
};
use base64::DecodeError;
use jsonwebtoken::Algorithm;
use serde::Serialize;
use thiserror::Error;

#[derive(Debug, Serialize, Clone, Error)]
#[serde(rename_all = "camelCase", tag = "code")]
pub enum AuthError {
    #[error("missing authorization header")]
    MissingAuthHeader,

    #[error("key of algorithm `{0:?}` is undefined")]
    InvalidAlgorithm(Algorithm),

    #[error("invalid authorization format: expected 'Bearer <token>'")]
    InvalidAuthFormat,

    #[error("no key id fed")]
    InvalidKeyId,

    #[error("Some of the text was invalid UTF-8: {0}")]
    InvalidUtf8(
        #[from]
        #[serde(skip)]
        FromUtf8Error,
    ),

    #[error("An error happened while serializing/deserializing JSON: {0}")]
    InvalidJson(
        #[from]
        #[serde(skip)]
        Arc<serde_json::Error>,
    ),

    #[error("An error happened when decoding some base64 text: {0}")]
    InvalidBase64(
        #[from]
        #[serde(skip)]
        DecodeError,
    ),

    #[error("token is invalid")]
    InvalidToken,

    #[error("token has expired")]
    TokenExpired,

    #[error("token is not yet valid")]
    TokenNotYetValid,

    #[error("invalid signature")]
    InvalidSignature,

    #[error("untrusted issuer")]
    InvalidIssuer,

    #[error("invalid audience")]
    InvalidAudience,

    #[error("invalid subject")]
    InvalidSubject,

    #[error("required claim `{0}` missing")]
    MissingClaim(String),

    #[error("insufficient permissions for this operation")]
    InsufficientPermissions,

    #[error("token has been revoked")]
    TokenRevoked,

    #[error("internal server error during authentication, details: {0}")]
    InternalError(#[serde(skip)] String),
}

impl From<jsonwebtoken::errors::Error> for AuthError {
    fn from(value: jsonwebtoken::errors::Error) -> Self {
        use jsonwebtoken::errors::ErrorKind::*;

        match value.into_kind() {
            ExpiredSignature => AuthError::TokenExpired,
            InvalidSignature => AuthError::InvalidSignature,
            InvalidIssuer => AuthError::InvalidIssuer,
            InvalidAudience => AuthError::InvalidAudience,
            InvalidSubject => AuthError::InvalidSubject,
            MissingRequiredClaim(claim) => AuthError::MissingClaim(claim),
            ImmatureSignature => AuthError::TokenNotYetValid,
            InvalidToken => AuthError::InvalidToken,
            Base64(e) => AuthError::InvalidBase64(e),
            Utf8(e) => AuthError::InvalidUtf8(e),
            Json(e) => AuthError::InvalidJson(e),

            InvalidEcdsaKey => AuthError::InternalError("the secret given is not a valid ECDSA key".into()),
            InvalidRsaKey(_) => AuthError::InternalError("the secret given is not a valid RSA key".into()),
            RsaFailedSigning => AuthError::InternalError("could not sign with the given key".into()),
            InvalidAlgorithmName => AuthError::InternalError("cannot parse algorithm from str".into()),
            InvalidKeyFormat => AuthError::InternalError("a key is provided with an invalid format".into()),
            InvalidAlgorithm => AuthError::InternalError("the algorithm in the header doesn't match the one passed to decode or the encoding/decoding key used doesn't match the alg requested".to_string()),
            MissingAlgorithm => AuthError::InternalError("the Validation struct does not contain at least 1 algorithm".into()),
            Crypto(e) => AuthError::InternalError(format!("Something unspecified went wrong with crypto: {e}")),
            _ => todo!()
        }
    }
}

impl IntoResponse for AuthError {
    fn into_response(self) -> Response {
        let status_code = match self {
            AuthError::MissingAuthHeader
            | AuthError::InvalidKeyId
            | AuthError::InvalidAuthFormat
            | AuthError::InvalidToken
            | AuthError::TokenExpired
            | AuthError::TokenNotYetValid
            | AuthError::InvalidAlgorithm(_)
            | AuthError::InvalidSignature
            | AuthError::InvalidIssuer
            | AuthError::InvalidAudience
            | AuthError::InvalidSubject
            | AuthError::MissingClaim(_)
            | AuthError::InvalidUtf8(_)
            | AuthError::InvalidJson(_)
            | AuthError::InvalidBase64(_)
            | AuthError::TokenRevoked => StatusCode::UNAUTHORIZED,

            AuthError::InsufficientPermissions => StatusCode::FORBIDDEN,

            AuthError::InternalError(_) => StatusCode::UNAUTHORIZED,
        };

        status_code.into_response()
    }
}

impl From<serde_json::Error> for AuthError {
    fn from(value: serde_json::Error) -> Self {
        Self::InvalidJson(Arc::new(value))
    }
}

impl From<AuthError> for Response {
    #[inline(always)]
    fn from(val: AuthError) -> Response {
        val.into_response()
    }
}