1use serde::{Deserialize, Deserializer, Serialize, Serializer};
10
11use crate::enums::OAuthScope;
12use crate::errors::error_code;
13use crate::ids::{AgentId, OAuthClientId};
14use crate::secrets::Secret;
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
19#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
20#[serde(rename_all = "snake_case")]
21pub enum OAuthErrorCode {
22 InvalidRequest,
23 InvalidClient,
24 InvalidGrant,
25 UnsupportedGrantType,
26 ServerError,
27 InvalidClientMetadata,
29 InvalidRedirectUri,
31}
32
33#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
40#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
41pub struct OAuthError {
42 pub error: OAuthErrorCode,
43 #[serde(default, skip_serializing_if = "Option::is_none")]
44 pub error_description: Option<String>,
45}
46
47impl OAuthError {
48 pub fn new(error: OAuthErrorCode, description: impl Into<String>) -> Self {
49 Self {
50 error,
51 error_description: Some(description.into()),
52 }
53 }
54
55 pub fn code(error: OAuthErrorCode) -> Self {
57 Self {
58 error,
59 error_description: None,
60 }
61 }
62}
63
64error_code!(
65 Bearer = "Bearer"
67);
68
69#[derive(Debug, Clone, Serialize, Deserialize)]
73#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
74pub struct TokenResponse {
75 #[serde(serialize_with = "expose", deserialize_with = "secret")]
76 #[cfg_attr(feature = "schemars", schemars(with = "String"))]
77 pub access_token: Secret,
78 pub token_type: Bearer,
79 pub expires_in: u64,
81 #[serde(serialize_with = "expose", deserialize_with = "secret")]
82 #[cfg_attr(feature = "schemars", schemars(with = "String"))]
83 pub refresh_token: Secret,
84 #[serde(serialize_with = "join_scopes", deserialize_with = "split_scopes")]
86 #[cfg_attr(feature = "schemars", schemars(with = "String"))]
87 pub scope: Vec<OAuthScope>,
88}
89
90fn expose<S: Serializer>(secret: &Secret, s: S) -> Result<S::Ok, S::Error> {
91 s.serialize_str(secret.expose())
92}
93
94fn secret<'de, D: Deserializer<'de>>(d: D) -> Result<Secret, D::Error> {
95 String::deserialize(d).map(Secret::new)
96}
97
98fn join_scopes<S: Serializer>(
99 scopes: &[OAuthScope],
100 s: S,
101) -> Result<S::Ok, S::Error> {
102 let words: Vec<String> = scopes.iter().map(ToString::to_string).collect();
103 s.serialize_str(&words.join(" "))
104}
105
106fn split_scopes<'de, D: Deserializer<'de>>(
107 d: D,
108) -> Result<Vec<OAuthScope>, D::Error> {
109 String::deserialize(d)?
110 .split_whitespace()
111 .map(|w| w.parse().map_err(serde::de::Error::custom))
112 .collect()
113}
114
115#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
117#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
118pub struct OAuthServerMetadata {
119 pub issuer: String,
124 pub authorization_endpoint: String,
125 pub token_endpoint: String,
126 pub registration_endpoint: String,
127 pub response_types_supported: Vec<String>,
128 pub grant_types_supported: Vec<String>,
129 pub code_challenge_methods_supported: Vec<String>,
130 pub token_endpoint_auth_methods_supported: Vec<String>,
131 pub scopes_supported: Vec<String>,
132}
133
134#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
141#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
142pub struct ClientRegistrationRequest {
143 pub redirect_uris: Vec<String>,
144 #[serde(default)]
145 pub client_name: Option<String>,
146 #[serde(default = "default_auth_method")]
150 pub token_endpoint_auth_method: String,
151}
152
153fn default_auth_method() -> String {
154 "none".to_string()
155}
156
157#[derive(Debug, Clone, Serialize, Deserialize)]
159#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
160pub struct ClientRegistrationResponse {
161 pub client_id: OAuthClientId,
162 pub client_id_issued_at: i64,
164 pub redirect_uris: Vec<String>,
165 pub client_name: String,
166 pub token_endpoint_auth_method: String,
168 pub grant_types: Vec<String>,
169 pub response_types: Vec<String>,
170 #[serde(
173 default,
174 skip_serializing_if = "Option::is_none",
175 serialize_with = "expose_opt",
176 deserialize_with = "secret_opt"
177 )]
178 #[cfg_attr(feature = "schemars", schemars(with = "Option<String>"))]
179 pub client_secret: Option<Secret>,
180 #[serde(default, skip_serializing_if = "Option::is_none")]
183 pub client_secret_expires_at: Option<i64>,
184}
185
186#[derive(Debug, Clone, Deserialize)]
195#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
196pub struct TokenRequest {
197 pub grant_type: String,
198 #[serde(default, deserialize_with = "secret_opt")]
200 #[cfg_attr(feature = "schemars", schemars(with = "Option<String>"))]
201 pub code: Option<Secret>,
202 #[serde(default)]
203 pub redirect_uri: Option<String>,
204 #[serde(default, deserialize_with = "secret_opt")]
206 #[cfg_attr(feature = "schemars", schemars(with = "Option<String>"))]
207 pub client_secret: Option<Secret>,
208 #[serde(default, deserialize_with = "secret_opt")]
210 #[cfg_attr(feature = "schemars", schemars(with = "Option<String>"))]
211 pub code_verifier: Option<Secret>,
212 #[serde(default, deserialize_with = "secret_opt")]
214 #[cfg_attr(feature = "schemars", schemars(with = "Option<String>"))]
215 pub refresh_token: Option<Secret>,
216 #[serde(default)]
217 pub client_id: Option<OAuthClientId>,
218}
219
220#[derive(Debug, Clone, Deserialize)]
224#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
225#[serde(deny_unknown_fields)]
226pub struct ApproveRequest {
227 pub client_id: OAuthClientId,
228 pub redirect_uri: String,
229 pub operator_email: String,
230 #[serde(deserialize_with = "secret")]
231 #[cfg_attr(feature = "schemars", schemars(with = "String"))]
232 pub operator_password: Secret,
233 pub agent_id: AgentId,
234 pub scope: Option<String>,
235 pub state: Option<String>,
236 pub code_challenge: String,
237 pub code_challenge_method: String,
238}
239
240#[derive(Debug, Clone, Deserialize)]
244#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
245#[serde(deny_unknown_fields)]
246pub struct RevokeTokenRequest {
247 pub operator_email: String,
248 #[serde(deserialize_with = "secret")]
249 #[cfg_attr(feature = "schemars", schemars(with = "String"))]
250 pub operator_password: Secret,
251 #[serde(deserialize_with = "secret")]
253 #[cfg_attr(feature = "schemars", schemars(with = "String"))]
254 pub token: Secret,
255}
256
257fn expose_opt<S: Serializer>(
258 secret: &Option<Secret>,
259 s: S,
260) -> Result<S::Ok, S::Error> {
261 match secret {
262 Some(secret) => s.serialize_some(secret.expose()),
263 None => s.serialize_none(),
264 }
265}
266
267fn secret_opt<'de, D: Deserializer<'de>>(
268 d: D,
269) -> Result<Option<Secret>, D::Error> {
270 Ok(Option::<String>::deserialize(d)?.map(Secret::new))
271}
272
273#[cfg(test)]
274mod tests;