headless_lms_server/domain/oauth/
errors.rs1use crate::domain::error::{ControllerError, ControllerErrorType, OAuthErrorCode, OAuthErrorData};
2use crate::prelude::BackendError;
3use dpop_verifier::DpopError;
4use thiserror::Error;
5
6#[derive(Debug, Error)]
8pub enum TokenGrantError {
9 #[error("Invalid grant: {0}")]
11 InvalidGrant(String),
12
13 #[error("Invalid client: {0}")]
15 InvalidClient(String),
16
17 #[error("Invalid scope: {0}")]
20 InvalidScope(String),
21
22 #[error("PKCE verification failed")]
24 PkceVerificationFailed,
25
26 #[error("Unsupported grant type")]
28 UnsupportedGrantType,
29
30 #[error("DPoP JKT mismatch")]
32 DpopMismatch,
33
34 #[error("Authorization pending")]
37 AuthorizationPending,
38
39 #[error("Slow down")]
41 SlowDown,
42
43 #[error("Device code expired")]
45 ExpiredToken,
46
47 #[error("Access denied")]
49 AccessDenied,
50
51 #[error("Invalid target: {0}")]
53 InvalidTarget(String),
54
55 #[error("Server error: {0}")]
57 ServerError(String),
58}
59
60impl From<TokenGrantError> for ControllerError {
61 fn from(err: TokenGrantError) -> Self {
62 let data = match &err {
63 TokenGrantError::InvalidGrant(msg) => {
64 OAuthErrorData::new(OAuthErrorCode::InvalidGrant, msg)
65 }
66 TokenGrantError::InvalidClient(msg) => {
67 OAuthErrorData::new(OAuthErrorCode::InvalidClient, msg)
68 }
69 TokenGrantError::InvalidScope(msg) => {
70 OAuthErrorData::new(OAuthErrorCode::InvalidScope, msg)
71 }
72 TokenGrantError::PkceVerificationFailed => {
73 OAuthErrorData::new(OAuthErrorCode::InvalidGrant, "PKCE verification failed")
74 }
75 TokenGrantError::UnsupportedGrantType => OAuthErrorData::new(
76 OAuthErrorCode::UnsupportedGrantType,
77 "unsupported grant type",
78 ),
79 TokenGrantError::DpopMismatch => {
80 OAuthErrorData::new(OAuthErrorCode::InvalidToken, "DPoP JKT mismatch")
81 }
82 TokenGrantError::AuthorizationPending => OAuthErrorData::new(
83 OAuthErrorCode::AuthorizationPending,
84 "authorization request is still pending",
85 ),
86 TokenGrantError::SlowDown => OAuthErrorData::new(
87 OAuthErrorCode::SlowDown,
88 "polling too frequently; slow down",
89 ),
90 TokenGrantError::ExpiredToken => {
91 OAuthErrorData::new(OAuthErrorCode::ExpiredToken, "device code has expired")
92 }
93 TokenGrantError::AccessDenied => OAuthErrorData::new(
94 OAuthErrorCode::AccessDenied,
95 "authorization request was denied",
96 ),
97 TokenGrantError::InvalidTarget(msg) => {
98 OAuthErrorData::new(OAuthErrorCode::InvalidTarget, msg)
99 }
100 TokenGrantError::ServerError(msg) => {
101 OAuthErrorData::new(OAuthErrorCode::ServerError, msg)
102 }
103 };
104
105 ControllerError::new(
106 ControllerErrorType::OAuthError(Box::new(data)),
107 err.to_string(),
108 Some(anyhow::anyhow!(err)),
109 )
110 }
111}
112
113impl From<DpopError> for TokenGrantError {
114 fn from(err: DpopError) -> Self {
115 match err {
116 DpopError::AthMismatch => TokenGrantError::DpopMismatch,
117 _ => TokenGrantError::ServerError(format!("DPoP error: {}", err)),
118 }
119 }
120}