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