openid_client/client/
client_impl.rs

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
35/// Implementation for Client
36impl Client {
37    /// Returns if the client is fapi or not
38    pub fn is_fapi(&self) -> bool {
39        self.fapi.is_some()
40    }
41
42    /// Returns if the client is fapi 1 or not
43    pub fn is_fapi1(&self) -> bool {
44        self.fapi.as_ref().is_some_and(|x| matches!(x, Fapi::V1))
45    }
46
47    /// Returns if the client is fapi 2 or not
48    pub fn is_fapi2(&self) -> bool {
49        self.fapi.as_ref().is_some_and(|x| matches!(x, Fapi::V2))
50    }
51
52    /// # Authorization Url
53    /// Builds an authorization url with respect to the `parameters`
54    ///
55    /// - `parameters` - [AuthorizationParameters] : Customize the authorization request
56    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    /// # End Session Url
101    /// Builds an endsession url with respect to the `parameters`
102    ///
103    /// - `parameters` - [EndSessionParameters] : Customize the endsession url
104    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    /// # Authorization Post
190    /// Builds an authorization post page with respect to the `parameters`
191    ///
192    /// - `parameters` - [AuthorizationParameters] : Customize the authorization request
193    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    /// # Token Grant
239    /// Performs a grant at the token endpoint
240    ///
241    /// - `http_client` - The http client to make the request
242    /// - `params` - Token grant params
243    #[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    /// # OAuth Callback
309    /// Performs the callback for Authorization Server's authorization response.
310    ///
311    /// - `http_client` - The http client to make the request
312    /// - `params` - OAuth callback params
313    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) = &params.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) = &params.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    /// # Skip Max Age Check
562    /// When `skip_max_age_check` is set to true, Id Token's
563    /// Max age wont be validated
564    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    /// # Skip Nonce Check
569    /// When `skip_nonce_check` is set to true, Id token's
570    /// Nonce wont be validated
571    pub fn set_skip_nonce_check(&mut self, nonce_check: bool) {
572        self.skip_nonce_check = nonce_check;
573    }
574
575    /// # Set Clock Skew
576    /// It is possible the RP or OP environment has a system clock skew,
577    /// which can result in the error "JWT not active yet".
578    pub fn set_clock_skew_duration(&mut self, duration: Duration) {
579        self.clock_tolerance = duration;
580    }
581
582    /// # Callback
583    /// Performs the callback for Authorization Server's authorization response.
584    ///
585    /// - `http_cliet` - The http client to make request
586    /// - `params` - OpenId callback params
587    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) = &params.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 &params.parameters.other {
740                Some(o) => o.clone(),
741                None => HashMap::new(),
742            };
743
744            if let Some(state) = &params.parameters.state {
745                other_fields.insert("state".to_owned(), state.to_owned());
746            }
747
748            if let Some(code) = &params.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 &params.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) = &params.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 &params.parameters.other {
877            Some(o) => o.clone(),
878            None => HashMap::new(),
879        };
880
881        if let Some(state) = &params.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    /// # Introspect
926    /// Performs an introspection request at `Issuer::introspection_endpoint`
927    ///
928    /// - `http_client` : The http client to make the request
929    /// - `token` : The token to introspect
930    /// - `token_type_hint` : Type of the token passed in `token`. Usually `access_token` or `refresh_token`
931    /// - `extras`: See [IntrospectionExtras]
932    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    /// # Request Resource
993    /// Performs a request to fetch using the access token at `resource_url`.
994    ///
995    /// - `http_client` : The http client to make the request
996    /// - `params` : [RequestResourceParams]
997    #[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) = &params.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    /// # Callback Params
1084    /// Tries to convert the Url or a body string to [CallbackParams]
1085    ///
1086    /// - `incoming_url` : The full url of the request ([Url]). Use this param if the request is of the type GET
1087    /// - `incoming_body` : Incoming body. Use this param if the request is of the type POST
1088    ///
1089    /// > Only one of the above parameter is parsed.
1090    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    /// # Refresh Request
1147    /// Performs a Token Refresh request at Issuer's `token_endpoint`
1148    ///
1149    /// - `http_client`: The http client to make the request
1150    /// - `token_set` : [TokenSet] with refresh token that will be used to perform the request
1151    /// - `extras` : See [RefreshTokenExtras]
1152    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    /// # Revoke Token
1231    /// Performs a token revocation at Issuer's `revocation_endpoint`
1232    ///
1233    /// - `http_client` : The http client to make the request
1234    /// - `token` : The token to be revoked
1235    /// - `token_type_hint` : Hint to which type of token is being revoked
1236    /// - `extras` : See [RevokeExtras]
1237    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    /// # Userinfo
1302    /// Performs userinfo request at Issuer's `userinfo` endpoint.
1303    ///
1304    /// - `http_client` : The http client to make the request
1305    /// - `token_set` : [TokenSet] with `access_token` that will be used to perform the request
1306    /// - `options` : See [UserinfoOptions]
1307    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            // What?
1400            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    /// # Request Object
1558    ///
1559    /// Creates a request object for JAR
1560    ///
1561    /// - `http_client` : The http client to make the request
1562    /// - `request_object` : A [Value] which should be an object
1563    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()); // e_content_encryption
1676        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    /// # Pushed Authorization Request
1721    ///
1722    /// Performs a PAR on the `pushed_authorization_request_endpoint`
1723    ///
1724    /// - `http_client` : The http client to make the request
1725    /// - `parameters` : See [AuthorizationParameters]
1726    /// - `extras` : See [PushedAuthorizationRequestExtras]
1727    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    /// # Device Authorization Grant
1833    /// Performs a Device Authorization Grant at `device_authorization_request_endpoint`.
1834    ///
1835    /// - `http_client` - The http client to make the request
1836    /// - `params` - See [DeviceAuthorizationParams]
1837    /// - `extras` - See [DeviceAuthorizationExtras]
1838    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    /// # CIBA Authenticate
2097    /// Performs CIBA Authentication request at `backchannel_authentication_endpoint`
2098    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}