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}