Skip to main content

aep_core/
model.rs

1use std::{collections::BTreeMap, fmt};
2
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5
6use crate::validation::deserialize_optional_non_null;
7
8pub type AdditionalMembers = BTreeMap<String, Value>;
9
10macro_rules! closed_string_enum {
11    ($name:ident { $($variant:ident => $value:literal),+ $(,)? }) => {
12        #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
13        pub enum $name {
14            $(#[serde(rename = $value)] $variant),+
15        }
16
17        impl $name {
18            pub const fn as_str(self) -> &'static str {
19                match self {
20                    $(Self::$variant => $value),+
21                }
22            }
23        }
24    };
25}
26
27macro_rules! extensible_string_enum {
28    ($name:ident { $($variant:ident => $value:literal),+ $(,)? }) => {
29        #[derive(Clone, Debug, Eq, Hash, PartialEq)]
30        pub enum $name {
31            $($variant),+,
32            Other(String),
33        }
34
35        impl $name {
36            pub fn as_str(&self) -> &str {
37                match self {
38                    $(Self::$variant => $value),+,
39                    Self::Other(value) => value,
40                }
41            }
42        }
43
44        impl From<&str> for $name {
45            fn from(value: &str) -> Self {
46                match value {
47                    $($value => Self::$variant),+,
48                    other => Self::Other(other.to_owned()),
49                }
50            }
51        }
52
53        impl From<String> for $name {
54            fn from(value: String) -> Self {
55                Self::from(value.as_str())
56            }
57        }
58
59        impl Serialize for $name {
60            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
61            where
62                S: serde::Serializer,
63            {
64                serializer.serialize_str(self.as_str())
65            }
66        }
67
68        impl<'de> Deserialize<'de> for $name {
69            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
70            where
71                D: serde::Deserializer<'de>,
72            {
73                Ok(Self::from(String::deserialize(deserializer)?))
74            }
75        }
76    };
77}
78
79extensible_string_enum!(Command {
80    Inspect => "inspect",
81    Enroll => "enroll",
82    Grant => "grant",
83    Revoke => "revoke",
84    Status => "status",
85});
86
87closed_string_enum!(AssertionOperation {
88    Enroll => "enroll",
89    Grant => "grant",
90    Revoke => "revoke",
91    Status => "status",
92    Authenticate => "authenticate",
93});
94
95extensible_string_enum!(SigningAlgorithm {
96    EdDsa => "EdDSA",
97    Es256 => "ES256",
98});
99
100extensible_string_enum!(GrantType {
101    OAuthBearer => "oauth-bearer",
102    ApiKey => "api-key",
103    Basic => "basic",
104});
105
106extensible_string_enum!(AuthenticationMethod {
107    AepJwt => "aep-jwt",
108    OAuthBearer => "oauth-bearer",
109    ApiKey => "api-key",
110    Basic => "basic",
111});
112
113extensible_string_enum!(Binding { Http => "http" });
114extensible_string_enum!(IdentityMethod { DidWeb => "did:web" });
115
116extensible_string_enum!(ClaimName {
117    ContactAddressPrimary => "contact.address.primary",
118    ContactEmail => "contact.email",
119    ContactMobile => "contact.mobile",
120    PersonBirthdate => "person.birthdate",
121    PersonFirstName => "person.first_name",
122    PersonLastName => "person.last_name",
123    PersonUsername => "person.username",
124});
125
126extensible_string_enum!(ErrorCode {
127    EnrollmentFailed => "enrollment_failed",
128    InvalidRequest => "invalid_request",
129    NotRecognized => "not_recognized",
130    IdentitySuspended => "identity_suspended",
131    IdentityTerminated => "identity_terminated",
132    IdentityUnavailable => "identity_unavailable",
133    VerificationPending => "verification_pending",
134    VerificationTimeout => "verification_timeout",
135    RequirementsUnmet => "requirements_unmet",
136    RateLimited => "rate_limited",
137    UnsupportedGrantType => "unsupported_grant_type",
138    IdempotencyConflict => "idempotency_conflict",
139    AuthenticationRequired => "authentication_required",
140    UnsupportedAuthenticationMethod => "unsupported_authentication_method",
141    InsufficientScope => "insufficient_scope",
142});
143
144closed_string_enum!(EnrollmentDecisionStatus {
145    Active => "active",
146    Pending => "pending",
147    Rejected => "rejected",
148});
149
150pub type EnrollmentStatus = EnrollmentDecisionStatus;
151
152closed_string_enum!(AgentStatus {
153    Active => "active",
154    Pending => "pending",
155    Rejected => "rejected",
156    Suspended => "suspended",
157    Terminated => "terminated",
158    Unavailable => "unavailable",
159});
160
161closed_string_enum!(StringBoolean { True => "true", False => "false" });
162closed_string_enum!(OpenApiTrailingSlash { Strict => "strict", Equivalent => "equivalent" });
163closed_string_enum!(AuthorizationCarrier {
164    Standard => "Authorization",
165    Dedicated => "AEP-Authorization",
166});
167closed_string_enum!(CredentialScheme { Aep => "AEP", Bearer => "Bearer", Basic => "Basic" });
168
169#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
170#[serde(deny_unknown_fields)]
171pub struct Authentication {
172    pub methods: Vec<AuthenticationMethod>,
173}
174
175#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
176pub struct Bindings {
177    pub supported: Vec<Binding>,
178    #[serde(flatten)]
179    pub additional: AdditionalMembers,
180}
181
182#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
183pub struct InspectClaims {
184    #[serde(default, skip_serializing_if = "Vec::is_empty")]
185    pub required: Vec<ClaimName>,
186    #[serde(default, skip_serializing_if = "Vec::is_empty")]
187    pub preferred: Vec<ClaimName>,
188    #[serde(default, skip_serializing_if = "Vec::is_empty")]
189    pub optional: Vec<ClaimName>,
190    #[serde(flatten)]
191    pub additional: AdditionalMembers,
192}
193
194#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
195pub struct GrantTypeConfig {
196    #[serde(
197        default,
198        deserialize_with = "deserialize_optional_non_null",
199        skip_serializing_if = "Option::is_none"
200    )]
201    pub supports_per_credential_revoke: Option<StringBoolean>,
202    #[serde(flatten)]
203    pub additional: AdditionalMembers,
204}
205
206#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
207pub struct Commands {
208    pub supported: Vec<Command>,
209    #[serde(default, skip_serializing_if = "Vec::is_empty")]
210    pub grant_types: Vec<GrantType>,
211    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
212    pub grant_types_config: BTreeMap<String, GrantTypeConfig>,
213    #[serde(flatten)]
214    pub additional: AdditionalMembers,
215}
216
217#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
218pub struct CoreConfiguration {
219    pub signing_algorithms: Vec<SigningAlgorithm>,
220    #[serde(flatten)]
221    pub additional: AdditionalMembers,
222}
223
224#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
225pub struct Extensions {
226    #[serde(default, skip_serializing_if = "Vec::is_empty")]
227    pub supported: Vec<String>,
228    #[serde(flatten)]
229    pub additional: AdditionalMembers,
230}
231
232#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
233#[serde(deny_unknown_fields)]
234pub struct OpenApiPathMatching {
235    pub trailing_slash: OpenApiTrailingSlash,
236}
237
238#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
239#[serde(deny_unknown_fields)]
240pub struct OpenApiReference {
241    pub url: String,
242    pub path_matching: OpenApiPathMatching,
243}
244
245#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
246pub struct HttpConfiguration {
247    #[serde(
248        default,
249        deserialize_with = "deserialize_optional_non_null",
250        skip_serializing_if = "Option::is_none"
251    )]
252    pub endpoint_base: Option<String>,
253    #[serde(
254        default,
255        deserialize_with = "deserialize_optional_non_null",
256        skip_serializing_if = "Option::is_none"
257    )]
258    pub openapi: Option<OpenApiReference>,
259    #[serde(flatten)]
260    pub additional: AdditionalMembers,
261}
262
263#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
264pub struct Identity {
265    pub methods: Vec<IdentityMethod>,
266    #[serde(flatten)]
267    pub additional: AdditionalMembers,
268}
269
270#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
271pub struct ServiceIdentity {
272    pub did: String,
273    #[serde(flatten)]
274    pub additional: AdditionalMembers,
275}
276
277#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
278pub struct InspectDocument {
279    pub aep_version: String,
280    #[serde(
281        default,
282        deserialize_with = "deserialize_optional_non_null",
283        skip_serializing_if = "Option::is_none"
284    )]
285    pub authentication: Option<Authentication>,
286    pub bindings: Bindings,
287    #[serde(
288        default,
289        deserialize_with = "deserialize_optional_non_null",
290        skip_serializing_if = "Option::is_none"
291    )]
292    pub claims: Option<InspectClaims>,
293    pub commands: Commands,
294    pub core: CoreConfiguration,
295    #[serde(
296        default,
297        deserialize_with = "deserialize_optional_non_null",
298        skip_serializing_if = "Option::is_none"
299    )]
300    pub extensions: Option<Extensions>,
301    pub http: HttpConfiguration,
302    pub identity: Identity,
303    pub service: ServiceIdentity,
304    #[serde(flatten)]
305    pub additional: AdditionalMembers,
306}
307
308#[derive(Clone, Default, Deserialize, PartialEq, Serialize)]
309pub struct ContactAddressPrimary {
310    #[serde(
311        default,
312        deserialize_with = "deserialize_optional_non_null",
313        skip_serializing_if = "Option::is_none"
314    )]
315    pub city: Option<String>,
316    pub country: String,
317    pub first_name: String,
318    pub last_name: String,
319    pub line1: String,
320    #[serde(
321        default,
322        deserialize_with = "deserialize_optional_non_null",
323        skip_serializing_if = "Option::is_none"
324    )]
325    pub line2: Option<String>,
326    #[serde(
327        default,
328        deserialize_with = "deserialize_optional_non_null",
329        skip_serializing_if = "Option::is_none"
330    )]
331    pub line3: Option<String>,
332    #[serde(
333        default,
334        deserialize_with = "deserialize_optional_non_null",
335        skip_serializing_if = "Option::is_none"
336    )]
337    pub postcode: Option<String>,
338    #[serde(
339        default,
340        deserialize_with = "deserialize_optional_non_null",
341        skip_serializing_if = "Option::is_none"
342    )]
343    pub region: Option<String>,
344    #[serde(flatten)]
345    pub additional: AdditionalMembers,
346}
347
348impl fmt::Debug for ContactAddressPrimary {
349    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
350        formatter.write_str("ContactAddressPrimary([REDACTED])")
351    }
352}
353
354#[derive(Clone, Default, Deserialize, PartialEq, Serialize)]
355pub struct ClaimValues {
356    #[serde(
357        default,
358        deserialize_with = "deserialize_optional_non_null",
359        rename = "contact.address.primary",
360        skip_serializing_if = "Option::is_none"
361    )]
362    pub contact_address_primary: Option<ContactAddressPrimary>,
363    #[serde(
364        default,
365        deserialize_with = "deserialize_optional_non_null",
366        rename = "contact.email",
367        skip_serializing_if = "Option::is_none"
368    )]
369    pub contact_email: Option<String>,
370    #[serde(
371        default,
372        deserialize_with = "deserialize_optional_non_null",
373        rename = "contact.mobile",
374        skip_serializing_if = "Option::is_none"
375    )]
376    pub contact_mobile: Option<String>,
377    #[serde(
378        default,
379        deserialize_with = "deserialize_optional_non_null",
380        rename = "person.birthdate",
381        skip_serializing_if = "Option::is_none"
382    )]
383    pub person_birthdate: Option<String>,
384    #[serde(
385        default,
386        deserialize_with = "deserialize_optional_non_null",
387        rename = "person.first_name",
388        skip_serializing_if = "Option::is_none"
389    )]
390    pub person_first_name: Option<String>,
391    #[serde(
392        default,
393        deserialize_with = "deserialize_optional_non_null",
394        rename = "person.last_name",
395        skip_serializing_if = "Option::is_none"
396    )]
397    pub person_last_name: Option<String>,
398    #[serde(
399        default,
400        deserialize_with = "deserialize_optional_non_null",
401        rename = "person.username",
402        skip_serializing_if = "Option::is_none"
403    )]
404    pub person_username: Option<String>,
405    #[serde(flatten)]
406    pub additional: AdditionalMembers,
407}
408
409impl fmt::Debug for ClaimValues {
410    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
411        formatter.write_str("ClaimValues([REDACTED])")
412    }
413}
414
415#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
416pub struct EnrollRequest {
417    pub agent_did: String,
418    #[serde(
419        default,
420        deserialize_with = "deserialize_optional_non_null",
421        skip_serializing_if = "Option::is_none"
422    )]
423    pub claims: Option<ClaimValues>,
424    #[serde(
425        default,
426        deserialize_with = "deserialize_optional_non_null",
427        skip_serializing_if = "Option::is_none"
428    )]
429    pub idempotency_key: Option<String>,
430    #[serde(flatten)]
431    pub additional: AdditionalMembers,
432}
433
434fn owner_action_is_not_true(value: &Option<StringBoolean>) -> bool {
435    !matches!(value, Some(StringBoolean::True))
436}
437
438#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
439pub struct EnrollResponse {
440    pub status: AgentStatus,
441    #[serde(
442        default,
443        deserialize_with = "deserialize_optional_non_null",
444        skip_serializing_if = "owner_action_is_not_true"
445    )]
446    pub owner_action_required: Option<StringBoolean>,
447    #[serde(
448        default,
449        deserialize_with = "deserialize_optional_non_null",
450        skip_serializing_if = "Option::is_none"
451    )]
452    pub verification_pending: Option<Vec<String>>,
453    #[serde(
454        default,
455        deserialize_with = "deserialize_optional_non_null",
456        skip_serializing_if = "Option::is_none"
457    )]
458    pub requirements_pending: Option<Vec<String>>,
459    #[serde(flatten)]
460    pub additional: AdditionalMembers,
461}
462
463#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
464pub struct StatusResponse {
465    pub status: AgentStatus,
466    #[serde(
467        default,
468        deserialize_with = "deserialize_optional_non_null",
469        skip_serializing_if = "owner_action_is_not_true"
470    )]
471    pub owner_action_required: Option<StringBoolean>,
472    #[serde(
473        default,
474        deserialize_with = "deserialize_optional_non_null",
475        skip_serializing_if = "Option::is_none"
476    )]
477    pub verification_pending: Option<Vec<String>>,
478    #[serde(
479        default,
480        deserialize_with = "deserialize_optional_non_null",
481        skip_serializing_if = "Option::is_none"
482    )]
483    pub requirements_pending: Option<Vec<String>>,
484    #[serde(
485        default,
486        deserialize_with = "deserialize_optional_non_null",
487        skip_serializing_if = "Option::is_none"
488    )]
489    pub since: Option<String>,
490    #[serde(flatten)]
491    pub additional: AdditionalMembers,
492}
493
494#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
495pub struct GrantRequest {
496    pub grant_type: GrantType,
497    #[serde(default, skip_serializing_if = "Vec::is_empty")]
498    pub requested_scopes: Vec<String>,
499    #[serde(flatten)]
500    pub additional: AdditionalMembers,
501}
502
503#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
504pub struct RevokeRequest {
505    #[serde(
506        default,
507        deserialize_with = "deserialize_optional_non_null",
508        skip_serializing_if = "Option::is_none"
509    )]
510    pub grant_type: Option<GrantType>,
511    #[serde(
512        default,
513        deserialize_with = "deserialize_optional_non_null",
514        skip_serializing_if = "Option::is_none"
515    )]
516    pub credential_id: Option<String>,
517    #[serde(
518        default,
519        deserialize_with = "deserialize_optional_non_null",
520        skip_serializing_if = "Option::is_none"
521    )]
522    pub all_grant_types: Option<StringBoolean>,
523    #[serde(flatten)]
524    pub additional: AdditionalMembers,
525}
526
527#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
528#[serde(deny_unknown_fields)]
529pub struct RevokeResponse {}
530
531#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
532pub struct IdempotencyMetadata {
533    #[serde(
534        default,
535        deserialize_with = "deserialize_optional_non_null",
536        skip_serializing_if = "Option::is_none"
537    )]
538    pub agent_did: Option<String>,
539    pub idempotency_key: String,
540    #[serde(
541        default,
542        deserialize_with = "deserialize_optional_non_null",
543        skip_serializing_if = "Option::is_none"
544    )]
545    pub first_body_hash: Option<String>,
546    #[serde(
547        default,
548        deserialize_with = "deserialize_optional_non_null",
549        skip_serializing_if = "Option::is_none"
550    )]
551    pub second_body_hash: Option<String>,
552    #[serde(flatten)]
553    pub additional: AdditionalMembers,
554}
555
556#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
557pub struct OpenApiAepSecurityScheme {
558    #[serde(rename = "x-aep-authentication-method")]
559    pub authentication_method: AuthenticationMethod,
560    #[serde(flatten)]
561    pub additional: AdditionalMembers,
562}
563
564#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
565pub struct ClientAssertionClaims {
566    pub aud: String,
567    pub exp: i64,
568    pub iat: i64,
569    pub iss: String,
570    pub jti: String,
571    pub op: AssertionOperation,
572    #[serde(
573        default,
574        deserialize_with = "deserialize_optional_non_null",
575        skip_serializing_if = "Option::is_none"
576    )]
577    pub resource: Option<String>,
578    pub sub: String,
579    #[serde(flatten)]
580    pub additional: AdditionalMembers,
581}
582
583#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
584pub struct ProblemDetails {
585    #[serde(rename = "type")]
586    pub problem_type: String,
587    pub title: String,
588    pub status: i64,
589    #[serde(
590        default,
591        deserialize_with = "deserialize_optional_non_null",
592        skip_serializing_if = "Option::is_none"
593    )]
594    pub detail: Option<String>,
595    #[serde(
596        default,
597        deserialize_with = "deserialize_optional_non_null",
598        skip_serializing_if = "Option::is_none"
599    )]
600    pub instance: Option<String>,
601    pub code: ErrorCode,
602    #[serde(
603        default,
604        deserialize_with = "deserialize_optional_non_null",
605        skip_serializing_if = "owner_action_is_not_true"
606    )]
607    pub owner_action_required: Option<StringBoolean>,
608    #[serde(
609        default,
610        deserialize_with = "deserialize_optional_non_null",
611        skip_serializing_if = "Option::is_none"
612    )]
613    pub requirements_pending: Option<Vec<String>>,
614    #[serde(
615        default,
616        deserialize_with = "deserialize_optional_non_null",
617        skip_serializing_if = "Option::is_none"
618    )]
619    pub verification_pending: Option<Vec<String>>,
620    #[serde(flatten)]
621    pub additional: AdditionalMembers,
622}
623
624fn deserialize_nullable_scopes<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
625where
626    D: serde::Deserializer<'de>,
627{
628    Ok(Option::<Vec<String>>::deserialize(deserializer)?.unwrap_or_default())
629}
630
631#[derive(Clone, Deserialize, PartialEq, Serialize)]
632pub struct OAuthBearerGrantResponse {
633    pub access_token: String,
634    pub credential_id: String,
635    pub expires_at: String,
636    #[serde(default, deserialize_with = "deserialize_nullable_scopes")]
637    pub scopes: Vec<String>,
638    pub token_type: String,
639    #[serde(flatten)]
640    pub additional: AdditionalMembers,
641}
642
643impl fmt::Debug for OAuthBearerGrantResponse {
644    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
645        formatter
646            .debug_struct("OAuthBearerGrantResponse")
647            .field("access_token", &"[REDACTED]")
648            .field("credential_id", &self.credential_id)
649            .field("expires_at", &self.expires_at)
650            .field("scopes", &self.scopes)
651            .field("token_type", &self.token_type)
652            .field("additional", &"[REDACTED]")
653            .finish()
654    }
655}
656
657#[derive(Clone, Deserialize, PartialEq, Serialize)]
658pub struct ApiKeyGrantResponse {
659    pub api_key: String,
660    pub credential_id: String,
661    pub expires_at: String,
662    pub header: String,
663    #[serde(default, deserialize_with = "deserialize_nullable_scopes")]
664    pub scopes: Vec<String>,
665    #[serde(flatten)]
666    pub additional: AdditionalMembers,
667}
668
669impl fmt::Debug for ApiKeyGrantResponse {
670    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
671        formatter
672            .debug_struct("ApiKeyGrantResponse")
673            .field("api_key", &"[REDACTED]")
674            .field("credential_id", &self.credential_id)
675            .field("expires_at", &self.expires_at)
676            .field("header", &self.header)
677            .field("scopes", &self.scopes)
678            .field("additional", &"[REDACTED]")
679            .finish()
680    }
681}
682
683#[derive(Clone, Deserialize, PartialEq, Serialize)]
684pub struct BasicGrantResponse {
685    pub credential_id: String,
686    pub expires_at: String,
687    pub password: String,
688    #[serde(
689        default,
690        deserialize_with = "deserialize_optional_non_null",
691        skip_serializing_if = "Option::is_none"
692    )]
693    pub realm: Option<String>,
694    #[serde(default, deserialize_with = "deserialize_nullable_scopes")]
695    pub scopes: Vec<String>,
696    pub username: String,
697    #[serde(flatten)]
698    pub additional: AdditionalMembers,
699}
700
701impl fmt::Debug for BasicGrantResponse {
702    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
703        formatter
704            .debug_struct("BasicGrantResponse")
705            .field("credential_id", &self.credential_id)
706            .field("expires_at", &self.expires_at)
707            .field("password", &"[REDACTED]")
708            .field("realm", &self.realm)
709            .field("scopes", &self.scopes)
710            .field("username", &self.username)
711            .field("additional", &"[REDACTED]")
712            .finish()
713    }
714}
715
716#[derive(Clone, Debug, PartialEq)]
717pub enum BuiltInGrantResponse {
718    OAuthBearer(OAuthBearerGrantResponse),
719    ApiKey(ApiKeyGrantResponse),
720    Basic(BasicGrantResponse),
721}
722
723impl BuiltInGrantResponse {
724    pub fn grant_type(&self) -> GrantType {
725        match self {
726            Self::OAuthBearer(_) => GrantType::OAuthBearer,
727            Self::ApiKey(_) => GrantType::ApiKey,
728            Self::Basic(_) => GrantType::Basic,
729        }
730    }
731}
732
733#[derive(Clone, Deserialize, Eq, PartialEq, Serialize)]
734#[serde(deny_unknown_fields)]
735pub struct ProtectedResourceAuthorization {
736    pub carrier: AuthorizationCarrier,
737    pub scheme: CredentialScheme,
738    pub credentials: String,
739}
740
741impl fmt::Debug for ProtectedResourceAuthorization {
742    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
743        formatter
744            .debug_struct("ProtectedResourceAuthorization")
745            .field("carrier", &self.carrier)
746            .field("scheme", &self.scheme)
747            .field("credentials", &"[REDACTED]")
748            .finish()
749    }
750}