1use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct ProtectedResourceMetadata {
16 pub resource: String,
18
19 pub authorization_servers: Vec<String>,
21
22 #[serde(skip_serializing_if = "Option::is_none")]
24 pub bearer_methods_supported: Option<Vec<String>>,
25
26 #[serde(skip_serializing_if = "Option::is_none")]
28 pub scopes_supported: Option<Vec<String>>,
29
30 #[serde(flatten)]
32 pub additional: HashMap<String, serde_json::Value>,
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize)]
41pub struct AuthorizationServerMetadata {
42 pub issuer: String,
44
45 pub authorization_endpoint: String,
47
48 pub token_endpoint: String,
50
51 #[serde(skip_serializing_if = "Option::is_none")]
53 pub registration_endpoint: Option<String>,
54
55 #[serde(skip_serializing_if = "Option::is_none")]
57 pub scopes_supported: Option<Vec<String>>,
58
59 pub response_types_supported: Vec<String>,
61
62 #[serde(skip_serializing_if = "Option::is_none")]
64 pub response_modes_supported: Option<Vec<String>>,
65
66 #[serde(skip_serializing_if = "Option::is_none")]
68 pub grant_types_supported: Option<Vec<String>>,
69
70 #[serde(skip_serializing_if = "Option::is_none")]
72 pub token_endpoint_auth_methods_supported: Option<Vec<String>>,
73
74 #[serde(skip_serializing_if = "Option::is_none")]
76 pub code_challenge_methods_supported: Option<Vec<String>>,
77
78 #[serde(skip_serializing_if = "Option::is_none")]
80 pub revocation_endpoint: Option<String>,
81
82 #[serde(skip_serializing_if = "Option::is_none")]
84 pub introspection_endpoint: Option<String>,
85
86 #[serde(flatten)]
88 pub additional: HashMap<String, serde_json::Value>,
89}
90
91#[derive(Debug, Clone, Serialize, Deserialize)]
97pub struct OpenIDProviderMetadata {
98 pub issuer: String,
100
101 pub authorization_endpoint: String,
103
104 pub token_endpoint: String,
106
107 #[serde(skip_serializing_if = "Option::is_none")]
109 pub userinfo_endpoint: Option<String>,
110
111 #[serde(skip_serializing_if = "Option::is_none")]
113 pub jwks_uri: Option<String>,
114
115 #[serde(skip_serializing_if = "Option::is_none")]
117 pub registration_endpoint: Option<String>,
118
119 #[serde(skip_serializing_if = "Option::is_none")]
121 pub scopes_supported: Option<Vec<String>>,
122
123 pub response_types_supported: Vec<String>,
125
126 #[serde(skip_serializing_if = "Option::is_none")]
128 pub code_challenge_methods_supported: Option<Vec<String>>,
129
130 #[serde(flatten)]
132 pub additional: HashMap<String, serde_json::Value>,
133}
134
135#[derive(Debug, Clone, Serialize, Deserialize)]
141pub struct ClientRegistrationRequest {
142 pub redirect_uris: Vec<String>,
144
145 #[serde(skip_serializing_if = "Option::is_none")]
147 pub client_name: Option<String>,
148
149 #[serde(skip_serializing_if = "Option::is_none")]
151 pub client_uri: Option<String>,
152
153 #[serde(skip_serializing_if = "Option::is_none")]
155 pub logo_uri: Option<String>,
156
157 #[serde(skip_serializing_if = "Option::is_none")]
159 pub grant_types: Option<Vec<String>>,
160
161 #[serde(skip_serializing_if = "Option::is_none")]
163 pub response_types: Option<Vec<String>>,
164
165 #[serde(skip_serializing_if = "Option::is_none")]
167 pub token_endpoint_auth_method: Option<String>,
168
169 #[serde(skip_serializing_if = "Option::is_none")]
171 pub scope: Option<String>,
172
173 #[serde(skip_serializing_if = "Option::is_none")]
175 pub software_id: Option<String>,
176
177 #[serde(skip_serializing_if = "Option::is_none")]
179 pub software_version: Option<String>,
180}
181
182#[derive(Debug, Clone, Serialize, Deserialize)]
184pub struct ClientRegistrationResponse {
185 pub client_id: String,
187
188 #[serde(skip_serializing_if = "Option::is_none")]
190 pub client_secret: Option<String>,
191
192 #[serde(skip_serializing_if = "Option::is_none")]
194 pub client_secret_expires_at: Option<u64>,
195
196 #[serde(skip_serializing_if = "Option::is_none")]
198 pub registration_access_token: Option<String>,
199
200 #[serde(skip_serializing_if = "Option::is_none")]
202 pub registration_client_uri: Option<String>,
203
204 pub redirect_uris: Vec<String>,
206
207 #[serde(flatten)]
209 pub additional: HashMap<String, serde_json::Value>,
210}
211
212#[derive(Debug, Clone, Serialize, Deserialize)]
218pub struct TokenRequest {
219 pub grant_type: String,
221
222 #[serde(skip_serializing_if = "Option::is_none")]
224 pub code: Option<String>,
225
226 #[serde(skip_serializing_if = "Option::is_none")]
228 pub redirect_uri: Option<String>,
229
230 #[serde(skip_serializing_if = "Option::is_none")]
232 pub code_verifier: Option<String>,
233
234 #[serde(skip_serializing_if = "Option::is_none")]
236 pub refresh_token: Option<String>,
237
238 #[serde(skip_serializing_if = "Option::is_none")]
240 pub resource: Option<String>,
241
242 #[serde(skip_serializing_if = "Option::is_none")]
244 pub client_id: Option<String>,
245
246 #[serde(skip_serializing_if = "Option::is_none")]
248 pub client_secret: Option<String>,
249
250 #[serde(skip_serializing_if = "Option::is_none")]
252 pub scope: Option<String>,
253}
254
255#[derive(Debug, Clone, Serialize, Deserialize)]
257pub struct TokenResponse {
258 pub access_token: String,
260
261 pub token_type: String,
263
264 #[serde(skip_serializing_if = "Option::is_none")]
266 pub expires_in: Option<u64>,
267
268 #[serde(skip_serializing_if = "Option::is_none")]
270 pub refresh_token: Option<String>,
271
272 #[serde(skip_serializing_if = "Option::is_none")]
274 pub scope: Option<String>,
275
276 #[serde(flatten)]
278 pub additional: HashMap<String, serde_json::Value>,
279}
280
281#[derive(Debug, Clone, Serialize, Deserialize)]
283pub struct OAuth2Error {
284 pub error: String,
286
287 #[serde(skip_serializing_if = "Option::is_none")]
289 pub error_description: Option<String>,
290
291 #[serde(skip_serializing_if = "Option::is_none")]
293 pub error_uri: Option<String>,
294}
295
296#[derive(Debug, Clone)]
302pub struct AuthChallenge {
303 pub scheme: String,
305
306 pub realm: Option<String>,
308
309 pub error: Option<String>,
311
312 pub error_description: Option<String>,
314
315 pub resource_metadata: Option<String>,
317
318 pub additional: HashMap<String, String>,
320}
321
322impl AuthChallenge {
323 pub fn parse(header_value: &str) -> Option<Self> {
325 let parts: Vec<&str> = header_value.splitn(2, ' ').collect();
326 if parts.is_empty() {
327 return None;
328 }
329
330 let scheme = parts[0].to_string();
331 let mut challenge = AuthChallenge {
332 scheme,
333 realm: None,
334 error: None,
335 error_description: None,
336 resource_metadata: None,
337 additional: HashMap::new(),
338 };
339
340 if parts.len() > 1 {
341 let params = parts[1];
343 for param in params.split(',') {
344 let param = param.trim();
345 if let Some(eq_pos) = param.find('=') {
346 let key = param[..eq_pos].trim();
347 let value = param[eq_pos + 1..].trim().trim_matches('"');
348
349 match key {
350 "realm" => challenge.realm = Some(value.to_string()),
351 "error" => challenge.error = Some(value.to_string()),
352 "error_description" => {
353 challenge.error_description = Some(value.to_string())
354 }
355 "resource_metadata" => {
356 challenge.resource_metadata = Some(value.to_string())
357 }
358 _ => {
359 challenge
360 .additional
361 .insert(key.to_string(), value.to_string());
362 }
363 }
364 }
365 }
366 }
367
368 Some(challenge)
369 }
370
371 pub fn format(&self) -> String {
373 let mut result = self.scheme.clone();
374 let mut params = Vec::new();
375
376 if let Some(realm) = &self.realm {
377 params.push(format!(r#"realm="{realm}""#));
378 }
379 if let Some(error) = &self.error {
380 params.push(format!(r#"error="{error}""#));
381 }
382 if let Some(desc) = &self.error_description {
383 params.push(format!(r#"error_description="{desc}""#));
384 }
385 if let Some(metadata) = &self.resource_metadata {
386 params.push(format!(r#"resource_metadata="{metadata}""#));
387 }
388
389 for (key, value) in &self.additional {
390 params.push(format!(r#"{key}="{value}""#));
391 }
392
393 if !params.is_empty() {
394 result.push(' ');
395 result.push_str(¶ms.join(", "));
396 }
397
398 result
399 }
400}
401
402#[derive(Debug, Clone)]
408pub struct AuthorizationContext {
409 pub access_token: Option<String>,
411
412 pub refresh_token: Option<String>,
414
415 pub expires_at: Option<u64>,
417
418 pub auth_server_metadata: Option<AuthorizationServerMetadata>,
420
421 pub resource_metadata: Option<ProtectedResourceMetadata>,
423
424 pub client_registration: Option<ClientRegistrationResponse>,
426
427 pub pkce_verifier: Option<String>,
429
430 pub state: Option<String>,
432
433 pub resource: String,
435}
436
437impl AuthorizationContext {
438 pub fn new(resource: String) -> Self {
440 Self {
441 access_token: None,
442 refresh_token: None,
443 expires_at: None,
444 auth_server_metadata: None,
445 resource_metadata: None,
446 client_registration: None,
447 pkce_verifier: None,
448 state: None,
449 resource,
450 }
451 }
452
453 pub fn is_token_expired(&self) -> bool {
455 if let Some(expires_at) = self.expires_at {
456 let now = std::time::SystemTime::now()
457 .duration_since(std::time::UNIX_EPOCH)
458 .unwrap()
459 .as_secs();
460 now >= expires_at
461 } else {
462 false
463 }
464 }
465
466 pub fn has_valid_token(&self) -> bool {
468 self.access_token.is_some() && !self.is_token_expired()
469 }
470}