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