systemprompt_api/routes/oauth/error/
conversions.rs1use systemprompt_config::SecretsBootstrapError;
8use systemprompt_identifiers::error::IdValidationError;
9use systemprompt_models::errors::GlobalConfigError;
10use systemprompt_oauth::{OauthError, OauthErrorKind};
11use systemprompt_oauth_issuance::IssuanceError;
12use systemprompt_traits::auth::AuthProviderError;
13
14use super::OAuthHttpError;
15use crate::routes::oauth::internal;
16
17impl From<GlobalConfigError> for OAuthHttpError {
18 fn from(err: GlobalConfigError) -> Self {
19 Self::server_error("Configuration unavailable").with_source(err)
20 }
21}
22
23impl From<IdValidationError> for OAuthHttpError {
24 fn from(err: IdValidationError) -> Self {
25 Self::invalid_request(err.to_string()).with_source(err)
26 }
27}
28
29impl From<OauthError> for OAuthHttpError {
30 fn from(err: OauthError) -> Self {
31 match err.kind() {
32 OauthErrorKind::InvalidClient => Self::invalid_client("Client authentication failed"),
33 OauthErrorKind::InvalidClientMetadata => Self::invalid_client_metadata(err.to_string()),
34 OauthErrorKind::InvalidGrant => Self::invalid_grant(err.to_string()),
35 OauthErrorKind::InvalidToken => Self::invalid_token(err.to_string()),
36 OauthErrorKind::InvalidRequest => Self::invalid_request(err.to_string()),
37 OauthErrorKind::AccessDenied => Self::access_denied(err.to_string()),
38 OauthErrorKind::UsernameUnavailable => Self::username_unavailable(
39 "Username is already taken. Please choose a different username.",
40 ),
41 OauthErrorKind::EmailExists => {
42 Self::email_exists("An account with this email already exists.")
43 },
44 OauthErrorKind::NotFound => Self::not_found(err.to_string()),
45 OauthErrorKind::ExpiredChallenge => Self::expired_challenge(
46 "The challenge has expired. Please start the ceremony again.",
47 ),
48 OauthErrorKind::InvalidCredential => Self::invalid_credential(
49 "WebAuthn verification failed. Please ensure your authenticator and browser are \
50 compatible.",
51 ),
52 OauthErrorKind::AuthenticationFailed => Self::authentication_failed(
53 "Authentication failed. Check the email address or register a passkey.",
54 ),
55 OauthErrorKind::ServerError => {
56 Self::server_error("Authorization operation failed").with_source(err)
57 },
58 }
59 }
60}
61
62impl From<AuthProviderError> for OAuthHttpError {
63 fn from(err: AuthProviderError) -> Self {
64 match err {
65 e @ (AuthProviderError::InvalidCredentials | AuthProviderError::InvalidToken) => {
66 Self::invalid_client(e.to_string())
67 },
68 e @ AuthProviderError::UserNotFound => Self::not_found(e.to_string()),
69 e @ AuthProviderError::TokenExpired => Self::invalid_grant(e.to_string()),
70 e @ (AuthProviderError::InsufficientPermissions
71 | AuthProviderError::ServiceAccountInteractiveLogin) => {
72 Self::access_denied(e.to_string())
73 },
74 other => Self::server_error("Authentication provider failed").with_source(other),
75 }
76 }
77}
78
79impl From<SecretsBootstrapError> for OAuthHttpError {
80 fn from(err: SecretsBootstrapError) -> Self {
81 Self::server_error("Secrets unavailable").with_source(err)
82 }
83}
84
85impl From<sqlx::Error> for OAuthHttpError {
86 fn from(err: sqlx::Error) -> Self {
87 Self::server_error("Database operation failed").with_source(err)
88 }
89}
90
91impl From<anyhow::Error> for OAuthHttpError {
92 fn from(err: anyhow::Error) -> Self {
93 Self::server_error("Authorization operation failed").with_source(err)
94 }
95}
96
97impl From<IssuanceError> for OAuthHttpError {
98 fn from(error: IssuanceError) -> Self {
99 match error {
100 IssuanceError::InvalidRequest { field, message } => {
101 Self::invalid_request(format!("{field}: {message}"))
102 },
103 IssuanceError::MalformedField {
104 field,
105 reason,
106 source,
107 } => internal::rejected(Self::invalid_request(format!("{field}: {reason}")), source),
108 IssuanceError::UnsupportedGrantType { grant_type } => {
109 Self::unsupported_grant_type(format!("Grant type '{grant_type}' is not supported"))
110 },
111 IssuanceError::InvalidClient => Self::invalid_client("Client authentication failed"),
112 IssuanceError::InvalidGrant { reason } => Self::invalid_grant(reason),
113 IssuanceError::RejectedGrant { reason, source } => {
114 internal::rejected(Self::invalid_grant(reason), source)
115 },
116 IssuanceError::InvalidRefreshToken { reason } => {
117 Self::invalid_grant(format!("Refresh token invalid: {reason}"))
118 },
119 IssuanceError::InvalidCredentials => Self::invalid_grant("Invalid credentials"),
120 IssuanceError::InvalidClientSecret => Self::invalid_client("Invalid client secret"),
121 IssuanceError::ExpiredCode => Self::invalid_grant("Authorization code expired"),
122 IssuanceError::ServerError { context, source } => {
123 internal::server_error(context, source)
124 },
125 IssuanceError::InvalidTarget { message } => Self::invalid_target(message),
126 IssuanceError::InvalidScope { message } => Self::invalid_scope(message),
127 IssuanceError::IdJagRejected(error) => Self::invalid_grant(error.to_string()),
128 IssuanceError::BoundResource(error) => Self::invalid_target(error.to_string()),
129 IssuanceError::Oauth(error) => Self::from(error),
130 }
131 }
132}