Skip to main content

systemprompt_api/routes/oauth/error/
conversions.rs

1//! `From` impls mapping domain errors onto [`OAuthHttpError`], keeping the
2//! variant-to-RFC-code mapping in one place so handlers use `?`.
3
4use systemprompt_config::SecretsBootstrapError;
5use systemprompt_models::errors::ConfigError;
6use systemprompt_oauth::OauthError;
7use systemprompt_traits::auth::AuthProviderError;
8
9use super::{OAuthErrorCode, OAuthHttpError};
10
11impl From<ConfigError> for OAuthHttpError {
12    fn from(err: ConfigError) -> Self {
13        Self::server_error(err.to_string())
14    }
15}
16
17impl From<OauthError> for OAuthHttpError {
18    fn from(err: OauthError) -> Self {
19        match &err {
20            OauthError::InvalidClient(_)
21            | OauthError::ClientNotFound(_)
22            | OauthError::InvalidClientMetadata(_) => Self::invalid_client(err.to_string()),
23            OauthError::InvalidGrant(_)
24            | OauthError::CodeNotFound(_)
25            | OauthError::TokenNotFound(_)
26            | OauthError::PkceMismatch(_)
27            | OauthError::Expired(_) => Self::invalid_grant(err.to_string()),
28            OauthError::Validation(_) => Self::invalid_request(err.to_string()),
29            OauthError::Unauthorized(_) => Self::access_denied(err.to_string()),
30            OauthError::UsernameTaken(_) => Self::username_unavailable(
31                "Username is already taken. Please choose a different username.",
32            ),
33            OauthError::EmailRegistered(_) => {
34                Self::email_exists("An account with this email already exists.")
35            },
36            OauthError::UserNotFound(_) => Self::not_found(err.to_string()),
37            OauthError::RegistrationStateExpired => Self::expired_challenge(
38                "Registration challenge has expired. Please start the registration process again.",
39            ),
40            OauthError::WebAuthnVerificationFailed(_) => Self::invalid_credential(
41                "WebAuthn verification failed. Please ensure your authenticator and browser are \
42                 compatible.",
43            ),
44            OauthError::WebAuthn(_)
45            | OauthError::User(_)
46            | OauthError::Session(_)
47            | OauthError::TokenInvalid(_)
48            | OauthError::TokenAlgMismatch { .. }
49            | OauthError::TokenMissingKid
50            | OauthError::TokenUnknownKid { .. }
51            | OauthError::Provider(_)
52            | OauthError::Repository(_)
53            | OauthError::DatabaseRepository(_)
54            | OauthError::Config(_)
55            | OauthError::Crypto(_)
56            | OauthError::CimdFetch(_)
57            | OauthError::WebAuthnConfig(_)
58            | OauthError::Internal(_) => Self::server_error(err.to_string()),
59        }
60    }
61}
62
63impl From<AuthProviderError> for OAuthHttpError {
64    fn from(err: AuthProviderError) -> Self {
65        match &err {
66            AuthProviderError::InvalidCredentials | AuthProviderError::InvalidToken => {
67                Self::invalid_client(err.to_string())
68            },
69            AuthProviderError::UserNotFound => Self::not_found(err.to_string()),
70            AuthProviderError::TokenExpired => Self::invalid_grant(err.to_string()),
71            AuthProviderError::InsufficientPermissions => Self::access_denied(err.to_string()),
72            _ => Self::server_error(err.to_string()),
73        }
74    }
75}
76
77impl From<SecretsBootstrapError> for OAuthHttpError {
78    fn from(err: SecretsBootstrapError) -> Self {
79        Self::server_error(err.to_string())
80    }
81}
82
83impl From<sqlx::Error> for OAuthHttpError {
84    fn from(err: sqlx::Error) -> Self {
85        if let sqlx::Error::Database(db_err) = &err
86            && db_err.is_unique_violation()
87        {
88            return Self::new(OAuthErrorCode::UsernameUnavailable, err.to_string());
89        }
90        Self::server_error(err.to_string())
91    }
92}
93
94impl From<anyhow::Error> for OAuthHttpError {
95    fn from(err: anyhow::Error) -> Self {
96        Self::server_error(err.to_string())
97    }
98}