1use josekit::jwe::JweHeader;
2use josekit::jws::JwsHeader;
3use josekit::{jwe, jws};
4use std::collections::HashMap;
5use std::time::Duration;
6
7use serde_json::{json, Value};
8use url::{form_urlencoded, Url};
9
10use crate::helpers::{generate_random, get_serde_value_as_string, string_map_to_form_url_encoded};
11use crate::jwks::jwks::CustomJwk;
12use crate::types::grant_params::GrantParams;
13use crate::types::http_client::HttpMethod;
14use crate::types::query_keystore::QueryKeyStore;
15use crate::types::{
16 CallbackParams, ClaimParam, DeviceAuthorizationExtras, DeviceAuthorizationParams,
17 DeviceAuthorizationResponse, Fapi, GrantExtras, HttpRequest, HttpResponse, IntrospectionExtras,
18 OAuthCallbackChecks, OAuthCallbackParams, OidcHttpClient, OidcReturnType, OpenIdCallbackParams,
19 ParResponse, PushedAuthorizationRequestExtras, RefreshTokenExtras, RequestResourceParams,
20 RevokeExtras, UserinfoOptions,
21};
22use crate::{
23 helpers::convert_json_to,
24 tokenset::{TokenSet, TokenSetParams},
25 types::{
26 authentication_post_param::AuthenticationPostParams, AuthorizationParameters,
27 EndSessionParameters, OidcClientError,
28 },
29};
30
31use super::{Client, DeviceFlowHandle};
32
33impl Client {
35 pub fn is_fapi(&self) -> bool {
37 self.fapi.is_some()
38 }
39
40 pub fn is_fapi1(&self) -> bool {
42 self.fapi.as_ref().is_some_and(|x| matches!(x, Fapi::V1))
43 }
44
45 pub fn is_fapi2(&self) -> bool {
47 self.fapi.as_ref().is_some_and(|x| matches!(x, Fapi::V2))
48 }
49
50 pub fn authorization_url(
55 &self,
56 mut parameters: AuthorizationParameters,
57 ) -> OidcReturnType<Url> {
58 let mut authorization_endpiont = self.get_auth_endpoint()?;
59
60 let mut query_params: HashMap<String, String> = authorization_endpiont
61 .query_pairs()
62 .map(|(k, v)| (k.to_string(), v.to_string()))
63 .collect();
64
65 parameters = self.authorization_params(parameters);
66
67 let params_query: HashMap<String, String> = parameters.into();
68
69 query_params.extend(params_query);
70
71 authorization_endpiont.set_query(None);
72
73 let mut new_query_params = form_urlencoded::Serializer::new(String::new());
74
75 let mut scope_str = None;
76
77 for (query, value) in &query_params {
78 if query == "scope" {
79 scope_str = Some(urlencoding::encode(value).to_string());
80 continue;
81 }
82 new_query_params.append_pair(query, value);
83 }
84
85 if !query_params.is_empty() {
86 let mut query = new_query_params.finish();
87
88 if let Some(scope) = scope_str {
89 query.push_str(&format!("&scope={}", scope));
90 }
91
92 authorization_endpiont.set_query(Some(&query));
93 }
94
95 Ok(authorization_endpiont)
96 }
97
98 pub fn end_session_url(&self, mut parameters: EndSessionParameters) -> OidcReturnType<Url> {
103 let mut end_session_endpoint = match &self.issuer {
104 Some(i) => match &i.end_session_endpoint {
105 Some(ae) => match Url::parse(ae) {
106 Ok(u) => u,
107 Err(_) => {
108 return Err(Box::new(OidcClientError::new_type_error(
109 "end_session_endpoint is invalid url",
110 None,
111 )));
112 }
113 },
114 None => {
115 return Err(Box::new(OidcClientError::new_type_error(
116 "end_session_endpoint must be configured on the issuer",
117 None,
118 )));
119 }
120 },
121 None => {
122 return Err(Box::new(OidcClientError::new_error(
123 "issuer is empty",
124 None,
125 )))
126 }
127 };
128
129 if parameters.client_id.is_none() {
130 parameters.client_id = Some(self.client_id.clone());
131 }
132
133 let mut post_logout: Option<String> = None;
134
135 if let Some(plrus) = &self.post_logout_redirect_uris {
136 if let Some(first) = plrus.first() {
137 post_logout = Some(first.clone());
138 }
139 }
140
141 if let Some(plu) = parameters.post_logout_redirect_uri {
142 post_logout = Some(plu);
143 }
144
145 let mut query_params: HashMap<String, String> = end_session_endpoint
146 .query_pairs()
147 .map(|(k, v)| (k.to_string(), v.to_string()))
148 .collect();
149
150 if let Some(other) = parameters.other {
151 for (k, v) in other {
152 query_params.entry(k).or_insert(v);
153 }
154 }
155
156 if let Some(client_id) = parameters.client_id {
157 query_params.insert("client_id".to_string(), client_id);
158 }
159
160 if let Some(post_logout_redirect_uri) = post_logout {
161 query_params.insert(
162 "post_logout_redirect_uri".to_string(),
163 post_logout_redirect_uri,
164 );
165 }
166
167 if let Some(id_token_hint) = parameters.id_token_hint {
168 query_params.insert("id_token_hint".to_string(), id_token_hint);
169 }
170
171 if let Some(logout_hint) = parameters.logout_hint {
172 query_params.insert("logout_hint".to_string(), logout_hint);
173 }
174
175 if let Some(state) = parameters.state {
176 query_params.insert("state".to_string(), state);
177 }
178
179 if !query_params.is_empty() {
180 let new_query_params = string_map_to_form_url_encoded(&query_params)?;
181 end_session_endpoint.set_query(Some(&new_query_params));
182 }
183
184 Ok(end_session_endpoint)
185 }
186
187 pub fn authorization_post(
192 &self,
193 mut parameters: AuthorizationParameters,
194 ) -> OidcReturnType<String> {
195 let authorization_endpiont = self.get_auth_endpoint()?;
196
197 let mut query_params: HashMap<String, String> = authorization_endpiont
198 .query_pairs()
199 .map(|(k, v)| (k.to_string(), v.to_string()))
200 .collect();
201
202 parameters = self.authorization_params(parameters);
203
204 let params_query: HashMap<String, String> = parameters.into();
205
206 query_params.extend(params_query);
207
208 let mut html = r#"<!DOCTYPE html>
209 <head>
210 <title>Requesting Authorization</title>
211 </head>
212 <body onload="javascript:document.forms[0].submit()">
213 <form method="post" action=""#
214 .to_string()
215 + authorization_endpiont.as_ref()
216 + r#"">"#
217 + "\n";
218
219 for (name, value) in query_params {
220 html = html
221 + r#"<input type="hidden" name=""#
222 + &name
223 + r#"" value=""#
224 + &value
225 + r#""/>"#
226 + "\n";
227 }
228
229 html += r#"</form>
230 </body>
231 </html>"#;
232
233 Ok(html)
234 }
235
236 #[async_recursion::async_recursion(? Send)]
242 pub async fn grant_async<T>(
243 &mut self,
244 http_client: &T,
245 params: GrantParams<'async_recursion>,
246 ) -> OidcReturnType<TokenSet>
247 where
248 T: OidcHttpClient,
249 {
250 let issuer = self
251 .issuer
252 .as_ref()
253 .ok_or(Box::new(OidcClientError::new_error(
254 "Issuer is required for authenticated_post",
255 None,
256 )))?;
257
258 if issuer.token_endpoint.is_none() {
259 return Err(Box::new(OidcClientError::new_type_error(
260 "token_endpoint must be configured on the issuer",
261 None,
262 )));
263 }
264
265 let req = HttpRequest::new().form(params.body.clone());
266
267 let auth_post_params = AuthenticationPostParams {
268 client_assertion_payload: params.extras.client_assertion_payload.as_ref(),
269 dpop: params.extras.dpop,
270 endpoint_auth_method: params.extras.endpoint_auth_method,
271 };
272
273 let response = match self
274 .authenticated_post_async("token", req, auth_post_params, http_client)
275 .await
276 .map_err(|e| *e)
277 {
278 Ok(r) => r,
279 Err(OidcClientError::OPError(e, Some(res))) => {
280 if params.retry && e.error == "use_dpop_nonce" {
281 return self.grant_async(http_client, params).await;
282 }
283
284 return Err(Box::new(OidcClientError::new_op_error(
285 e.error,
286 e.error_description,
287 e.error_uri,
288 Some(res),
289 )));
290 }
291 Err(e) => return Err(Box::new(e)),
292 };
293
294 let body = response.body.clone().ok_or(OidcClientError::new_error(
295 "body expected in grant response",
296 Some(response.clone()),
297 ))?;
298
299 let token_params = convert_json_to::<TokenSetParams>(&body).or(Err(Box::new(
300 OidcClientError::new_error("could not convert body to TokenSetParams", Some(response)),
301 )))?;
302
303 Ok(TokenSet::new(token_params))
304 }
305
306 pub async fn oauth_callback_async<T>(
312 &mut self,
313 http_client: &T,
314 mut params: OAuthCallbackParams<'_>,
315 ) -> OidcReturnType<TokenSet>
316 where
317 T: OidcHttpClient,
318 {
319 let checks = params.checks.unwrap_or_default();
320
321 if checks.jarm.is_some_and(|x| x) && params.parameters.response.is_none() {
322 return Err(Box::new(OidcClientError::new_rp_error(
323 "expected a JARM response",
324 None,
325 )));
326 } else if let Some(response) = ¶ms.parameters.response {
327 let decrypted = self.decrypt_jarm(response)?;
328 let payload = self.validate_jarm_async(&decrypted, http_client).await?;
329 params.parameters = CallbackParams::from_jwt_payload(&payload);
330 }
331
332 if params.parameters.state.is_some() && checks.state.is_none() {
333 return Err(Box::new(OidcClientError::new_type_error(
334 "checks.state argument is missing",
335 None,
336 )));
337 }
338
339 if params.parameters.state.is_none() && checks.state.is_some() {
340 return Err(Box::new(OidcClientError::new_rp_error(
341 "state missing from the response",
342 None,
343 )));
344 }
345
346 if params.parameters.state.as_deref() != checks.state {
347 let checks_state = checks.state;
348 let params_state = params.parameters.state.clone();
349
350 return Err(Box::new(OidcClientError::new_rp_error(
351 &format!(
352 "state mismatch, expected {0}, got: {1}",
353 checks_state.unwrap(),
354 params_state.unwrap()
355 ),
356 None,
357 )));
358 }
359
360 let issuer = match self.issuer.as_ref() {
361 Some(iss) => iss,
362 None => {
363 return Err(Box::new(OidcClientError::new_type_error(
364 "Issuer is required",
365 None,
366 )))
367 }
368 };
369
370 if params.parameters.iss.is_some() {
371 if issuer.issuer.is_empty() {
372 return Err(Box::new(OidcClientError::new_type_error(
373 "issuer must be configured on the issuer",
374 None,
375 )));
376 }
377
378 let params_iss = params.parameters.iss.clone().unwrap();
379 if params_iss != issuer.issuer {
380 return Err(Box::new(OidcClientError::new_rp_error(
381 &format!(
382 "iss mismatch, expected {}, got: {params_iss}",
383 issuer.issuer
384 ),
385 None,
386 )));
387 }
388 } else if issuer
389 .authorization_response_iss_parameter_supported
390 .is_some_and(|x| x)
391 && params.parameters.id_token.is_none()
392 && params.parameters.response.is_none()
393 {
394 return Err(Box::new(OidcClientError::new_rp_error(
395 "iss missing from the response",
396 None,
397 )));
398 }
399
400 if params.parameters.error.is_some() {
401 return Err(Box::new(OidcClientError::new_op_error(
402 params.parameters.error.unwrap(),
403 params.parameters.error_description,
404 params.parameters.error_uri,
405 None,
406 )));
407 }
408
409 if params
410 .parameters
411 .id_token
412 .as_ref()
413 .is_some_and(|x| !x.is_empty())
414 {
415 return Err(Box::new(OidcClientError::new_rp_error(
416 "id_token detected in the response, you must use client.callback_async() instead of client.oauth_callback_async()",
417 None,
418 )));
419 }
420
421 params.parameters.id_token = None;
422
423 if checks.response_type.is_some() {
424 for res_type in checks.response_type.as_ref().unwrap().split(' ') {
425 if res_type == "none"
426 && (params.parameters.code.is_some()
427 || params.parameters.id_token.is_some()
428 || params.parameters.access_token.is_some())
429 {
430 return Err(Box::new(OidcClientError::new_rp_error(
431 "unexpected params encountered for \"none\" response",
432 None,
433 )));
434 }
435
436 if res_type == "code" || res_type == "token" {
437 let mut message = "";
438
439 if res_type == "code" && params.parameters.code.is_none() {
440 message = "code missing from response";
441 }
442
443 if res_type == "token" && params.parameters.access_token.is_none() {
444 message = "access_token missing from response";
445 }
446
447 if res_type == "token" && params.parameters.token_type.is_none() {
448 message = "token_type missing from response";
449 }
450
451 if !message.is_empty() {
452 return Err(Box::new(OidcClientError::new_rp_error(message, None)));
453 }
454 }
455 }
456 }
457
458 if params.parameters.code.is_some() {
459 let mut exchange_body = match params.extras.as_ref() {
460 Some(e) => e
461 .exchange_body
462 .clone()
463 .unwrap_or(HashMap::<String, String>::new()),
464 None => HashMap::<String, String>::new(),
465 };
466
467 exchange_body.insert("grant_type".to_string(), "authorization_code".to_owned());
468 exchange_body.insert(
469 "code".to_string(),
470 params.parameters.code.as_ref().unwrap().to_owned(),
471 );
472 exchange_body.insert("redirect_uri".to_string(), params.redirect_uri.to_owned());
473
474 if let Some(cv) = checks.code_verifier {
475 exchange_body.insert("code_verifier".to_string(), cv.to_owned());
476 };
477
478 let mut grant_extras = GrantExtras::default();
479
480 match ¶ms.extras {
481 Some(e) => {
482 grant_extras
483 .client_assertion_payload
484 .clone_from(&e.client_assertion_payload);
485 grant_extras.dpop = e.dpop.as_ref();
486 }
487 None => {}
488 };
489
490 let mut token_set = self
491 .grant_async(
492 http_client,
493 GrantParams {
494 body: exchange_body,
495 extras: grant_extras,
496 retry: true,
497 },
498 )
499 .await?;
500
501 if token_set.get_id_token().is_some_and(|x| !x.is_empty()) {
502 return Err(Box::new(OidcClientError::new_rp_error(
503 "id_token detected in the response, you must use client.callback_async() instead of client.oauth_callback_async()",
504 None,
505 )));
506 }
507
508 token_set.set_id_token(None);
509
510 return Ok(token_set);
511 }
512
513 let mut other_fields = match params.parameters.other {
514 Some(o) => o.clone(),
515 None => HashMap::new(),
516 };
517
518 if let Some(state) = params.parameters.state {
519 other_fields.insert("state".to_string(), state);
520 }
521
522 if let Some(code) = params.parameters.code {
523 other_fields.insert("code".to_string(), code);
524 }
525
526 let expires_at = match other_fields.get("expires_at") {
527 Some(eat) => eat.parse::<i64>().ok(),
528 None => None,
529 };
530 let scope = other_fields.get("scope").map(|s| s.to_owned());
531 let token_type = other_fields.get("token_type").map(|tt| tt.to_owned());
532 let session_state = other_fields.get("session_state").map(|ss| ss.to_owned());
533 let refresh_token = other_fields.get("refresh_token").map(|rt| rt.to_owned());
534 let expires_in = match params.parameters.expires_in {
535 Some(exp_in) => exp_in.parse::<i64>().ok(),
536 None => None,
537 };
538
539 let mut tokenset_others = HashMap::new();
540
541 for (k, v) in other_fields {
542 if let Ok(val) = serde_json::to_value(v) {
543 tokenset_others.insert(k, val);
544 }
545 }
546
547 let token_params = TokenSetParams {
548 access_token: params.parameters.access_token,
549 id_token: params.parameters.id_token,
550 expires_in,
551 expires_at,
552 scope,
553 token_type,
554 session_state,
555 refresh_token,
556 other: Some(tokenset_others),
557 };
558
559 Ok(TokenSet::new(token_params))
560 }
561
562 pub fn set_skip_max_age_check(&mut self, max_age_check: bool) {
566 self.skip_max_age_check = max_age_check;
567 }
568
569 pub fn set_skip_nonce_check(&mut self, nonce_check: bool) {
573 self.skip_nonce_check = nonce_check;
574 }
575
576 pub fn set_clock_skew_duration(&mut self, duration: Duration) {
580 self.clock_tolerance = duration;
581 }
582
583 pub async fn callback_async<T>(
589 &mut self,
590 http_client: &T,
591 mut params: OpenIdCallbackParams<'_>,
592 ) -> OidcReturnType<TokenSet>
593 where
594 T: OidcHttpClient,
595 {
596 let mut checks = params.checks.unwrap_or_default();
597
598 let default_oauth_checks = OAuthCallbackChecks::default();
599
600 let oauth_checks = checks
601 .oauth_checks
602 .as_ref()
603 .unwrap_or(&default_oauth_checks);
604
605 if oauth_checks.jarm.is_some_and(|x| x) && params.parameters.response.is_none() {
606 return Err(Box::new(OidcClientError::new_rp_error(
607 "expected a JARM response",
608 None,
609 )));
610 } else if let Some(response) = ¶ms.parameters.response {
611 let decrypted = self.decrypt_jarm(response)?;
612 let payload = self.validate_jarm_async(&decrypted, http_client).await?;
613 params.parameters = CallbackParams::from_jwt_payload(&payload);
614 }
615
616 if self.default_max_age.is_some() && checks.max_age.is_none() {
617 checks.max_age = self.default_max_age;
618 }
619
620 if params.parameters.state.is_some() && oauth_checks.state.is_none() {
621 return Err(Box::new(OidcClientError::new_type_error(
622 "checks.state argument is missing",
623 None,
624 )));
625 }
626
627 if params.parameters.state.is_none() && oauth_checks.state.is_some() {
628 return Err(Box::new(OidcClientError::new_rp_error(
629 "state missing from the response",
630 None,
631 )));
632 }
633
634 if params.parameters.state.as_deref() != oauth_checks.state {
635 let checks_state = oauth_checks.state;
636 let params_state = params.parameters.state.clone();
637
638 return Err(Box::new(OidcClientError::new_rp_error(
639 &format!(
640 "state mismatch, expected {0}, got: {1}",
641 checks_state.unwrap(),
642 params_state.unwrap()
643 ),
644 None,
645 )));
646 }
647
648 let issuer = match self.issuer.as_ref() {
649 Some(iss) => iss,
650 None => {
651 return Err(Box::new(OidcClientError::new_type_error(
652 "Issuer is required",
653 None,
654 )))
655 }
656 };
657
658 if params.parameters.iss.is_some() {
659 if issuer.issuer.is_empty() {
660 return Err(Box::new(OidcClientError::new_type_error(
661 "issuer must be configured on the issuer",
662 None,
663 )));
664 }
665
666 let params_iss = params.parameters.iss.clone().unwrap();
667 if params_iss != issuer.issuer {
668 return Err(Box::new(OidcClientError::new_rp_error(
669 &format!(
670 "iss mismatch, expected {}, got: {params_iss}",
671 issuer.issuer
672 ),
673 None,
674 )));
675 }
676 } else if issuer
677 .authorization_response_iss_parameter_supported
678 .is_some_and(|x| x)
679 && params.parameters.id_token.is_none()
680 && params.parameters.response.is_none()
681 {
682 return Err(Box::new(OidcClientError::new_rp_error(
683 "iss missing from the response",
684 None,
685 )));
686 }
687
688 if params.parameters.error.is_some() {
689 return Err(Box::new(OidcClientError::new_op_error(
690 params.parameters.error.unwrap(),
691 params.parameters.error_description,
692 params.parameters.error_uri,
693 None,
694 )));
695 }
696
697 if oauth_checks.response_type.is_some() {
698 for res_type in oauth_checks.response_type.as_ref().unwrap().split(' ') {
699 if res_type == "none"
700 && (params.parameters.code.is_some()
701 || params.parameters.id_token.is_some()
702 || params.parameters.access_token.is_some())
703 {
704 return Err(Box::new(OidcClientError::new_rp_error(
705 "unexpected params encountered for \"none\" response",
706 None,
707 )));
708 } else if res_type == "code" || res_type == "token" || res_type == "id_token" {
709 let mut message = "";
710
711 if res_type == "code" && params.parameters.code.is_none() {
712 message = "code missing from response";
713 }
714
715 if res_type == "token" && params.parameters.access_token.is_none() {
716 message = "access_token missing from response";
717 }
718
719 if res_type == "token" && params.parameters.token_type.is_none() {
720 message = "token_type missing from response";
721 }
722
723 if res_type == "id_token" && params.parameters.id_token.is_none() {
724 message = "id_token missing from response";
725 }
726
727 if !message.is_empty() {
728 return Err(Box::new(OidcClientError::new_rp_error(message, None)));
729 }
730 }
731 }
732 }
733
734 if params
735 .parameters
736 .id_token
737 .as_ref()
738 .is_some_and(|x| !x.is_empty())
739 {
740 let mut other_fields = match ¶ms.parameters.other {
741 Some(o) => o.clone(),
742 None => HashMap::new(),
743 };
744
745 if let Some(state) = ¶ms.parameters.state {
746 other_fields.insert("state".to_owned(), state.to_owned());
747 }
748
749 if let Some(code) = ¶ms.parameters.code {
750 other_fields.insert("code".to_owned(), code.to_owned());
751 }
752
753 let expires_at = match other_fields.get("expires_at") {
754 Some(eat) => eat.parse::<i64>().ok(),
755 None => None,
756 };
757 let scope = other_fields.get("scope").map(|s| s.to_owned());
758 let token_type = other_fields.get("token_type").map(|tt| tt.to_owned());
759 let session_state = other_fields.get("session_state").map(|ss| ss.to_owned());
760 let refresh_token = other_fields.get("refresh_token").map(|rt| rt.to_owned());
761 let expires_in = match ¶ms.parameters.expires_in {
762 Some(exp_in) => exp_in.parse::<i64>().ok(),
763 None => None,
764 };
765
766 let mut tokenset_others = HashMap::new();
767
768 for (k, v) in other_fields {
769 if let Ok(val) = serde_json::to_value(v) {
770 tokenset_others.insert(k, val);
771 }
772 }
773
774 let token_params = TokenSetParams {
775 access_token: params.parameters.access_token.clone(),
776 id_token: params.parameters.id_token.clone(),
777 expires_in,
778 expires_at,
779 scope,
780 token_type,
781 session_state,
782 refresh_token,
783 other: Some(tokenset_others),
784 };
785
786 let mut token_set = TokenSet::new(token_params);
787
788 token_set = self.decrypt_id_token(token_set)?;
789
790 token_set = self
791 .validate_id_token_async(
792 token_set,
793 checks.nonce.map(|x| x.to_owned()),
794 "authorization",
795 checks.max_age,
796 oauth_checks.state.map(|x| x.to_owned()),
797 http_client,
798 )
799 .await?;
800
801 if params.parameters.code.is_none() {
802 return Ok(token_set);
803 }
804 }
805
806 if params.parameters.code.is_some() {
807 let mut exchange_body = match params.extras.as_ref() {
808 Some(e) => e
809 .exchange_body
810 .clone()
811 .unwrap_or(HashMap::<String, String>::new()),
812 None => HashMap::<String, String>::new(),
813 };
814
815 exchange_body.insert("grant_type".to_string(), "authorization_code".to_owned());
816 exchange_body.insert(
817 "code".to_string(),
818 params.parameters.code.as_ref().unwrap().to_owned(),
819 );
820 exchange_body.insert("redirect_uri".to_string(), params.redirect_uri.to_owned());
821
822 if let Some(cv) = oauth_checks.code_verifier {
823 exchange_body.insert("code_verifier".to_string(), cv.to_owned());
824 };
825
826 let mut grant_extras = GrantExtras::default();
827
828 match ¶ms.extras {
829 Some(e) => {
830 grant_extras
831 .client_assertion_payload
832 .clone_from(&e.client_assertion_payload);
833 grant_extras.dpop = e.dpop.as_ref();
834 }
835 None => {}
836 };
837
838 let mut token_set = self
839 .grant_async(
840 http_client,
841 GrantParams {
842 body: exchange_body,
843 extras: grant_extras,
844 retry: true,
845 },
846 )
847 .await?;
848
849 token_set = self.decrypt_id_token(token_set)?;
850 token_set = self
851 .validate_id_token_async(
852 token_set,
853 checks.nonce.map(|x| x.to_owned()),
854 "token",
855 checks.max_age,
856 oauth_checks.state.map(|x| x.to_owned()),
857 http_client,
858 )
859 .await?;
860
861 if params.parameters.session_state.is_some() {
862 token_set.set_session_state(params.parameters.session_state);
863 }
864
865 return Ok(token_set);
866 }
867
868 let mut other_fields = match ¶ms.parameters.other {
869 Some(o) => o.clone(),
870 None => HashMap::new(),
871 };
872
873 if let Some(state) = ¶ms.parameters.state {
874 other_fields.insert("state".to_string(), state.to_owned());
875 }
876
877 if let Some(code) = params.parameters.code {
878 other_fields.insert("code".to_string(), code);
879 }
880
881 let expires_at = match other_fields.get("expires_at") {
882 Some(eat) => eat.parse::<i64>().ok(),
883 None => None,
884 };
885 let scope = other_fields.get("scope").map(|s| s.to_owned());
886 let token_type = other_fields.get("token_type").map(|tt| tt.to_owned());
887 let session_state = other_fields.get("session_state").map(|ss| ss.to_owned());
888 let refresh_token = other_fields.get("refresh_token").map(|rt| rt.to_owned());
889 let expires_in = match params.parameters.expires_in {
890 Some(exp_in) => exp_in.parse::<i64>().ok(),
891 None => None,
892 };
893
894 let mut tokenset_others = HashMap::new();
895
896 for (k, v) in other_fields {
897 if let Ok(val) = serde_json::to_value(v) {
898 tokenset_others.insert(k, val);
899 }
900 }
901
902 let token_params = TokenSetParams {
903 access_token: params.parameters.access_token,
904 id_token: params.parameters.id_token,
905 expires_in,
906 expires_at,
907 scope,
908 token_type,
909 session_state,
910 refresh_token,
911 other: Some(tokenset_others),
912 };
913
914 Ok(TokenSet::new(token_params))
915 }
916
917 pub async fn introspect_async<T>(
925 &mut self,
926 http_client: &T,
927 token: String,
928 token_type_hint: Option<String>,
929 extras: Option<IntrospectionExtras>,
930 ) -> OidcReturnType<HttpResponse>
931 where
932 T: OidcHttpClient,
933 {
934 let issuer = match self.issuer.as_ref() {
935 Some(iss) => iss,
936 None => {
937 return Err(Box::new(OidcClientError::new_type_error(
938 "Issuer is required",
939 None,
940 )))
941 }
942 };
943
944 if issuer.introspection_endpoint.is_none() {
945 return Err(Box::new(OidcClientError::new_type_error(
946 "introspection_endpoint must be configured on the issuer",
947 None,
948 )));
949 }
950
951 let mut form = HashMap::new();
952
953 form.insert("token".to_string(), token);
954
955 if let Some(hint) = token_type_hint {
956 form.insert("token_type_hint".to_string(), hint);
957 }
958
959 if let Some(p) = &extras {
960 if let Some(body) = &p.introspect_body {
961 for (k, v) in body {
962 form.insert(k.to_owned(), v.to_owned());
963 }
964 }
965 }
966
967 let req = HttpRequest::new().form(form);
968
969 self.authenticated_post_async(
970 "introspection",
971 req,
972 AuthenticationPostParams {
973 client_assertion_payload: extras
974 .as_ref()
975 .and_then(|x| x.client_assertion_payload.as_ref()),
976 dpop: None,
977 endpoint_auth_method: None,
978 },
979 http_client,
980 )
981 .await
982 }
983
984 #[async_recursion::async_recursion(? Send)]
990 pub async fn request_resource_async<T>(
991 &mut self,
992 http_client: &T,
993 mut params: RequestResourceParams<'async_recursion>,
994 ) -> OidcReturnType<HttpResponse>
995 where
996 T: OidcHttpClient,
997 {
998 if self.dpop_bound_access_tokens.is_some_and(|x| x) && params.options.dpop.is_none() {
999 return Err(Box::new(OidcClientError::new_type_error(
1000 "DPoP key not set",
1001 None,
1002 )));
1003 }
1004
1005 let tt = if params.options.dpop.is_some() {
1006 "DPoP"
1007 } else {
1008 params.token_type.unwrap_or("Bearer")
1009 };
1010
1011 if !params
1012 .options
1013 .headers
1014 .iter()
1015 .any(|(k, _)| k.as_str().to_lowercase() == "authorization")
1016 && ((tt == "Bearer" && params.options.bearer) || tt == "DPoP")
1017 {
1018 params.options.headers.insert(
1019 "authorization".to_string(),
1020 vec![format!("{tt} {}", params.access_token)],
1021 );
1022 }
1023
1024 let mut req = HttpRequest::new()
1025 .url(Url::parse(params.resource_url).map_err(|e| {
1026 Box::new(OidcClientError::new_error(
1027 &format!("Invalid Url: {}", e),
1028 None,
1029 ))
1030 })?)
1031 .method(params.options.method.clone())
1032 .headers(params.options.headers.clone())
1033 .mtls(
1034 self.tls_client_certificate_bound_access_tokens
1035 .is_some_and(|x| x),
1036 )
1037 .expect_bearer(params.options.bearer)
1038 .expect_json_body(params.options.expect_body_to_be_json);
1039
1040 if let Some(body) = ¶ms.options.body {
1041 req = req.body(body.clone());
1042 }
1043
1044 match self
1045 .instance_request_async(
1046 req,
1047 params.options.dpop,
1048 Some(params.access_token),
1049 http_client,
1050 )
1051 .await
1052 .map_err(|e| *e)
1053 {
1054 Ok(r) => Ok(r),
1055 Err(OidcClientError::OPError(e, Some(res))) => {
1056 if params.retry && e.error == "use_dpop_nonce" {
1057 if let Some(header_val) = res.www_authenticate.as_ref() {
1058 if header_val.starts_with("dpop ") {
1059 return self.request_resource_async(http_client, params).await;
1060 }
1061 }
1062 }
1063
1064 return Err(Box::new(OidcClientError::new_op_error(
1065 e.error,
1066 e.error_description,
1067 e.error_uri,
1068 Some(res),
1069 )));
1070 }
1071 Err(e) => Err(Box::new(e)),
1072 }
1073 }
1074
1075 pub fn callback_params(
1083 &self,
1084 incoming_url: Option<&Url>,
1085 incoming_body: Option<String>,
1086 ) -> OidcReturnType<CallbackParams> {
1087 let mut query_pairs = None;
1088 if let Some(url) = incoming_url {
1089 query_pairs = Some(
1090 url.query_pairs()
1091 .map(|(x, y)| (x.to_string(), y.to_string()))
1092 .collect::<Vec<(String, String)>>(),
1093 );
1094 } else if let Some(body) = incoming_body {
1095 query_pairs = Some(
1096 form_urlencoded::parse(body.as_bytes())
1097 .map(|(k, v)| (k.to_string(), v.to_string()))
1098 .collect(),
1099 );
1100 }
1101
1102 if let Some(qp) = query_pairs {
1103 let mut params = CallbackParams::default();
1104
1105 let mut other = HashMap::new();
1106
1107 for (k, v) in qp {
1108 match k.as_str() {
1109 "access_token" => params.access_token = Some(v),
1110 "code" => params.code = Some(v),
1111 "error" => params.error = Some(v),
1112 "error_description" => params.error_description = Some(v),
1113 "error_uri" => params.error_uri = Some(v),
1114 "id_token" => params.id_token = Some(v),
1115 "iss" => params.iss = Some(v),
1116 "response" => params.response = Some(v),
1117 "session_state" => params.session_state = Some(v),
1118 "state" => params.state = Some(v),
1119 "token_type" => params.token_type = Some(v),
1120 _ => {
1121 other.insert(k, v);
1122 }
1123 };
1124 }
1125
1126 if !other.is_empty() {
1127 params.other = Some(other);
1128 }
1129 return Ok(params);
1130 }
1131
1132 Err(Box::new(OidcClientError::new_error(
1133 "could not parse the request",
1134 None,
1135 )))
1136 }
1137
1138 pub async fn refresh_async<T>(
1145 &mut self,
1146 http_client: &T,
1147 token_set: TokenSet,
1148 extras: Option<RefreshTokenExtras<'_>>,
1149 ) -> OidcReturnType<TokenSet>
1150 where
1151 T: OidcHttpClient,
1152 {
1153 let refresh_token = match token_set.get_refresh_token() {
1154 Some(rt) => rt,
1155 None => {
1156 return Err(Box::new(OidcClientError::new_type_error(
1157 "refresh_token not present in TokenSet",
1158 None,
1159 )));
1160 }
1161 };
1162
1163 let mut body = HashMap::new();
1164
1165 if let Some(exchange_payload) = extras.as_ref().and_then(|x| x.exchange_body.as_ref()) {
1166 for (k, v) in exchange_payload {
1167 body.insert(k.to_owned(), v.to_owned());
1168 }
1169 }
1170
1171 body.insert("grant_type".to_string(), "refresh_token".to_owned());
1172 body.insert("refresh_token".to_string(), refresh_token.to_owned());
1173
1174 let grant_extras = GrantExtras {
1175 client_assertion_payload: extras
1176 .as_ref()
1177 .and_then(|x| x.client_assertion_payload.to_owned()),
1178 dpop: extras.as_ref().and_then(|x| x.dpop),
1179 endpoint_auth_method: None,
1180 };
1181
1182 let mut new_token_set = self
1183 .grant_async(
1184 http_client,
1185 GrantParams {
1186 body,
1187 extras: grant_extras,
1188 retry: true,
1189 },
1190 )
1191 .await?;
1192
1193 if new_token_set.get_id_token().is_some() {
1194 new_token_set = self.decrypt_id_token(new_token_set)?;
1195 new_token_set = self
1196 .validate_id_token_async(new_token_set, None, "token", None, None, http_client)
1197 .await?;
1198
1199 if let Some(Value::String(expected_sub)) =
1200 token_set.claims().as_ref().and_then(|x| x.get("sub"))
1201 {
1202 if let Some(Value::String(new_sub)) =
1203 new_token_set.claims().as_ref().and_then(|x| x.get("sub"))
1204 {
1205 if expected_sub != new_sub {
1206 return Err(Box::new(OidcClientError::new_rp_error(
1207 &format!("sub mismatch, expected {expected_sub}, got: {new_sub}"),
1208 None,
1209 )));
1210 }
1211 }
1212 }
1213 }
1214
1215 Ok(new_token_set)
1216 }
1217
1218 pub async fn revoke_async<T>(
1226 &mut self,
1227 http_client: &T,
1228 token: &str,
1229 token_type_hint: Option<&str>,
1230 extras: Option<RevokeExtras>,
1231 ) -> OidcReturnType<HttpResponse>
1232 where
1233 T: OidcHttpClient,
1234 {
1235 let issuer = match self.issuer.as_ref() {
1236 Some(iss) => iss,
1237 None => {
1238 return Err(Box::new(OidcClientError::new_type_error(
1239 "Issuer is required",
1240 None,
1241 )))
1242 }
1243 };
1244
1245 if issuer.revocation_endpoint.is_none() {
1246 return Err(Box::new(OidcClientError::new_type_error(
1247 "revocation_endpoint must be configured on the issuer",
1248 None,
1249 )));
1250 }
1251
1252 let mut form = HashMap::new();
1253
1254 form.insert("token".to_string(), token.to_owned());
1255
1256 if let Some(h) = token_type_hint {
1257 form.insert("token_type_hint".to_string(), h.to_owned());
1258 }
1259
1260 let mut client_assertion_payload = None;
1261
1262 if let Some(p) = extras {
1263 if let Some(body) = p.revocation_body {
1264 for (k, v) in body {
1265 form.insert(k, v);
1266 }
1267 }
1268
1269 if let Some(cap) = p.client_assertion_payload {
1270 client_assertion_payload = Some(cap);
1271 }
1272 }
1273
1274 let req = HttpRequest::new().form(form).expect_body(false);
1275
1276 self.authenticated_post_async(
1277 "revocation",
1278 req,
1279 AuthenticationPostParams {
1280 client_assertion_payload: client_assertion_payload.as_ref(),
1281 dpop: None,
1282 endpoint_auth_method: None,
1283 },
1284 http_client,
1285 )
1286 .await
1287 }
1288
1289 pub async fn userinfo_async<T>(
1296 &mut self,
1297 http_client: &T,
1298 token_set: &TokenSet,
1299 options: UserinfoOptions<'_>,
1300 ) -> OidcReturnType<Value>
1301 where
1302 T: OidcHttpClient,
1303 {
1304 let issuer = match self.issuer.as_ref() {
1305 Some(iss) => iss,
1306 None => {
1307 return Err(Box::new(OidcClientError::new_type_error(
1308 "Issuer is required",
1309 None,
1310 )))
1311 }
1312 };
1313
1314 let userinfo_endpoint = match &issuer.userinfo_endpoint {
1315 Some(e) => e.to_string(),
1316 None => {
1317 return Err(Box::new(OidcClientError::new_type_error(
1318 "userinfo_endpoint must be configured on the issuer",
1319 None,
1320 )))
1321 }
1322 };
1323
1324 let access_token = token_set
1325 .get_access_token()
1326 .ok_or(OidcClientError::new_type_error(
1327 "access_token is required in token_set",
1328 None,
1329 ))?;
1330
1331 if options.via != "header" && options.via != "body" {
1332 return Err(Box::new(OidcClientError::new_type_error(
1333 "via can only be body or header",
1334 None,
1335 )));
1336 }
1337
1338 if options.method != "GET" && options.method != "POST" {
1339 return Err(Box::new(OidcClientError::new_type_error(
1340 "userinfo_async() method can only be POST or a GET",
1341 None,
1342 )));
1343 }
1344
1345 if options.via == "body" && options.method != "POST" {
1346 return Err(Box::new(OidcClientError::new_type_error(
1347 "can only send body on POST",
1348 None,
1349 )));
1350 }
1351
1352 let jwt = self.userinfo_signed_response_alg.is_some()
1353 || self.userinfo_encrypted_response_alg.is_some();
1354
1355 let mut resource_params = RequestResourceParams::default()
1356 .access_token(&access_token)
1357 .use_bearer(options.via == "header")
1358 .expect_json_body(!jwt)
1359 .retry(true);
1360
1361 if jwt {
1362 resource_params = resource_params.set_header("accept", "application/jwt");
1363 } else {
1364 resource_params = resource_params.set_header("accept", "application/json");
1365 }
1366
1367 let mtls = self
1368 .tls_client_certificate_bound_access_tokens
1369 .is_some_and(|x| x);
1370
1371 let mut target_url = None;
1372
1373 if mtls && issuer.mtls_endpoint_aliases.is_some() {
1374 if let Some(mtls_alias) = &issuer.mtls_endpoint_aliases {
1375 if mtls_alias.userinfo_endpoint.is_some() {
1376 target_url.clone_from(&mtls_alias.userinfo_endpoint);
1377 }
1378 }
1379 }
1380
1381 let mut url = Url::parse(target_url.unwrap_or(userinfo_endpoint).as_str())
1382 .map_err(|_| OidcClientError::new_error("Invalid Url", None))?;
1383
1384 let mut form_body = HashMap::new();
1385
1386 if options.via == "body" {
1387 resource_params = resource_params
1389 .remove_header("authorization")
1390 .set_header("content-type", "application/x-www-form-urlencoded");
1391 form_body.insert("access_token".to_string(), access_token.to_owned());
1392 }
1393
1394 if let Some(params) = options.params {
1395 if options.method == "GET" {
1396 for (k, v) in params {
1397 url.query_pairs_mut().append_pair(&k, &v);
1398 }
1399 } else if options.via == "body" && options.method == "POST" {
1400 for (k, v) in params {
1401 form_body.insert(k, v);
1402 }
1403 } else {
1404 resource_params = resource_params
1405 .remove_header("content-type")
1406 .set_header("content-type", "application/x-www-form-urlencoded");
1407 for (k, v) in params {
1408 form_body.insert(k, v);
1409 }
1410 }
1411 }
1412
1413 let mut body = None;
1414 if !form_body.is_empty() {
1415 body = Some(string_map_to_form_url_encoded(&form_body)?);
1416 }
1417
1418 let method = if options.method == "GET" {
1419 HttpMethod::GET
1420 } else {
1421 HttpMethod::POST
1422 };
1423
1424 resource_params = resource_params
1425 .set_method(method)
1426 .resource_url(url.as_str());
1427
1428 if let Some(dpop) = options.dpop {
1429 resource_params = resource_params.set_dpop_key(dpop);
1430 }
1431
1432 if let Some(body) = body {
1433 resource_params = resource_params.set_body(body);
1434 }
1435
1436 let token_type = token_set.get_token_type();
1437 if let Some(tt) = token_type.as_deref() {
1438 resource_params = resource_params.token_type(tt);
1439 }
1440
1441 let res = self
1442 .request_resource_async(http_client, resource_params)
1443 .await?;
1444
1445 let payload = match jwt {
1446 true => {
1447 if !res
1448 .content_type
1449 .as_ref()
1450 .is_some_and(|ct| ct.starts_with("application/jwt;"))
1451 {
1452 return Err(Box::new(OidcClientError::new_rp_error(
1453 "expected application/jwt response from the userinfo_endpoint",
1454 Some(res),
1455 )));
1456 }
1457
1458 let body = res
1459 .body
1460 .as_ref()
1461 .ok_or(OidcClientError::new_rp_error(
1462 "body was emtpy",
1463 Some(res.clone()),
1464 ))?
1465 .to_owned();
1466 let userinfo = self.decrypt_jwt_userinfo(body)?;
1467
1468 if self.userinfo_signed_response_alg.is_none() {
1469 if let Ok(Value::Object(json_res)) = serde_json::from_str::<Value>(&userinfo) {
1470 let mut payload = json!({});
1471 for (k, v) in json_res {
1472 payload[k] = v;
1473 }
1474 payload
1475 } else {
1476 return Err(Box::new(OidcClientError::new_rp_error(
1477 "failed to parse userinfo JWE payload as JSON",
1478 Some(res),
1479 )));
1480 }
1481 } else {
1482 let (jwt_payload, _, _) = self
1483 .validate_jwt_userinfo_async(&userinfo, http_client)
1484 .await?;
1485 let mut payload = json!({});
1486 for (k, v) in jwt_payload.claims_set() {
1487 payload[k] = v.clone();
1488 }
1489 payload
1490 }
1491 }
1492 false => {
1493 let body = res
1494 .body
1495 .as_ref()
1496 .ok_or(Box::new(OidcClientError::new_rp_error(
1497 "body was emtpy",
1498 Some(res.clone()),
1499 )))?
1500 .to_owned();
1501
1502 if let Ok(Value::Object(json_res)) = serde_json::from_str::<Value>(&body) {
1503 let mut payload = json!({});
1504 for (k, v) in json_res {
1505 payload[k] = v;
1506 }
1507 payload
1508 } else {
1509 return Err(Box::new(OidcClientError::new_rp_error(
1510 "failed to parse userinfo JWE payload as JSON",
1511 Some(res),
1512 )));
1513 }
1514 }
1515 };
1516
1517 if let Some(id_token) = token_set.get_id_token() {
1518 if let Some(Value::String(expected_sub)) =
1519 token_set.claims().as_ref().and_then(|x| x.get("sub"))
1520 {
1521 if let Some(Value::String(new_sub)) = payload.get("sub") {
1522 if expected_sub != new_sub {
1523 let mut extra_data: HashMap<String, Value> = HashMap::new();
1524
1525 if let Some(Ok(b)) = res.body.map(|x| serde_json::from_str::<Value>(&x)) {
1526 extra_data.insert("body".to_string(), b);
1527 }
1528
1529 extra_data.insert("jwt".to_string(), json!(id_token));
1530
1531 return Err(Box::new(OidcClientError::new_rp_error(
1532 &format!(
1533 "userinfo sub mismatch, expected {expected_sub}, got: {new_sub}"
1534 ),
1535 None,
1536 )));
1537 }
1538 }
1539 }
1540 }
1541
1542 Ok(payload)
1543 }
1544
1545 pub async fn request_object_async<T>(
1552 &mut self,
1553 http_client: &T,
1554 mut request_object: Value,
1555 ) -> OidcReturnType<String>
1556 where
1557 T: OidcHttpClient,
1558 {
1559 if !request_object.is_object() {
1560 return Err(Box::new(OidcClientError::new_type_error(
1561 "request_object must be a plain object",
1562 None,
1563 )));
1564 }
1565
1566 let e_key_management = self.request_object_encryption_alg.clone();
1567
1568 let header_alg = self
1569 .request_object_signing_alg
1570 .as_ref()
1571 .map(|x| x.to_owned())
1572 .unwrap_or("none".to_string());
1573 let header_typ = "oauth-authz-req+jwt";
1574
1575 let unix = (self.now)();
1576
1577 request_object["iss"] = json!(self.client_id);
1578
1579 if let Some(aud) = self.issuer.as_ref().map(|x| x.issuer.to_owned()) {
1580 request_object["aud"] = json!(aud);
1581 }
1582
1583 request_object["client_id"] = json!(self.client_id);
1584
1585 request_object["jti"] = json!(generate_random(None));
1586
1587 request_object["iat"] = json!(unix);
1588
1589 request_object["exp"] = json!(unix + 300);
1590
1591 if self.is_fapi() {
1592 request_object["nbf"] = json!(unix);
1593 }
1594
1595 let signed;
1596 let mut key = None;
1597
1598 let payload = request_object.to_string();
1599
1600 if header_alg == "none" {
1601 let encoded_header = base64_url::encode(&format!(
1602 "{{\"alg\":\"{header_alg}\",\"typ\":\"{header_typ}\"}}"
1603 ));
1604 let encoded_payload = base64_url::encode(&payload);
1605 signed = format!("{encoded_header}.{encoded_payload}.");
1606 } else {
1607 let symmetric = &header_alg.starts_with("HS");
1608 if *symmetric {
1609 key = Some(self.secret_for_alg(&header_alg)?);
1610 } else {
1611 let keystore =
1612 self.private_jwks
1613 .as_ref()
1614 .ok_or(OidcClientError::new_type_error(
1615 &format!(
1616 "no keystore present for client, cannot sign using alg {header_alg}"
1617 ),
1618 None,
1619 ))?;
1620
1621 key = keystore
1622 .get(Some(header_alg.to_string()), Some("sig".to_string()), None)?
1623 .first()
1624 .map(|x| x.to_owned().clone());
1625
1626 if key.is_none() {
1627 return Err(Box::new(OidcClientError::new_type_error(
1628 &format!("no key to sign with found for alg {header_alg}"),
1629 None,
1630 )));
1631 }
1632 }
1633
1634 let jwk = key.clone().ok_or(Box::new(OidcClientError::new_error(
1635 "No key found for signing request object",
1636 None,
1637 )))?;
1638 let signer = jwk.to_signer()?;
1639
1640 let mut header = JwsHeader::new();
1641 header.set_algorithm(&header_alg);
1642 header.set_token_type(header_typ);
1643
1644 if !symmetric {
1645 if let Some(kid) = jwk.key_id() {
1646 header.set_key_id(kid);
1647 }
1648 }
1649
1650 signed = jws::serialize_compact(payload.as_bytes(), &header, &*signer)
1651 .map_err(|e| OidcClientError::new_error(&e.to_string(), None))?;
1652 }
1653
1654 let field_alg = match e_key_management {
1655 Some(a) => a,
1656 None => return Ok(signed),
1657 };
1658
1659 let field_enc = self
1660 .request_object_encryption_enc
1661 .as_ref()
1662 .map(|x| x.to_owned())
1663 .unwrap_or("A128CBC-HS256".to_string()); let field_cty = "oauth-authz-req+jwt";
1665
1666 if field_alg.contains("RSA") || field_alg.contains("ECDH") {
1667 if let Some(issuer) = &mut self.issuer {
1668 let query_params = QueryKeyStore {
1669 alg: Some(field_alg.to_string()),
1670 key_use: Some("enc".to_string()),
1671 ..Default::default()
1672 };
1673 key = issuer
1674 .query_keystore_async(query_params, true, http_client)
1675 .await?
1676 .get_keys()
1677 .first()
1678 .map(|x| x.to_owned());
1679 }
1680 } else {
1681 let alg = if field_alg == "dir" {
1682 &field_enc
1683 } else {
1684 &field_alg
1685 };
1686 key = Some(self.secret_for_alg(alg)?);
1687 }
1688
1689 let jwk = key.ok_or(OidcClientError::new_error(
1690 "No key found for encrypting request object",
1691 None,
1692 ))?;
1693 let encryptor = jwk.to_jwe_encrypter()?;
1694
1695 let mut jwe_header = JweHeader::new();
1696
1697 jwe_header.set_algorithm(&field_alg);
1698 jwe_header.set_content_encryption(field_enc);
1699 jwe_header.set_content_type(field_cty);
1700 if let Some(kid) = jwk.key_id() {
1701 jwe_header.set_key_id(kid);
1702 }
1703
1704 jwe::serialize_compact(payload.as_bytes(), &jwe_header, &*encryptor)
1705 .map_err(|x| Box::new(OidcClientError::new_error(&x.to_string(), None)))
1706 }
1707
1708 pub async fn pushed_authorization_request_async<T>(
1716 &mut self,
1717 http_client: &T,
1718 parameters: Option<AuthorizationParameters>,
1719 extras: Option<PushedAuthorizationRequestExtras<'_>>,
1720 ) -> OidcReturnType<ParResponse>
1721 where
1722 T: OidcHttpClient,
1723 {
1724 let issuer = match self.issuer.as_ref() {
1725 Some(iss) => iss,
1726 None => {
1727 return Err(Box::new(OidcClientError::new_type_error(
1728 "Issuer is required",
1729 None,
1730 )))
1731 }
1732 };
1733
1734 if issuer.pushed_authorization_request_endpoint.is_none() {
1735 return Err(Box::new(OidcClientError::new_type_error(
1736 "pushed_authorization_request_endpoint must be configured on the issuer",
1737 None,
1738 )));
1739 }
1740
1741 let auth_params = parameters.unwrap_or_default();
1742
1743 let mut body = if auth_params.request.is_some() {
1744 auth_params
1745 } else {
1746 self.authorization_params(auth_params)
1747 };
1748
1749 body.client_id = Some(self.client_id.clone());
1750
1751 let form_body: HashMap<String, String> = body.into();
1752
1753 let req = HttpRequest::new()
1754 .form(form_body)
1755 .expect_json_body(true)
1756 .expect_status_code(201);
1757
1758 let client_assertion_payload = extras
1759 .as_ref()
1760 .and_then(|x| x.client_assertion_payload.as_ref());
1761
1762 let dpop = extras.as_ref().and_then(|x| x.dpop);
1763
1764 let params = AuthenticationPostParams {
1765 client_assertion_payload,
1766 endpoint_auth_method: Some("token"),
1767 dpop,
1768 };
1769
1770 let res = self
1771 .authenticated_post_async("pushed_authorization_request", req, params, http_client)
1772 .await?;
1773
1774 let body_obj = match res.body.as_ref().map(|b| convert_json_to::<Value>(b)) {
1775 Some(Ok(json)) => json,
1776 _ => {
1777 return Err(Box::new(OidcClientError::new_error(
1778 "could not convert body to serde::json value",
1779 None,
1780 )))
1781 }
1782 };
1783
1784 if body_obj.get("expires_in").is_none() {
1785 return Err(Box::new(OidcClientError::new_rp_error(
1786 "expected expires_in in Pushed Authorization Successful Response",
1787 Some(res),
1788 )));
1789 }
1790
1791 if !body_obj["expires_in"].is_number() {
1792 return Err(Box::new(OidcClientError::new_rp_error(
1793 "invalid expires_in value in Pushed Authorization Successful Response",
1794 Some(res),
1795 )));
1796 }
1797
1798 if body_obj.get("request_uri").is_none() {
1799 return Err(Box::new(OidcClientError::new_rp_error(
1800 "expected request_uri in Pushed Authorization Successful Response",
1801 Some(res),
1802 )));
1803 }
1804
1805 if !body_obj["request_uri"].is_string() {
1806 return Err(Box::new(OidcClientError::new_rp_error(
1807 "invalid request_uri value in Pushed Authorization Successful Response",
1808 Some(res),
1809 )));
1810 }
1811
1812 serde_json::from_value::<ParResponse>(body_obj).map_err(|_| {
1813 Box::new(OidcClientError::new_error(
1814 "Could not convert Par Json",
1815 None,
1816 ))
1817 })
1818 }
1819
1820 pub async fn device_authorization_async<T>(
1827 &mut self,
1828 http_client: &T,
1829 params: DeviceAuthorizationParams,
1830 extras: Option<DeviceAuthorizationExtras>,
1831 ) -> OidcReturnType<DeviceFlowHandle>
1832 where
1833 T: OidcHttpClient,
1834 {
1835 let issuer = match self.issuer.as_ref() {
1836 Some(iss) => iss,
1837 None => {
1838 return Err(Box::new(OidcClientError::new_type_error(
1839 "Issuer is required",
1840 None,
1841 )))
1842 }
1843 };
1844
1845 if issuer.token_endpoint.is_none() {
1846 return Err(Box::new(OidcClientError::new_type_error(
1847 "token_endpoint must be configured on the issuer",
1848 None,
1849 )));
1850 }
1851
1852 if issuer.device_authorization_endpoint.is_none() {
1853 return Err(Box::new(OidcClientError::new_type_error(
1854 "device_authorization_endpoint must be configured on the issuer",
1855 None,
1856 )));
1857 }
1858
1859 let mut auth_params = AuthorizationParameters {
1860 client_id: Some(self.client_id.clone()),
1861 ..Default::default()
1862 };
1863
1864 if let Some(client_id) = params.client_id {
1865 auth_params.client_id = Some(client_id);
1866 }
1867
1868 if let Some(scope) = params.scope {
1869 auth_params.scope = Some(scope);
1870 }
1871
1872 for (k, v) in params.other {
1873 if k == "redirect_uri" || k == "response_type" || k == "client_id" || k == "scope" {
1874 continue;
1875 } else if k == "audience" {
1876 if let Some(audience) = v.as_array() {
1877 if !audience.is_empty() {
1878 let mut aud_arr = vec![];
1879 for aud in audience {
1880 if let Some(a) = aud.as_str() {
1881 aud_arr.push(a.to_string());
1882 }
1883 }
1884
1885 auth_params.audience = Some(aud_arr);
1886 }
1887 }
1888
1889 if let Some(audience) = v.as_str() {
1890 let aud_arr = vec![audience.to_string()];
1891 auth_params.audience = Some(aud_arr);
1892 }
1893 } else if k == "claims" {
1894 if let Ok(claims) = serde_json::from_value::<ClaimParam>(v.clone()) {
1895 auth_params.claims = Some(claims);
1896 }
1897 } else if k == "acr_values" {
1898 if let Some(acr_values) = v.as_array() {
1899 if !acr_values.is_empty() {
1900 let mut acr_arr = vec![];
1901 for acr in acr_values {
1902 if let Some(a) = acr.as_str() {
1903 acr_arr.push(a.to_string());
1904 }
1905 }
1906
1907 auth_params.acr_values = Some(acr_arr);
1908 }
1909 }
1910 if let Some(acr_values) = v.as_str() {
1911 let acr_arr = vec![acr_values.to_string()];
1912 auth_params.acr_values = Some(acr_arr);
1913 }
1914 } else if k == "claims_locales" {
1915 if let Some(claims_locales) = v.as_array() {
1916 if !claims_locales.is_empty() {
1917 let mut locale_arr = vec![];
1918 for locale in claims_locales {
1919 if let Some(a) = locale.as_str() {
1920 locale_arr.push(a.to_string());
1921 }
1922 }
1923
1924 auth_params.claims_locales = Some(locale_arr);
1925 }
1926 }
1927
1928 if let Some(claims_locales) = v.as_str() {
1929 let locale_arr = vec![claims_locales.to_string()];
1930 auth_params.claims_locales = Some(locale_arr);
1931 }
1932 } else if k == "code_challenge_method" {
1933 if let Some(code_challenge_method) = v.as_str() {
1934 auth_params.code_challenge_method = Some(code_challenge_method.to_string());
1935 }
1936 } else if k == "code_challenge" {
1937 if let Some(code_challenge) = v.as_str() {
1938 auth_params.code_challenge = Some(code_challenge.to_string());
1939 }
1940 } else if k == "display" {
1941 if let Some(display) = v.as_str() {
1942 auth_params.display = Some(display.to_string());
1943 }
1944 } else if k == "id_token_hint" {
1945 if let Some(id_token_hint) = v.as_str() {
1946 auth_params.id_token_hint = Some(id_token_hint.to_string());
1947 }
1948 } else if k == "login_hint" {
1949 if let Some(login_hint) = v.as_str() {
1950 auth_params.login_hint = Some(login_hint.to_string());
1951 }
1952 } else if k == "max_age" {
1953 if let Some(max_age) = v.as_str() {
1954 auth_params.max_age = Some(max_age.to_string());
1955 }
1956 } else if k == "nonce" {
1957 if let Some(nonce) = v.as_str() {
1958 auth_params.nonce = Some(nonce.to_string());
1959 }
1960 } else if k == "prompt" {
1961 if let Some(prompt) = v.as_array() {
1962 if !prompt.is_empty() {
1963 let mut prompt_arr = vec![];
1964 for prompt in prompt {
1965 if let Some(a) = prompt.as_str() {
1966 prompt_arr.push(a.to_string());
1967 }
1968 }
1969
1970 auth_params.prompt = Some(prompt_arr);
1971 }
1972 }
1973
1974 if let Some(prompt) = v.as_str() {
1975 let prompt_arr = vec![prompt.to_string()];
1976 auth_params.prompt = Some(prompt_arr);
1977 }
1978 } else if k == "registration" {
1979 if let Some(registration) = v.as_str() {
1980 auth_params.registration = Some(registration.to_string());
1981 }
1982 } else if k == "request_uri" {
1983 if let Some(request_uri) = v.as_str() {
1984 auth_params.request_uri = Some(request_uri.to_string());
1985 }
1986 } else if k == "request" {
1987 if let Some(request) = v.as_str() {
1988 auth_params.request = Some(request.to_string());
1989 }
1990 } else if k == "resource" {
1991 if let Some(resource) = v.as_array() {
1992 if !resource.is_empty() {
1993 let mut resource_arr = vec![];
1994 for r in resource {
1995 if let Some(a) = r.as_str() {
1996 resource_arr.push(a.to_string());
1997 }
1998 }
1999
2000 auth_params.resource = Some(resource_arr);
2001 }
2002 }
2003
2004 if let Some(resource) = v.as_str() {
2005 let resource_arr = vec![resource.to_string()];
2006 auth_params.resource = Some(resource_arr);
2007 }
2008 } else if k == "response_mode" {
2009 if let Some(response_mode) = v.as_str() {
2010 auth_params.response_mode = Some(response_mode.to_string());
2011 }
2012 } else if k == "state" {
2013 if let Some(state) = v.as_str() {
2014 auth_params.state = Some(state.to_string());
2015 }
2016 } else if k == "ui_locales" {
2017 if let Some(ui_locales) = v.as_array() {
2018 if !ui_locales.is_empty() {
2019 let mut ui_locales_arr = vec![];
2020 for ui_locale in ui_locales {
2021 if let Some(a) = ui_locale.as_str() {
2022 ui_locales_arr.push(a.to_string());
2023 }
2024 }
2025
2026 auth_params.ui_locales = Some(ui_locales_arr);
2027 }
2028 }
2029
2030 if let Some(ui_locales) = v.as_str() {
2031 let locale_arr = vec![ui_locales.to_string()];
2032 auth_params.ui_locales = Some(locale_arr);
2033 }
2034 } else if let Some(other) = &mut auth_params.other {
2035 other.insert(k, get_serde_value_as_string(&v)?);
2036 } else {
2037 let mut other = HashMap::new();
2038 other.insert(k, get_serde_value_as_string(&v)?);
2039 auth_params.other = Some(other);
2040 }
2041 }
2042
2043 let body = self.authorization_params(auth_params);
2044
2045 let form_body: HashMap<String, String> = body.into();
2046
2047 let req = HttpRequest::new()
2048 .form(form_body)
2049 .expect_body(true)
2050 .expect_json_body(true);
2051
2052 let auth_post_params = AuthenticationPostParams {
2053 client_assertion_payload: extras
2054 .as_ref()
2055 .and_then(|x| x.client_assertion_payload.as_ref()),
2056 endpoint_auth_method: Some("token"),
2057 dpop: None,
2058 };
2059
2060 let res = self
2061 .authenticated_post_async("device_authorization", req, auth_post_params, http_client)
2062 .await?;
2063
2064 let device_res = res
2065 .body
2066 .as_ref()
2067 .and_then(|x| convert_json_to::<DeviceAuthorizationResponse>(x).ok())
2068 .ok_or(OidcClientError::new_type_error(
2069 &format!(
2070 "could not convert response body to device authorization response: {}",
2071 res.body.clone().unwrap_or_default()
2072 ),
2073 Some(res),
2074 ))?;
2075
2076 Ok(DeviceFlowHandle::new(
2077 self.clone(),
2078 device_res,
2079 extras,
2080 params.max_age,
2081 ))
2082 }
2083}