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