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
9use crate::routes::oauth::OAuthHttpError;
10
11pub type TokenResult<T> = Result<T, TokenError>;
12
13#[derive(Debug, Deserialize)]
14pub struct TokenRequest {
15    pub grant_type: String,
16    pub code: Option<String>,
17    pub redirect_uri: Option<String>,
18    pub client_id: Option<String>,
19    pub client_secret: Option<String>,
20    pub refresh_token: Option<String>,
21    pub scope: Option<String>,
22    pub code_verifier: Option<String>,
23    pub resource: Option<String>,
24    pub plugin_id: Option<String>,
25    pub audience: Option<String>,
26    pub subject_token: Option<String>,
27    pub subject_token_type: Option<String>,
28    pub actor_token: Option<String>,
29    pub actor_token_type: Option<String>,
30    pub requested_token_type: Option<String>,
31}
32
33#[derive(Debug, Serialize)]
34pub struct TokenResponse {
35    pub access_token: String,
36    pub token_type: String,
37    pub expires_in: i64,
38    #[serde(skip_serializing_if = "Option::is_none")]
39    pub refresh_token: Option<String>,
40    #[serde(skip_serializing_if = "Option::is_none")]
41    pub scope: Option<String>,
42    // RFC 8693 ยง2.2.1 issued_token_type. Only set by the
43    // urn:ietf:params:oauth:grant-type:token-exchange flow.
44    #[serde(skip_serializing_if = "Option::is_none")]
45    pub issued_token_type: Option<String>,
46}
47
48#[derive(Debug, thiserror::Error)]
49pub enum TokenError {
50    #[error("Invalid request: {field} {message}")]
51    InvalidRequest { field: String, message: String },
52
53    #[error("Unsupported grant type: {grant_type}")]
54    UnsupportedGrantType { grant_type: String },
55
56    #[error("Invalid client credentials")]
57    InvalidClient,
58
59    #[error("Invalid authorization code: {reason}")]
60    InvalidGrant { reason: String },
61
62    #[error("Invalid refresh token: {reason}")]
63    InvalidRefreshToken { reason: String },
64
65    #[error("Invalid credentials")]
66    InvalidCredentials,
67
68    #[error("Invalid client secret")]
69    InvalidClientSecret,
70
71    #[error("Authorization code expired")]
72    ExpiredCode,
73
74    #[error("Server error: {message}")]
75    ServerError { message: String },
76
77    #[error("Invalid target resource: {message}")]
78    InvalidTarget { message: String },
79}
80
81impl From<TokenError> for OAuthHttpError {
82    fn from(error: TokenError) -> Self {
83        match error {
84            TokenError::InvalidRequest { field, message } => {
85                Self::invalid_request(format!("{field}: {message}"))
86            },
87            TokenError::UnsupportedGrantType { grant_type } => {
88                Self::unsupported_grant_type(format!("Grant type '{grant_type}' is not supported"))
89            },
90            TokenError::InvalidClient => Self::invalid_client("Client authentication failed"),
91            TokenError::InvalidGrant { reason } => Self::invalid_grant(reason),
92            TokenError::InvalidRefreshToken { reason } => {
93                Self::invalid_grant(format!("Refresh token invalid: {reason}"))
94            },
95            TokenError::InvalidCredentials => Self::invalid_grant("Invalid credentials"),
96            TokenError::InvalidClientSecret => Self::invalid_client("Invalid client secret"),
97            TokenError::ExpiredCode => Self::invalid_grant("Authorization code expired"),
98            TokenError::ServerError { message } => Self::server_error(message),
99            TokenError::InvalidTarget { message } => Self::invalid_target(message),
100        }
101    }
102}