1use http::Method;
8use mime::Mime;
9use serde::{Deserialize, Serialize};
10use serde_json::{Map, Value};
11
12use crate::types::{
13 ClientId, ClientPassword, ClientSecret, Code, CodeVerifier, Scope, ScopeFromStrError,
14 ScopeParameter,
15};
16
17pub const METHOD: Method = Method::POST;
18pub const CONTENT_TYPE: Mime = mime::APPLICATION_WWW_FORM_URLENCODED;
19pub const GRANT_TYPE_WITH_AUTHORIZATION_CODE_GRANT: &str = "authorization_code";
20
21#[derive(Serialize, Deserialize, Debug, Clone)]
25#[serde(tag = "grant_type")]
26pub enum Body<SCOPE>
27where
28 SCOPE: Scope,
29{
30 #[serde(rename = "authorization_code")]
32 AuthorizationCodeGrant(BodyWithAuthorizationCodeGrant),
33 #[serde(rename = "urn:ietf:params:oauth:grant-type:device_code")]
35 DeviceAuthorizationGrant(BodyWithDeviceAuthorizationGrant),
36 #[serde(rename = "client_credentials")]
38 ClientCredentialsGrant(BodyWithClientCredentialsGrant<SCOPE>),
39 #[serde(rename = "password")]
40 ResourceOwnerPasswordCredentialsGrant(BodyWithResourceOwnerPasswordCredentialsGrant<SCOPE>),
41 #[serde(rename = "urn:ietf:params:oauth:grant-type:jwt-bearer")]
43 JwtAuthorizationGrant(BodyWithJwtAuthorizationGrant<SCOPE>),
44}
45
46#[derive(Serialize, Deserialize, Debug, Clone)]
48pub struct BodyWithAuthorizationCodeGrant {
49 pub code: Code,
50 #[serde(skip_serializing_if = "Option::is_none")]
51 pub redirect_uri: Option<String>,
52 #[serde(skip_serializing_if = "Option::is_none")]
53 pub client_id: Option<ClientId>,
54 #[serde(skip_serializing_if = "Option::is_none")]
56 pub client_secret: Option<ClientSecret>,
57
58 #[serde(skip_serializing_if = "Option::is_none")]
60 pub code_verifier: Option<CodeVerifier>,
61
62 #[serde(flatten, skip_serializing_if = "Option::is_none")]
63 _extra: Option<Map<String, Value>>,
64}
65
66impl BodyWithAuthorizationCodeGrant {
67 pub fn new(
68 code: Code,
69 redirect_uri: Option<String>,
70 client_id: Option<ClientId>,
71 client_secret: Option<ClientSecret>,
72 ) -> Self {
73 Self::internal_new(code, redirect_uri, client_id, client_secret, None)
74 }
75
76 fn internal_new(
77 code: Code,
78 redirect_uri: Option<String>,
79 client_id: Option<ClientId>,
80 client_secret: Option<ClientSecret>,
81 code_verifier: Option<CodeVerifier>,
82 ) -> Self {
83 Self {
84 code,
85 redirect_uri,
86 client_id,
87 client_secret,
88 code_verifier,
89 _extra: None,
90 }
91 }
92
93 pub fn set_extra(&mut self, extra: Map<String, Value>) {
94 self._extra = Some(extra);
95 }
96 pub fn extra(&self) -> Option<&Map<String, Value>> {
97 self._extra.as_ref()
98 }
99}
100
101#[derive(Serialize, Deserialize, Debug, Clone)]
103pub struct BodyWithDeviceAuthorizationGrant {
104 pub device_code: String,
105 #[serde(skip_serializing_if = "Option::is_none")]
106 pub client_id: Option<ClientId>,
107 #[serde(skip_serializing_if = "Option::is_none")]
109 pub client_secret: Option<ClientSecret>,
110
111 #[serde(flatten, skip_serializing_if = "Option::is_none")]
112 _extra: Option<Map<String, Value>>,
113}
114
115impl BodyWithDeviceAuthorizationGrant {
116 pub fn new(
117 device_code: String,
118 client_id: Option<ClientId>,
119 client_secret: Option<ClientSecret>,
120 ) -> Self {
121 Self {
122 device_code,
123 client_id,
124 client_secret,
125 _extra: None,
126 }
127 }
128
129 pub fn set_extra(&mut self, extra: Map<String, Value>) {
130 self._extra = Some(extra);
131 }
132 pub fn extra(&self) -> Option<&Map<String, Value>> {
133 self._extra.as_ref()
134 }
135}
136
137#[derive(Serialize, Deserialize, Debug, Clone)]
139pub struct BodyWithClientCredentialsGrant<SCOPE>
140where
141 SCOPE: Scope,
142{
143 #[serde(skip_serializing_if = "Option::is_none")]
144 pub scope: Option<ScopeParameter<SCOPE>>,
145 #[serde(flatten, skip_serializing_if = "Option::is_none")]
146 pub client_password: Option<ClientPassword>,
147
148 #[serde(flatten, skip_serializing_if = "Option::is_none")]
149 _extra: Option<Map<String, Value>>,
150}
151
152impl<SCOPE> BodyWithClientCredentialsGrant<SCOPE>
153where
154 SCOPE: Scope,
155{
156 pub fn new(scope: Option<ScopeParameter<SCOPE>>) -> Self {
157 Self {
158 scope,
159 client_password: None,
160 _extra: None,
161 }
162 }
163
164 pub fn new_with_client_password(
165 scope: Option<ScopeParameter<SCOPE>>,
166 client_password: ClientPassword,
167 ) -> Self {
168 Self {
169 scope,
170 client_password: Some(client_password),
171 _extra: None,
172 }
173 }
174
175 pub fn set_extra(&mut self, extra: Map<String, Value>) {
176 self._extra = Some(extra);
177 }
178 pub fn extra(&self) -> Option<&Map<String, Value>> {
179 self._extra.as_ref()
180 }
181
182 pub fn try_from_t_with_string(
183 body: &BodyWithClientCredentialsGrant<String>,
184 ) -> Result<Self, ScopeFromStrError> {
185 let scope = if let Some(x) = &body.scope {
186 Some(ScopeParameter::<SCOPE>::try_from_t_with_string(x)?)
187 } else {
188 None
189 };
190
191 let mut this = Self::new(scope);
192 this.client_password = body.client_password.to_owned();
193 if let Some(extra) = body.extra() {
194 this.set_extra(extra.to_owned());
195 }
196 Ok(this)
197 }
198}
199
200#[derive(Serialize, Deserialize, Debug, Clone)]
202pub struct BodyWithResourceOwnerPasswordCredentialsGrant<SCOPE>
203where
204 SCOPE: Scope,
205{
206 pub username: String,
207 pub password: String,
208
209 #[serde(skip_serializing_if = "Option::is_none")]
210 pub scope: Option<ScopeParameter<SCOPE>>,
211 #[serde(flatten, skip_serializing_if = "Option::is_none")]
212 pub client_password: Option<ClientPassword>,
213
214 #[serde(flatten, skip_serializing_if = "Option::is_none")]
215 _extra: Option<Map<String, Value>>,
216}
217
218impl<SCOPE> BodyWithResourceOwnerPasswordCredentialsGrant<SCOPE>
219where
220 SCOPE: Scope,
221{
222 pub fn new(
223 username: impl AsRef<str>,
224 password: impl AsRef<str>,
225 scope: Option<ScopeParameter<SCOPE>>,
226 ) -> Self {
227 Self {
228 username: username.as_ref().to_owned(),
229 password: password.as_ref().to_owned(),
230 scope,
231 client_password: None,
232 _extra: None,
233 }
234 }
235
236 pub fn new_with_client_password(
237 username: impl AsRef<str>,
238 password: impl AsRef<str>,
239 scope: Option<ScopeParameter<SCOPE>>,
240 client_password: ClientPassword,
241 ) -> Self {
242 Self {
243 username: username.as_ref().to_owned(),
244 password: password.as_ref().to_owned(),
245 scope,
246 client_password: Some(client_password),
247 _extra: None,
248 }
249 }
250
251 pub fn set_extra(&mut self, extra: Map<String, Value>) {
252 self._extra = Some(extra);
253 }
254 pub fn extra(&self) -> Option<&Map<String, Value>> {
255 self._extra.as_ref()
256 }
257
258 pub fn try_from_t_with_string(
259 body: &BodyWithResourceOwnerPasswordCredentialsGrant<String>,
260 ) -> Result<Self, ScopeFromStrError> {
261 let scope = if let Some(x) = &body.scope {
262 Some(ScopeParameter::<SCOPE>::try_from_t_with_string(x)?)
263 } else {
264 None
265 };
266
267 let mut this = Self::new(&body.username, &body.password, scope);
268 this.client_password = body.client_password.to_owned();
269 if let Some(extra) = body.extra() {
270 this.set_extra(extra.to_owned());
271 }
272 Ok(this)
273 }
274}
275
276#[derive(Serialize, Deserialize, Debug, Clone)]
278pub struct BodyWithJwtAuthorizationGrant<SCOPE>
279where
280 SCOPE: Scope,
281{
282 pub assertion: String,
283 #[serde(skip_serializing_if = "Option::is_none")]
284 pub scope: Option<ScopeParameter<SCOPE>>,
285 #[serde(skip_serializing_if = "Option::is_none")]
286 pub client_id: Option<ClientId>,
287
288 #[serde(flatten, skip_serializing_if = "Option::is_none")]
289 _extra: Option<Map<String, Value>>,
290}
291
292impl<SCOPE> BodyWithJwtAuthorizationGrant<SCOPE>
293where
294 SCOPE: Scope,
295{
296 pub fn new(
297 assertion: String,
298 scope: Option<ScopeParameter<SCOPE>>,
299 client_id: Option<ClientId>,
300 ) -> Self {
301 Self {
302 assertion,
303 scope,
304 client_id,
305 _extra: None,
306 }
307 }
308
309 pub fn set_extra(&mut self, extra: Map<String, Value>) {
310 self._extra = Some(extra);
311 }
312 pub fn extra(&self) -> Option<&Map<String, Value>> {
313 self._extra.as_ref()
314 }
315
316 pub fn try_from_t_with_string(
317 body: &BodyWithJwtAuthorizationGrant<String>,
318 ) -> Result<Self, ScopeFromStrError> {
319 let scope = if let Some(x) = &body.scope {
320 Some(ScopeParameter::<SCOPE>::try_from_t_with_string(x)?)
321 } else {
322 None
323 };
324
325 let mut this = Self::new(body.assertion.to_owned(), scope, body.client_id.to_owned());
326
327 if let Some(extra) = body.extra() {
328 this.set_extra(extra.to_owned());
329 }
330 Ok(this)
331 }
332}
333
334#[cfg(test)]
335mod tests_with_authorization_code_grant {
336 use super::*;
337
338 #[test]
339 fn test_ser_de() {
340 let body_str = "grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb";
341 match serde_urlencoded::from_str::<Body<String>>(body_str) {
342 Ok(Body::AuthorizationCodeGrant(body)) => {
343 assert_eq!(body.code, "SplxlOBeZQQYbYS6WxSbIA");
344 assert_eq!(
345 body.redirect_uri,
346 Some("https://client.example.com/cb".parse().unwrap())
347 );
348 }
349 #[allow(unreachable_patterns)]
350 Ok(body) => panic!("{body:?}"),
351 Err(err) => panic!("{err}"),
352 }
353 }
354}
355
356#[cfg(test)]
357mod tests_with_device_authorization_grant {
358 use super::*;
359
360 #[test]
361 fn test_ser_de() {
362 let body_str = "grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Adevice_code&device_code=GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS&client_id=1406020730";
363 match serde_urlencoded::from_str::<Body<String>>(body_str) {
364 Ok(Body::DeviceAuthorizationGrant(body)) => {
365 assert_eq!(
366 body.device_code,
367 "GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS"
368 );
369 assert_eq!(body.client_id, Some("1406020730".to_owned()));
370
371 assert_eq!(
372 body_str,
373 serde_urlencoded::to_string(Body::<String>::DeviceAuthorizationGrant(body))
374 .unwrap()
375 );
376 }
377 #[allow(unreachable_patterns)]
378 Ok(body) => panic!("{body:?}"),
379 Err(err) => panic!("{err}"),
380 }
381 }
382
383 #[test]
384 fn test_ser_de_extra() {
385 let mut extra = Map::new();
387 extra.insert("foo".to_owned(), Value::String("bar".to_owned()));
388 let mut body = BodyWithDeviceAuthorizationGrant::new(
389 "your_device_code".to_owned(),
390 Some("your_client_id".to_owned()),
391 Some("your_client_secret".to_owned()),
392 );
393 body.set_extra(extra.to_owned());
394 let body = Body::<String>::DeviceAuthorizationGrant(body);
395 let body_str = serde_urlencoded::to_string(body).unwrap();
396 assert_eq!(body_str, "grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Adevice_code&device_code=your_device_code&client_id=your_client_id&client_secret=your_client_secret&foo=bar");
397
398 match serde_urlencoded::from_str::<Body<String>>(body_str.as_str()) {
399 Ok(Body::DeviceAuthorizationGrant(body)) => {
400 assert_eq!(body.extra(), Some(&extra));
401 }
402 #[allow(unreachable_patterns)]
403 Ok(body) => panic!("{body:?}"),
404 Err(err) => panic!("{err}"),
405 }
406 }
407}
408
409#[cfg(test)]
410mod tests_with_client_credentials_grant {
411 use super::*;
412
413 #[test]
414 fn test_ser_de() {
415 let body_str = "grant_type=client_credentials";
416 match serde_urlencoded::from_str::<Body<String>>(body_str) {
417 Ok(Body::ClientCredentialsGrant(body)) => {
418 assert_eq!(body.client_password, None);
419 }
420 #[allow(unreachable_patterns)]
421 Ok(body) => panic!("{body:?}"),
422 Err(err) => panic!("{err}"),
423 }
424
425 let body_str =
426 "grant_type=client_credentials&client_id=CLIENT_ID&client_secret=CLIENT_SECRET";
427 match serde_urlencoded::from_str::<Body<String>>(body_str) {
428 Ok(Body::ClientCredentialsGrant(body)) => {
429 assert_eq!(body.client_password.unwrap().client_id, "CLIENT_ID");
430 }
431 #[allow(unreachable_patterns)]
432 Ok(body) => panic!("{body:?}"),
433 Err(err) => panic!("{err}"),
434 }
435
436 let body_str = "grant_type=client_credentials&client_id=CLIENT_ID";
437 match serde_urlencoded::from_str::<Body<String>>(body_str) {
438 Ok(Body::ClientCredentialsGrant(body)) => {
439 assert_eq!(body.client_password, None);
440 }
441 #[allow(unreachable_patterns)]
442 Ok(body) => panic!("{body:?}"),
443 Err(err) => panic!("{err}"),
444 }
445 }
446
447 #[test]
448 fn test_ser_de_extra() {
449 let body_str = "grant_type=client_credentials&foo=bar";
450 match serde_urlencoded::from_str::<Body<String>>(body_str) {
451 Ok(Body::ClientCredentialsGrant(body)) => {
452 assert_eq!(body.client_password, None);
453 assert_eq!(
454 body.extra().unwrap().get("foo").unwrap().as_str(),
455 Some("bar")
456 )
457 }
458 #[allow(unreachable_patterns)]
459 Ok(body) => panic!("{body:?}"),
460 Err(err) => panic!("{err}"),
461 }
462
463 let body_str =
464 "grant_type=client_credentials&client_id=CLIENT_ID&client_secret=CLIENT_SECRET&foo=bar";
465 match serde_urlencoded::from_str::<Body<String>>(body_str) {
466 Ok(Body::ClientCredentialsGrant(body)) => {
467 assert_eq!(
468 body.client_password.to_owned().unwrap().client_id,
469 "CLIENT_ID"
470 );
471 assert_eq!(
472 body.extra().unwrap().get("foo").unwrap().as_str(),
473 Some("bar")
474 )
475 }
476 #[allow(unreachable_patterns)]
477 Ok(body) => panic!("{body:?}"),
478 Err(err) => panic!("{err}"),
479 }
480 }
481}
482
483#[cfg(test)]
484mod tests_with_resource_owner_password_credentials_grant {
485 use super::*;
486
487 #[test]
488 fn test_ser_de() {
489 let body_str = "grant_type=password&username=USERNAME&password=PASSWORD";
490 match serde_urlencoded::from_str::<Body<String>>(body_str) {
491 Ok(Body::ResourceOwnerPasswordCredentialsGrant(body)) => {
492 assert_eq!(body.username, "USERNAME");
493 assert_eq!(body.password, "PASSWORD");
494 assert_eq!(body.client_password, None);
495 }
496 #[allow(unreachable_patterns)]
497 Ok(body) => panic!("{body:?}"),
498 Err(err) => panic!("{err}"),
499 }
500
501 let body_str =
502 "grant_type=password&username=USERNAME&password=PASSWORD&client_id=CLIENT_ID&client_secret=CLIENT_SECRET";
503 match serde_urlencoded::from_str::<Body<String>>(body_str) {
504 Ok(Body::ResourceOwnerPasswordCredentialsGrant(body)) => {
505 assert_eq!(body.username, "USERNAME");
506 assert_eq!(body.password, "PASSWORD");
507 assert_eq!(body.client_password.unwrap().client_id, "CLIENT_ID");
508 }
509 #[allow(unreachable_patterns)]
510 Ok(body) => panic!("{body:?}"),
511 Err(err) => panic!("{err}"),
512 }
513
514 let body_str =
515 "grant_type=password&username=USERNAME&password=PASSWORD&client_id=CLIENT_ID";
516 match serde_urlencoded::from_str::<Body<String>>(body_str) {
517 Ok(Body::ResourceOwnerPasswordCredentialsGrant(body)) => {
518 assert_eq!(body.username, "USERNAME");
519 assert_eq!(body.password, "PASSWORD");
520 assert_eq!(body.client_password, None);
521 }
522 #[allow(unreachable_patterns)]
523 Ok(body) => panic!("{body:?}"),
524 Err(err) => panic!("{err}"),
525 }
526 }
527}
528
529#[cfg(test)]
530mod tests_with_jwt_authorization_grant {
531 use super::*;
532
533 #[test]
534 fn test_ser_de() {
535 let body_str = "grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";
536 match serde_urlencoded::from_str::<Body<String>>(body_str) {
537 Ok(Body::JwtAuthorizationGrant(body)) => {
538 assert_eq!(
539 body.assertion,
540 "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
541 );
542 assert_eq!(body.scope, None);
543 assert_eq!(body.client_id, None);
544
545 assert_eq!(
546 body_str,
547 serde_urlencoded::to_string(Body::<String>::JwtAuthorizationGrant(body))
548 .unwrap()
549 );
550 }
551 #[allow(unreachable_patterns)]
552 Ok(body) => panic!("{body:?}"),
553 Err(err) => panic!("{err}"),
554 }
555 }
556}