graph_oauth/
oauth_serializer.rs

1use std::collections::btree_map::{BTreeMap, Entry};
2use std::collections::{BTreeSet, HashMap};
3use std::default::Default;
4use std::fmt;
5use std::fmt::Display;
6
7use url::form_urlencoded::Serializer;
8
9use graph_error::{AuthorizationFailure, IdentityResult};
10
11use crate::identity::{AsQuery, Prompt, ResponseType};
12use crate::strum::IntoEnumIterator;
13
14/// Fields that represent common OAuth credentials.
15#[derive(
16    Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Serialize, Deserialize, EnumIter,
17)]
18pub enum AuthParameter {
19    ClientId,
20    ClientSecret,
21    RedirectUri,
22    AuthorizationCode,
23    AccessToken,
24    RefreshToken,
25    ResponseMode,
26    State,
27    SessionState,
28    ResponseType,
29    GrantType,
30    Nonce,
31    Prompt,
32    IdToken,
33    Resource,
34    DomainHint,
35    Scope,
36    LoginHint,
37    ClientAssertion,
38    ClientAssertionType,
39    CodeVerifier,
40    CodeChallenge,
41    CodeChallengeMethod,
42    AdminConsent,
43    Username,
44    Password,
45    DeviceCode,
46}
47
48impl AuthParameter {
49    pub fn alias(self) -> &'static str {
50        match self {
51            AuthParameter::ClientId => "client_id",
52            AuthParameter::ClientSecret => "client_secret",
53            AuthParameter::RedirectUri => "redirect_uri",
54            AuthParameter::AuthorizationCode => "code",
55            AuthParameter::AccessToken => "access_token",
56            AuthParameter::RefreshToken => "refresh_token",
57            AuthParameter::ResponseMode => "response_mode",
58            AuthParameter::ResponseType => "response_type",
59            AuthParameter::State => "state",
60            AuthParameter::SessionState => "session_state",
61            AuthParameter::GrantType => "grant_type",
62            AuthParameter::Nonce => "nonce",
63            AuthParameter::Prompt => "prompt",
64            AuthParameter::IdToken => "id_token",
65            AuthParameter::Resource => "resource",
66            AuthParameter::DomainHint => "domain_hint",
67            AuthParameter::Scope => "scope",
68            AuthParameter::LoginHint => "login_hint",
69            AuthParameter::ClientAssertion => "client_assertion",
70            AuthParameter::ClientAssertionType => "client_assertion_type",
71            AuthParameter::CodeVerifier => "code_verifier",
72            AuthParameter::CodeChallenge => "code_challenge",
73            AuthParameter::CodeChallengeMethod => "code_challenge_method",
74            AuthParameter::AdminConsent => "admin_consent",
75            AuthParameter::Username => "username",
76            AuthParameter::Password => "password",
77            AuthParameter::DeviceCode => "device_code",
78        }
79    }
80
81    fn is_debug_redacted(&self) -> bool {
82        matches!(
83            self,
84            AuthParameter::ClientId
85                | AuthParameter::ClientSecret
86                | AuthParameter::AccessToken
87                | AuthParameter::RefreshToken
88                | AuthParameter::IdToken
89                | AuthParameter::CodeVerifier
90                | AuthParameter::CodeChallenge
91                | AuthParameter::Password
92        )
93    }
94}
95
96impl Display for AuthParameter {
97    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
98        write!(f, "{}", self.alias())
99    }
100}
101
102impl AsRef<str> for AuthParameter {
103    fn as_ref(&self) -> &'static str {
104        self.alias()
105    }
106}
107
108/// Serializer for query/x-www-form-urlencoded OAuth requests.
109///
110/// OAuth Serializer for query/form serialization that supports the OAuth 2.0 and OpenID
111/// Connect protocols on Microsoft identity platform.
112///
113/// # Example
114/// ```
115/// use graph_oauth::extensions::AuthSerializer;
116/// let oauth = AuthSerializer::new();
117/// ```
118#[derive(Default, Clone, Eq, PartialEq, Serialize, Deserialize)]
119pub struct AuthSerializer {
120    scopes: BTreeSet<String>,
121    parameters: BTreeMap<String, String>,
122    log_pii: bool,
123}
124
125impl AuthSerializer {
126    /// Create a new OAuth instance.
127    ///
128    /// # Example
129    /// ```
130    /// use graph_oauth::extensions::AuthSerializer;
131    ///
132    /// let mut oauth = AuthSerializer::new();
133    /// ```
134    pub fn new() -> AuthSerializer {
135        AuthSerializer {
136            scopes: BTreeSet::new(),
137            parameters: BTreeMap::new(),
138            log_pii: false,
139        }
140    }
141
142    /// Insert oauth credentials using the OAuthParameter enum.
143    /// This method is used internally for each of the setter methods.
144    /// Callers can optionally use this method to set credentials instead
145    /// of the individual setter methods.
146    ///
147    /// # Example
148    /// ```
149    /// # use graph_oauth::extensions::AuthSerializer;
150    /// # use graph_oauth::extensions::AuthParameter;
151    /// # let mut oauth = AuthSerializer::new();
152    /// oauth.insert(AuthParameter::AuthorizationCode, "code");
153    /// assert!(oauth.contains(AuthParameter::AuthorizationCode));
154    /// println!("{:#?}", oauth.get(AuthParameter::AuthorizationCode));
155    /// ```
156    pub fn insert<V: ToString>(&mut self, oac: AuthParameter, value: V) -> &mut AuthSerializer {
157        self.parameters.insert(oac.to_string(), value.to_string());
158        self
159    }
160
161    /// Insert and OAuth credential using the entry trait and
162    /// returning the credential. This internally calls
163    /// `entry.(OAuthParameter).or_insret_with(value)`.
164    ///
165    /// # Example
166    /// ```
167    /// # use graph_oauth::extensions::AuthSerializer;
168    /// # use graph_oauth::extensions::AuthParameter;
169    /// # let mut oauth = AuthSerializer::new();
170    /// let entry = oauth.entry_with(AuthParameter::AuthorizationCode, "code");
171    /// assert_eq!(entry, "code")
172    /// ```
173    pub fn entry_with<V: ToString>(&mut self, oac: AuthParameter, value: V) -> &mut String {
174        self.parameters
175            .entry(oac.alias().to_string())
176            .or_insert_with(|| value.to_string())
177    }
178
179    /// A view into a single entry in a map, which may either be vacant or occupied.
180    ///
181    /// This `enum` is constructed from the [`entry`] method on [`BTreeMap`].
182    ///
183    /// [`entry`]: BTreeMap::entry
184    pub fn entry<V: ToString>(&mut self, oac: AuthParameter) -> Entry<String, String> {
185        self.parameters.entry(oac.alias().to_string())
186    }
187
188    /// Get a previously set credential.
189    ///
190    /// # Example
191    /// ```
192    /// # use graph_oauth::extensions::AuthSerializer;
193    /// # use graph_oauth::extensions::AuthParameter;
194    /// # let mut oauth = AuthSerializer::new();
195    /// oauth.authorization_code("code");
196    /// let code = oauth.get(AuthParameter::AuthorizationCode);
197    /// assert_eq!("code", code.unwrap().as_str());
198    /// ```
199    pub fn get(&self, oac: AuthParameter) -> Option<String> {
200        self.parameters.get(oac.alias()).cloned()
201    }
202
203    /// Check if an OAuth credential has already been set.
204    ///
205    /// # Example
206    /// ```
207    /// # use graph_oauth::extensions::AuthSerializer;
208    /// # use graph_oauth::extensions::AuthParameter;
209    /// # let mut oauth = AuthSerializer::new();
210    /// println!("{:#?}", oauth.contains(AuthParameter::Nonce));
211    /// ```
212    pub fn contains(&self, t: AuthParameter) -> bool {
213        if t == AuthParameter::Scope {
214            return !self.scopes.is_empty();
215        }
216        self.parameters.contains_key(t.alias())
217    }
218
219    pub fn contains_key(&self, key: &str) -> bool {
220        self.parameters.contains_key(key)
221    }
222
223    /// Remove a field from OAuth.
224    ///
225    /// # Example
226    /// ```
227    /// # use graph_oauth::extensions::AuthSerializer;
228    /// # use graph_oauth::extensions::AuthParameter;
229    /// # let mut oauth = AuthSerializer::new();
230    /// oauth.client_id("client_id");
231    ///
232    /// assert_eq!(oauth.contains(AuthParameter::ClientId), true);
233    /// oauth.remove(AuthParameter::ClientId);
234    ///
235    /// assert_eq!(oauth.contains(AuthParameter::ClientId), false);
236    /// ```
237    pub fn remove(&mut self, oac: AuthParameter) -> &mut AuthSerializer {
238        self.parameters.remove(oac.alias());
239        self
240    }
241
242    /// Set the client id for an OAuth request.
243    ///
244    /// # Example
245    /// ```
246    /// # use graph_oauth::extensions::AuthSerializer;
247    /// # use graph_oauth::extensions::AuthParameter;
248    /// # let mut oauth = AuthSerializer::new();
249    /// oauth.client_id("client_id");
250    /// ```
251    pub fn client_id(&mut self, value: &str) -> &mut AuthSerializer {
252        self.insert(AuthParameter::ClientId, value)
253    }
254
255    /// Set the state for an OAuth request.
256    ///
257    /// # Example
258    /// ```
259    /// # use graph_oauth::extensions::AuthSerializer;
260    /// # use graph_oauth::extensions::AuthParameter;
261    /// # let mut oauth = AuthSerializer::new();
262    /// oauth.state("1234");
263    /// ```
264    pub fn state(&mut self, value: &str) -> &mut AuthSerializer {
265        self.insert(AuthParameter::State, value)
266    }
267
268    /// Set the client secret for an OAuth request.
269    ///
270    /// # Example
271    /// ```
272    /// # use graph_oauth::extensions::AuthSerializer;
273    /// # let mut oauth = AuthSerializer::new();
274    /// oauth.client_secret("client_secret");
275    /// ```
276    pub fn client_secret(&mut self, value: &str) -> &mut AuthSerializer {
277        self.insert(AuthParameter::ClientSecret, value)
278    }
279
280    /// Set the redirect url of a request
281    ///
282    /// # Example
283    /// ```
284    /// # use graph_oauth::extensions::AuthSerializer;
285    /// # let mut oauth = AuthSerializer::new();
286    /// oauth.redirect_uri("https://localhost:8888/redirect");
287    /// ```
288    pub fn redirect_uri(&mut self, value: &str) -> &mut AuthSerializer {
289        self.insert(AuthParameter::RedirectUri, value)
290    }
291
292    /// Set the access code.
293    ///
294    /// # Example
295    /// ```
296    /// # use graph_oauth::extensions::AuthSerializer;
297    /// # let mut serializer = AuthSerializer::new();
298    /// serializer.authorization_code("LDSF[POK43");
299    /// ```
300    pub fn authorization_code(&mut self, value: &str) -> &mut AuthSerializer {
301        self.insert(AuthParameter::AuthorizationCode, value)
302    }
303
304    /// Set the response mode.
305    ///
306    /// # Example
307    /// ```
308    /// # use graph_oauth::extensions::AuthSerializer;
309    /// # let mut serializer = AuthSerializer::new();
310    /// serializer.response_mode("query");
311    /// ```
312    pub fn response_mode(&mut self, value: &str) -> &mut AuthSerializer {
313        self.insert(AuthParameter::ResponseMode, value)
314    }
315
316    /// Set the response type.
317    ///
318    /// # Example
319    /// ```
320    /// # use graph_oauth::extensions::AuthSerializer;
321    /// # let mut oauth = AuthSerializer::new();
322    /// oauth.response_type("token");
323    /// ```
324    pub fn response_type<T: ToString>(&mut self, value: T) -> &mut AuthSerializer {
325        self.insert(AuthParameter::ResponseType, value)
326    }
327
328    pub fn response_types(
329        &mut self,
330        value: std::collections::btree_set::Iter<'_, ResponseType>,
331    ) -> &mut AuthSerializer {
332        self.insert(AuthParameter::ResponseType, value.as_query())
333    }
334
335    /// Set the nonce.
336    ///
337    /// # Example
338    /// ```
339    /// # use graph_oauth::extensions::AuthSerializer;
340    ///
341    /// # let mut oauth = AuthSerializer::new();
342    /// oauth.nonce("1234");
343    /// ```
344    pub fn nonce(&mut self, value: &str) -> &mut AuthSerializer {
345        self.insert(AuthParameter::Nonce, value)
346    }
347
348    /// Set the prompt for open id.
349    ///
350    /// # Example
351    /// ```
352    /// # use graph_oauth::extensions::AuthSerializer;
353    ///
354    /// # let mut oauth = AuthSerializer::new();
355    /// oauth.prompt("login");
356    /// ```
357    pub fn prompt(&mut self, value: &str) -> &mut AuthSerializer {
358        self.insert(AuthParameter::Prompt, value)
359    }
360
361    pub fn prompts(&mut self, value: &[Prompt]) -> &mut AuthSerializer {
362        self.insert(AuthParameter::Prompt, value.to_vec().as_query())
363    }
364
365    /// Set the session state.
366    ///
367    /// # Example
368    /// ```
369    /// # use graph_oauth::extensions::AuthSerializer;
370    /// # let mut oauth = AuthSerializer::new();
371    /// oauth.session_state("session-state");
372    /// ```
373    pub fn session_state(&mut self, value: &str) -> &mut AuthSerializer {
374        self.insert(AuthParameter::SessionState, value)
375    }
376
377    /// Set the grant_type.
378    ///
379    /// # Example
380    /// ```
381    /// # use graph_oauth::extensions::AuthSerializer;
382    /// # let mut oauth = AuthSerializer::new();
383    /// oauth.grant_type("token");
384    /// ```
385    pub fn grant_type(&mut self, value: &str) -> &mut AuthSerializer {
386        self.insert(AuthParameter::GrantType, value)
387    }
388
389    /// Set the resource.
390    ///
391    /// # Example
392    /// ```
393    /// # use graph_oauth::extensions::AuthSerializer;
394    /// # let mut oauth = AuthSerializer::new();
395    /// oauth.resource("resource");
396    /// ```
397    pub fn resource(&mut self, value: &str) -> &mut AuthSerializer {
398        self.insert(AuthParameter::Resource, value)
399    }
400
401    /// Set the code verifier.
402    ///
403    /// # Example
404    /// ```
405    /// # use graph_oauth::extensions::AuthSerializer;
406    /// # let mut oauth = AuthSerializer::new();
407    /// oauth.code_verifier("code_verifier");
408    /// ```
409    pub fn code_verifier(&mut self, value: &str) -> &mut AuthSerializer {
410        self.insert(AuthParameter::CodeVerifier, value)
411    }
412
413    /// Set the domain hint.
414    ///
415    /// # Example
416    /// ```
417    /// # use graph_oauth::extensions::AuthSerializer;
418    /// # let mut oauth = AuthSerializer::new();
419    /// oauth.domain_hint("domain_hint");
420    /// ```
421    pub fn domain_hint(&mut self, value: &str) -> &mut AuthSerializer {
422        self.insert(AuthParameter::DomainHint, value)
423    }
424
425    /// Set the code challenge.
426    ///
427    /// # Example
428    /// ```
429    /// # use graph_oauth::extensions::AuthSerializer;
430    /// # let mut oauth = AuthSerializer::new();
431    /// oauth.code_challenge("code_challenge");
432    /// ```
433    pub fn code_challenge(&mut self, value: &str) -> &mut AuthSerializer {
434        self.insert(AuthParameter::CodeChallenge, value)
435    }
436
437    /// Set the code challenge method.
438    ///
439    /// # Example
440    /// ```
441    /// # use graph_oauth::extensions::AuthSerializer;
442    /// # let mut oauth = AuthSerializer::new();
443    /// oauth.code_challenge_method("code_challenge_method");
444    /// ```
445    pub fn code_challenge_method(&mut self, value: &str) -> &mut AuthSerializer {
446        self.insert(AuthParameter::CodeChallengeMethod, value)
447    }
448
449    /// Set the login hint.
450    ///
451    /// # Example
452    /// ```
453    /// # use graph_oauth::extensions::AuthSerializer;
454    /// # let mut oauth = AuthSerializer::new();
455    /// oauth.login_hint("login_hint");
456    /// ```
457    pub fn login_hint(&mut self, value: &str) -> &mut AuthSerializer {
458        self.insert(AuthParameter::LoginHint, value)
459    }
460
461    /// Set the client assertion.
462    ///
463    /// # Example
464    /// ```
465    /// # use graph_oauth::extensions::AuthSerializer;
466    /// # let mut oauth = AuthSerializer::new();
467    /// oauth.client_assertion("client_assertion");
468    /// ```
469    pub fn client_assertion(&mut self, value: &str) -> &mut AuthSerializer {
470        self.insert(AuthParameter::ClientAssertion, value)
471    }
472
473    /// Set the client assertion type.
474    ///
475    /// # Example
476    /// ```
477    /// # use graph_oauth::extensions::AuthSerializer;
478    /// # let mut oauth = AuthSerializer::new();
479    /// oauth.client_assertion_type("client_assertion_type");
480    /// ```
481    pub fn client_assertion_type(&mut self, value: &str) -> &mut AuthSerializer {
482        self.insert(AuthParameter::ClientAssertionType, value)
483    }
484
485    /// Set the redirect uri that user will be redirected to after logging out.
486    ///
487    /// # Example
488    /// ```
489    /// # use graph_oauth::extensions::{AuthSerializer, AuthParameter};
490    /// # let mut oauth = AuthSerializer::new();
491    /// oauth.username("user");
492    /// assert!(oauth.contains(AuthParameter::Username))
493    /// ```
494    pub fn username(&mut self, value: &str) -> &mut AuthSerializer {
495        self.insert(AuthParameter::Username, value)
496    }
497
498    /// Set the redirect uri that user will be redirected to after logging out.
499    ///
500    /// # Example
501    /// ```
502    /// # use graph_oauth::extensions::{AuthSerializer, AuthParameter};
503    /// # let mut oauth = AuthSerializer::new();
504    /// oauth.password("user");
505    /// assert!(oauth.contains(AuthParameter::Password))
506    /// ```
507    pub fn password(&mut self, value: &str) -> &mut AuthSerializer {
508        self.insert(AuthParameter::Password, value)
509    }
510
511    pub fn refresh_token(&mut self, value: &str) -> &mut AuthSerializer {
512        self.insert(AuthParameter::RefreshToken, value)
513    }
514
515    /// Set the device code for the device authorization flow.
516    ///
517    /// # Example
518    /// ```
519    /// # use graph_oauth::extensions::{AuthSerializer, AuthParameter};
520    /// # let mut oauth = AuthSerializer::new();
521    /// oauth.device_code("device_code");
522    /// assert!(oauth.contains(AuthParameter::DeviceCode))
523    /// ```
524    pub fn device_code(&mut self, value: &str) -> &mut AuthSerializer {
525        self.insert(AuthParameter::DeviceCode, value)
526    }
527
528    /// Add a scope' for the OAuth URL.
529    ///
530    /// # Example
531    /// ```
532    /// # use graph_oauth::extensions::AuthSerializer;
533    /// # let mut oauth = AuthSerializer::new();
534    ///
535    /// oauth.add_scope("Sites.Read")
536    ///     .add_scope("Sites.ReadWrite")
537    ///     .add_scope("Sites.ReadWrite.All");
538    /// assert_eq!(oauth.join_scopes(" "), "Sites.Read Sites.ReadWrite Sites.ReadWrite.All");
539    /// ```
540    pub fn add_scope<T: ToString>(&mut self, scope: T) -> &mut AuthSerializer {
541        self.scopes.insert(scope.to_string());
542        self
543    }
544
545    /// Get the scopes.
546    ///
547    /// # Example
548    /// ```
549    /// # use graph_oauth::extensions::AuthSerializer;
550    /// let mut oauth = AuthSerializer::new();
551    /// oauth.add_scope("Files.Read");
552    /// oauth.add_scope("Files.ReadWrite");
553    ///
554    /// let scopes = oauth.get_scopes();
555    /// assert!(scopes.contains("Files.Read"));
556    /// assert!(scopes.contains("Files.ReadWrite"));
557    /// ```
558    pub fn get_scopes(&self) -> &BTreeSet<String> {
559        &self.scopes
560    }
561
562    /// Join scopes.
563    ///
564    /// # Example
565    /// ```
566    /// # use graph_oauth::extensions::AuthSerializer;
567    /// # let mut oauth = AuthSerializer::new();
568    ///
569    /// // the scopes take a separator just like Vec join.
570    ///  let s = oauth.join_scopes(" ");
571    /// println!("{:#?}", s);
572    /// ```
573    pub fn join_scopes(&self, sep: &str) -> String {
574        self.scopes
575            .iter()
576            .map(|s| &**s)
577            .collect::<Vec<&str>>()
578            .join(sep)
579    }
580
581    /// Set scope. Overriding all previous scope values.
582    ///
583    /// # Example
584    /// ```
585    /// # use graph_oauth::extensions::AuthSerializer;
586    /// # use std::collections::HashSet;
587    /// # let mut oauth = AuthSerializer::new();
588    ///
589    /// let scopes = vec!["Files.Read", "Files.ReadWrite"];
590    /// oauth.extend_scopes(&scopes);
591    ///
592    /// assert_eq!(oauth.join_scopes(" "), "Files.Read Files.ReadWrite");
593    /// ```
594    pub fn set_scope<T: ToString, I: IntoIterator<Item = T>>(&mut self, iter: I) -> &mut Self {
595        self.scopes = iter.into_iter().map(|s| s.to_string()).collect();
596        self
597    }
598
599    /// Extend scopes.
600    ///
601    /// # Example
602    /// ```
603    /// # use graph_oauth::extensions::AuthSerializer;
604    /// # use std::collections::HashSet;
605    /// # let mut oauth = AuthSerializer::new();
606    ///
607    /// let scopes = vec!["Files.Read", "Files.ReadWrite"];
608    /// oauth.extend_scopes(&scopes);
609    ///
610    /// assert_eq!(oauth.join_scopes(" "), "Files.Read Files.ReadWrite");
611    /// ```
612    pub fn extend_scopes<T: ToString, I: IntoIterator<Item = T>>(&mut self, iter: I) -> &mut Self {
613        self.scopes.extend(iter.into_iter().map(|s| s.to_string()));
614        self
615    }
616
617    /// Check if OAuth contains a specific scope.
618    ///
619    /// # Example
620    /// ```
621    /// # use graph_oauth::extensions::AuthSerializer;
622    /// # let mut oauth = AuthSerializer::new();
623    ///
624    /// oauth.add_scope("Files.Read");
625    /// assert_eq!(oauth.contains_scope("Files.Read"), true);
626    ///
627    /// // Or using static scopes
628    /// oauth.add_scope("File.ReadWrite");
629    /// assert!(oauth.contains_scope("File.ReadWrite"));
630    /// ```
631    pub fn contains_scope<T: ToString>(&self, scope: T) -> bool {
632        self.scopes.contains(&scope.to_string())
633    }
634}
635
636impl AuthSerializer {
637    fn try_as_tuple(&self, oac: &AuthParameter) -> IdentityResult<(String, String)> {
638        if oac.eq(&AuthParameter::Scope) {
639            if self.scopes.is_empty() {
640                return Err(AuthorizationFailure::required(oac));
641            }
642            Ok((oac.alias().to_owned(), self.join_scopes(" ")))
643        } else {
644            Ok((
645                oac.alias().to_owned(),
646                self.get(*oac).ok_or(AuthorizationFailure::required(oac))?,
647            ))
648        }
649    }
650
651    pub fn encode_query(
652        &mut self,
653        optional_fields: Vec<AuthParameter>,
654        required_fields: Vec<AuthParameter>,
655    ) -> IdentityResult<String> {
656        let mut serializer = Serializer::new(String::new());
657        for parameter in required_fields {
658            if parameter.alias().eq("scope") {
659                if self.scopes.is_empty() {
660                    return AuthorizationFailure::result::<String>(parameter.alias());
661                } else {
662                    serializer.append_pair("scope", self.join_scopes(" ").as_str());
663                }
664            } else {
665                let value = self
666                    .get(parameter)
667                    .ok_or(AuthorizationFailure::required(parameter))?;
668
669                serializer.append_pair(parameter.alias(), value.as_str());
670            }
671        }
672
673        for parameter in optional_fields {
674            if parameter.alias().eq("scope") && !self.scopes.is_empty() {
675                serializer.append_pair("scope", self.join_scopes(" ").as_str());
676            } else if let Some(val) = self.get(parameter) {
677                serializer.append_pair(parameter.alias(), val.as_str());
678            }
679        }
680
681        Ok(serializer.finish())
682    }
683
684    pub fn as_credential_map(
685        &mut self,
686        optional_fields: Vec<AuthParameter>,
687        required_fields: Vec<AuthParameter>,
688    ) -> IdentityResult<HashMap<String, String>> {
689        let mut required_map = required_fields
690            .iter()
691            .map(|oac| self.try_as_tuple(oac))
692            .collect::<IdentityResult<HashMap<String, String>>>()?;
693
694        let optional_map: HashMap<String, String> = optional_fields
695            .iter()
696            .flat_map(|oac| self.try_as_tuple(oac))
697            .collect();
698
699        required_map.extend(optional_map);
700        Ok(required_map)
701    }
702}
703
704/// Extend the OAuth credentials.
705///
706/// # Example
707/// ```
708/// # use graph_oauth::extensions::{AuthSerializer, AuthParameter};
709/// # use std::collections::HashMap;
710/// # let mut oauth = AuthSerializer::new();
711/// let mut map: HashMap<AuthParameter, &str> = HashMap::new();
712/// map.insert(AuthParameter::ClientId, "client_id");
713/// map.insert(AuthParameter::ClientSecret, "client_secret");
714///
715/// oauth.extend(map);
716/// # assert_eq!(oauth.get(AuthParameter::ClientId), Some("client_id".to_string()));
717/// # assert_eq!(oauth.get(AuthParameter::ClientSecret), Some("client_secret".to_string()));
718/// ```
719impl<V: ToString> Extend<(AuthParameter, V)> for AuthSerializer {
720    fn extend<I: IntoIterator<Item = (AuthParameter, V)>>(&mut self, iter: I) {
721        iter.into_iter().for_each(|entry| {
722            self.insert(entry.0, entry.1);
723        });
724    }
725}
726
727impl fmt::Debug for AuthSerializer {
728    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
729        let mut map_debug: BTreeMap<&str, &str> = BTreeMap::new();
730        for (key, value) in self.parameters.iter() {
731            if self.log_pii {
732                map_debug.insert(key.as_str(), value.as_str());
733            } else if let Some(oac) = AuthParameter::iter()
734                .find(|oac| oac.alias().eq(key.as_str()) && oac.is_debug_redacted())
735            {
736                map_debug.insert(oac.alias(), "[REDACTED]");
737            } else {
738                map_debug.insert(key.as_str(), value.as_str());
739            }
740        }
741
742        f.debug_struct("OAuthSerializer")
743            .field("credentials", &map_debug)
744            .field("scopes", &self.scopes)
745            .finish()
746    }
747}