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