Skip to main content

systemprompt_api/routes/oauth/endpoints/token/
mod.rs

1pub mod generation;
2mod handler;
3pub mod validation;
4
5pub use handler::handle_token;
6
7use serde::{Deserialize, Serialize};
8
9pub type TokenResult<T> = Result<T, TokenError>;
10
11#[derive(Debug, Deserialize)]
12pub struct TokenRequest {
13    pub grant_type: String,
14    pub code: Option<String>,
15    pub redirect_uri: Option<String>,
16    pub client_id: Option<String>,
17    pub client_secret: Option<String>,
18    pub refresh_token: Option<String>,
19    pub scope: Option<String>,
20    pub code_verifier: Option<String>,
21    pub resource: Option<String>,
22}
23
24#[derive(Debug, Serialize)]
25
26pub struct TokenResponse {
27    pub access_token: String,
28    pub token_type: String,
29    pub expires_in: i64,
30    #[serde(skip_serializing_if = "Option::is_none")]
31    pub refresh_token: Option<String>,
32    #[serde(skip_serializing_if = "Option::is_none")]
33    pub scope: Option<String>,
34}
35
36#[derive(Debug, thiserror::Error)]
37pub enum TokenError {
38    #[error("Invalid request: {field} {message}")]
39    InvalidRequest { field: String, message: String },
40
41    #[error("Unsupported grant type: {grant_type}")]
42    UnsupportedGrantType { grant_type: String },
43
44    #[error("Invalid client credentials")]
45    InvalidClient,
46
47    #[error("Invalid authorization code: {reason}")]
48    InvalidGrant { reason: String },
49
50    #[error("Invalid refresh token: {reason}")]
51    InvalidRefreshToken { reason: String },
52
53    #[error("Invalid credentials")]
54    InvalidCredentials,
55
56    #[error("Invalid client secret")]
57    InvalidClientSecret,
58
59    #[error("Authorization code expired")]
60    ExpiredCode,
61
62    #[error("Server error: {message}")]
63    ServerError { message: String },
64}
65
66#[derive(Debug, Serialize)]
67
68pub struct TokenErrorResponse {
69    pub error: String,
70    #[serde(skip_serializing_if = "Option::is_none")]
71    pub error_description: Option<String>,
72}
73
74impl From<TokenError> for TokenErrorResponse {
75    fn from(error: TokenError) -> Self {
76        let (error_type, description) = match &error {
77            TokenError::InvalidRequest { field, message } => {
78                ("invalid_request", Some(format!("{field}: {message}")))
79            },
80            TokenError::UnsupportedGrantType { grant_type } => (
81                "unsupported_grant_type",
82                Some(format!("Grant type '{grant_type}' is not supported")),
83            ),
84            TokenError::InvalidClient => (
85                "invalid_client",
86                Some("Client authentication failed".to_string()),
87            ),
88            TokenError::InvalidGrant { reason } => ("invalid_grant", Some(reason.clone())),
89            TokenError::InvalidRefreshToken { reason } => (
90                "invalid_grant",
91                Some(format!("Refresh token invalid: {reason}")),
92            ),
93            TokenError::InvalidCredentials => {
94                ("invalid_grant", Some("Invalid credentials".to_string()))
95            },
96            TokenError::InvalidClientSecret => {
97                ("invalid_client", Some("Invalid client secret".to_string()))
98            },
99            TokenError::ExpiredCode => (
100                "invalid_grant",
101                Some("Authorization code expired".to_string()),
102            ),
103            TokenError::ServerError { message } => ("server_error", Some(message.clone())),
104        };
105
106        Self {
107            error: error_type.to_string(),
108            error_description: description,
109        }
110    }
111}