Skip to main content

swf_core/models/
authentication.rs

1use serde::{Deserialize, Serialize};
2
3/// Provides the default OAuth2 request encoding
4fn default_oauth2_request_encoding() -> String {
5    OAuth2RequestEncoding::FORM_URL.to_string()
6}
7
8/// Provides the default OAUTH2 token endpoint
9fn default_token_endpoint() -> String {
10    "/oauth2/token".to_string()
11}
12
13/// Provides the default OAUTH2 revocation endpoint
14fn default_revocation_endpoint() -> String {
15    "/oauth2/revoke".to_string()
16}
17
18/// Provides the default OAUTH2 introspection endpoint
19fn default_introspection_endpoint() -> String {
20    "/oauth2/introspect".to_string()
21}
22
23string_constants! {
24    /// Enumerates all supported authentication schemes
25    AuthenticationScheme {
26        BASIC => "Basic",
27        BEARER => "Bearer",
28        CERTIFICATE => "Certificate",
29        DIGEST => "Digest",
30        OAUTH2 => "OAuth2",
31        OIDC => "OpenIDConnect",
32    }
33}
34
35string_constants! {
36    /// Enumerates all supported OAUTH2 authentication methods
37    OAuth2ClientAuthenticationMethod {
38        BASIC => "client_secret_basic",
39        POST => "client_secret_post",
40        JWT => "client_secret_jwt",
41        PRIVATE_KEY => "private_key_jwt",
42        NONE => "none",
43    }
44}
45
46string_constants! {
47    /// Exposes all supported request encodings for OAUTH2 requests
48    OAuth2RequestEncoding {
49        FORM_URL => "application/x-www-form-urlencoded",
50        JSON => "application/json",
51    }
52}
53
54/// Represents the definition of an authentication policy
55#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
56pub struct AuthenticationPolicyDefinition {
57    /// Gets/sets the name of the top level authentication policy to use, if any
58    #[serde(rename = "use", skip_serializing_if = "Option::is_none")]
59    pub use_: Option<String>,
60
61    /// Gets/sets the `basic` authentication scheme to use, if any
62    #[serde(skip_serializing_if = "Option::is_none")]
63    pub basic: Option<BasicAuthenticationSchemeDefinition>,
64
65    /// Gets/sets the `Bearer` authentication scheme to use, if any
66    #[serde(skip_serializing_if = "Option::is_none")]
67    pub bearer: Option<BearerAuthenticationSchemeDefinition>,
68
69    /// Gets/sets the `Certificate` authentication scheme to use, if any
70    #[serde(skip_serializing_if = "Option::is_none")]
71    pub certificate: Option<CertificateAuthenticationSchemeDefinition>,
72
73    /// Gets/sets the `Digest` authentication scheme to use, if any
74    #[serde(skip_serializing_if = "Option::is_none")]
75    pub digest: Option<DigestAuthenticationSchemeDefinition>,
76
77    /// Gets/sets the `OAUTH2` authentication scheme to use, if any
78    #[serde(skip_serializing_if = "Option::is_none")]
79    pub oauth2: Option<OAuth2AuthenticationSchemeDefinition>,
80
81    /// Gets/sets the `OIDC` authentication scheme to use, if any
82    #[serde(skip_serializing_if = "Option::is_none")]
83    pub oidc: Option<OpenIDConnectSchemeDefinition>,
84}
85
86/// Represents a referenceable authentication policy that can either be a reference or an inline policy
87#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
88#[serde(untagged)]
89pub enum ReferenceableAuthenticationPolicy {
90    /// A reference to a named authentication policy
91    Reference(AuthenticationPolicyReference),
92    /// An inline authentication policy definition
93    Policy(Box<AuthenticationPolicyDefinition>),
94}
95
96/// Represents a reference to a named authentication policy
97#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
98pub struct AuthenticationPolicyReference {
99    /// The name of the authentication policy to use
100    #[serde(rename = "use")]
101    pub use_: String,
102}
103
104/// Macro to define a username/password credential authentication scheme.
105/// Used by Basic and Digest schemes which share the same field structure.
106macro_rules! credential_auth_scheme {
107    ($(#[$meta:meta])* $name:ident) => {
108        $(#[$meta])*
109        #[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
110        pub struct $name {
111            /// Gets/sets the name of the secret, if any, used to configure the authentication scheme
112            #[serde(rename = "use", skip_serializing_if = "Option::is_none")]
113            pub use_: Option<String>,
114
115            /// Gets/sets the username used for authentication
116            #[serde(skip_serializing_if = "Option::is_none")]
117            pub username: Option<String>,
118
119            /// Gets/sets the password used for authentication
120            #[serde(skip_serializing_if = "Option::is_none")]
121            pub password: Option<String>,
122        }
123    };
124}
125
126credential_auth_scheme!(
127    /// Represents the definition of a basic authentication scheme
128    BasicAuthenticationSchemeDefinition
129);
130
131/// Represents the definition of a bearer authentication scheme
132#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
133pub struct BearerAuthenticationSchemeDefinition {
134    /// Gets/sets the name of the secret, if any, used to configure the authentication scheme
135    #[serde(rename = "use", skip_serializing_if = "Option::is_none")]
136    pub use_: Option<String>,
137
138    /// Gets/sets the bearer token used for authentication
139    #[serde(skip_serializing_if = "Option::is_none")]
140    pub token: Option<String>,
141}
142
143/// Represents the definition of a certificate authentication scheme
144#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
145pub struct CertificateAuthenticationSchemeDefinition {
146    /// Gets/sets the name of the secret, if any, used to configure the authentication scheme
147    #[serde(rename = "use", skip_serializing_if = "Option::is_none")]
148    pub use_: Option<String>,
149}
150
151credential_auth_scheme!(
152    /// Represents the definition of a digest authentication scheme
153    DigestAuthenticationSchemeDefinition
154);
155
156/// Represents the definition of an OAUTH2 client
157#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
158pub struct OAuth2AuthenticationClientDefinition {
159    /// Gets/sets the OAUTH2 `client_id` to use. Required if 'Authentication' has NOT been set to 'none'.
160    #[serde(skip_serializing_if = "Option::is_none")]
161    pub id: Option<String>,
162
163    /// Gets/sets the OAUTH2 `client_secret` to use, if any
164    #[serde(skip_serializing_if = "Option::is_none")]
165    pub secret: Option<String>,
166
167    /// Gets/sets a JWT, if any, containing a signed assertion with the application credentials
168    #[serde(skip_serializing_if = "Option::is_none")]
169    pub assertion: Option<String>,
170
171    /// Gets/sets the authentication method to use to authenticate the client. Defaults to 'client_secret_post'
172    #[serde(skip_serializing_if = "Option::is_none")]
173    pub authentication: Option<String>,
174}
175
176/// Represents the configuration of an OAUTH2 authentication request
177#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
178pub struct OAuth2AuthenticationRequestDefinition {
179    /// Gets/sets the encoding of the authentication request. Defaults to 'application/x-www-form-urlencoded'
180    #[serde(default = "default_oauth2_request_encoding")]
181    pub encoding: String,
182}
183
184/// Represents the definition of an OAUTH2 token
185#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
186pub struct OAuth2TokenDefinition {
187    /// Gets/sets the security token to use
188    pub token: String,
189
190    /// Gets/sets the type of security token to use
191    #[serde(rename = "type")]
192    pub type_: String,
193}
194
195/// Represents the configuration of OAUTH2 endpoints
196#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
197pub struct OAuth2AuthenticationEndpointsDefinition {
198    /// Gets/sets the relative path to the token endpoint. Defaults to `/oauth2/token`
199    #[serde(default = "default_token_endpoint")]
200    pub token: String,
201
202    /// Gets/sets the relative path to the revocation endpoint. Defaults to `/oauth2/revoke`
203    #[serde(default = "default_revocation_endpoint")]
204    pub revocation: String,
205
206    /// Gets/sets the relative path to the introspection endpoint. Defaults to `/oauth2/introspect`
207    #[serde(default = "default_introspection_endpoint")]
208    pub introspection: String,
209}
210
211/// Macro to define OAuth2-like authentication scheme structs.
212/// OAuth2 and OIDC share the same field set except OAuth2 has an extra `endpoints` field.
213macro_rules! oauth2_like_auth_scheme {
214    ($( #[$meta:meta] )* $name:ident { $($extra_field:tt)* }) => {
215        $( #[$meta] )*
216        #[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
217        pub struct $name {
218            /// Gets/sets the name of the secret, if any, used to configure the authentication scheme
219            #[serde(rename = "use", skip_serializing_if = "Option::is_none")]
220            pub use_: Option<String>,
221
222            $($extra_field)*
223
224            /// Gets/sets the URI that references the OAUTH2 authority to use.
225            #[serde(skip_serializing_if = "Option::is_none")]
226            pub authority: Option<String>,
227
228            /// Gets/sets the grant type to use.
229            #[serde(skip_serializing_if = "Option::is_none")]
230            pub grant: Option<String>,
231
232            /// Gets/sets the definition of the client to use.
233            #[serde(skip_serializing_if = "Option::is_none")]
234            pub client: Option<OAuth2AuthenticationClientDefinition>,
235
236            /// Gets/sets the configuration of the authentication request to perform.
237            #[serde(skip_serializing_if = "Option::is_none")]
238            pub request: Option<OAuth2AuthenticationRequestDefinition>,
239
240            /// Gets/sets a list of valid issuers for token checks.
241            #[serde(skip_serializing_if = "Option::is_none")]
242            pub issuers: Option<Vec<String>>,
243
244            /// Gets/sets the scopes to request the token for.
245            #[serde(skip_serializing_if = "Option::is_none")]
246            pub scopes: Option<Vec<String>>,
247
248            /// Gets/sets the audiences to request the token for.
249            #[serde(skip_serializing_if = "Option::is_none")]
250            pub audiences: Option<Vec<String>>,
251
252            /// Gets/sets the username to use (for Password grant).
253            #[serde(skip_serializing_if = "Option::is_none")]
254            pub username: Option<String>,
255
256            /// Gets/sets the password to use (for Password grant).
257            #[serde(skip_serializing_if = "Option::is_none")]
258            pub password: Option<String>,
259
260            /// Gets/sets the token representing the identity of the party on whose behalf the request is made.
261            #[serde(skip_serializing_if = "Option::is_none")]
262            pub subject: Option<OAuth2TokenDefinition>,
263
264            /// Gets/sets the token representing the acting party's identity.
265            #[serde(skip_serializing_if = "Option::is_none")]
266            pub actor: Option<OAuth2TokenDefinition>,
267        }
268    };
269}
270
271oauth2_like_auth_scheme!(
272    /// Represents the definition of an OAUTH2 authentication scheme
273    OAuth2AuthenticationSchemeDefinition {
274        /// Gets/sets the configuration of the OAUTH2 endpoints to use
275        #[serde(skip_serializing_if = "Option::is_none")]
276        pub endpoints: Option<OAuth2AuthenticationEndpointsDefinition>,
277    }
278);
279
280oauth2_like_auth_scheme!(
281    /// Represents the definition of an OpenIDConnect authentication scheme
282    OpenIDConnectSchemeDefinition {}
283);
284
285#[cfg(test)]
286mod tests {
287    use super::*;
288
289    #[test]
290    fn test_basic_auth_serialize() {
291        let basic = BasicAuthenticationSchemeDefinition {
292            use_: None,
293            username: Some("john".to_string()),
294            password: Some("12345".to_string()),
295        };
296        let json = serde_json::to_string(&basic).unwrap();
297        assert!(json.contains("\"username\":\"john\""));
298        assert!(json.contains("\"password\":\"12345\""));
299        assert!(!json.contains("\"use\""));
300    }
301
302    #[test]
303    fn test_basic_auth_deserialize() {
304        let json = r#"{"username": "admin", "password": "secret"}"#;
305        let basic: BasicAuthenticationSchemeDefinition = serde_json::from_str(json).unwrap();
306        assert_eq!(basic.username, Some("admin".to_string()));
307        assert_eq!(basic.password, Some("secret".to_string()));
308    }
309
310    #[test]
311    fn test_basic_auth_with_use_secret() {
312        let json = r#"{"use": "mySecret"}"#;
313        let basic: BasicAuthenticationSchemeDefinition = serde_json::from_str(json).unwrap();
314        assert_eq!(basic.use_, Some("mySecret".to_string()));
315        assert!(basic.username.is_none());
316    }
317
318    #[test]
319    fn test_bearer_auth_serialize() {
320        let bearer = BearerAuthenticationSchemeDefinition {
321            use_: None,
322            token: Some("mytoken123".to_string()),
323        };
324        let json = serde_json::to_string(&bearer).unwrap();
325        assert!(json.contains("\"token\":\"mytoken123\""));
326    }
327
328    #[test]
329    fn test_bearer_auth_with_use_secret() {
330        let json = r#"{"use": "bearerSecret"}"#;
331        let bearer: BearerAuthenticationSchemeDefinition = serde_json::from_str(json).unwrap();
332        assert_eq!(bearer.use_, Some("bearerSecret".to_string()));
333    }
334
335    #[test]
336    fn test_digest_auth_serialize() {
337        let digest = DigestAuthenticationSchemeDefinition {
338            use_: None,
339            username: Some("digestUser".to_string()),
340            password: Some("digestPass".to_string()),
341        };
342        let json = serde_json::to_string(&digest).unwrap();
343        assert!(json.contains("\"username\":\"digestUser\""));
344        assert!(json.contains("\"password\":\"digestPass\""));
345    }
346
347    #[test]
348    fn test_auth_policy_basic() {
349        let policy = AuthenticationPolicyDefinition {
350            use_: None,
351            basic: Some(BasicAuthenticationSchemeDefinition {
352                use_: None,
353                username: Some("john".to_string()),
354                password: Some("12345".to_string()),
355            }),
356            bearer: None,
357            certificate: None,
358            digest: None,
359            oauth2: None,
360            oidc: None,
361        };
362        let json = serde_json::to_string(&policy).unwrap();
363        assert!(json.contains("\"basic\""));
364        assert!(json.contains("\"username\":\"john\""));
365        assert!(!json.contains("\"bearer\""));
366    }
367
368    #[test]
369    fn test_auth_policy_digest_deserialize() {
370        let json = r#"{
371            "digest": {
372                "username": "digestUser",
373                "password": "digestPass"
374            }
375        }"#;
376        let policy: AuthenticationPolicyDefinition = serde_json::from_str(json).unwrap();
377        assert!(policy.digest.is_some());
378        assert!(policy.basic.is_none());
379        let digest = policy.digest.unwrap();
380        assert_eq!(digest.username, Some("digestUser".to_string()));
381        assert_eq!(digest.password, Some("digestPass".to_string()));
382    }
383
384    #[test]
385    fn test_auth_policy_oauth2_inline() {
386        let json = r#"{
387            "oauth2": {
388                "authority": "https://auth.example.com",
389                "grant": "client_credentials",
390                "scopes": ["scope1", "scope2"]
391            }
392        }"#;
393        let policy: AuthenticationPolicyDefinition = serde_json::from_str(json).unwrap();
394        assert!(policy.oauth2.is_some());
395        let oauth2 = policy.oauth2.unwrap();
396        assert_eq!(
397            oauth2.authority,
398            Some("https://auth.example.com".to_string())
399        );
400        assert_eq!(oauth2.grant, Some("client_credentials".to_string()));
401        assert_eq!(
402            oauth2.scopes,
403            Some(vec!["scope1".to_string(), "scope2".to_string()])
404        );
405    }
406
407    #[test]
408    fn test_auth_policy_oauth2_use() {
409        let json = r#"{
410            "oauth2": {
411                "use": "mysecret"
412            }
413        }"#;
414        let policy: AuthenticationPolicyDefinition = serde_json::from_str(json).unwrap();
415        assert!(policy.oauth2.is_some());
416        let oauth2 = policy.oauth2.unwrap();
417        assert_eq!(oauth2.use_, Some("mysecret".to_string()));
418    }
419
420    #[test]
421    fn test_referenceable_auth_policy_reference() {
422        let json = r#"{"use": "myAuthPolicy"}"#;
423        let ref_policy: ReferenceableAuthenticationPolicy = serde_json::from_str(json).unwrap();
424        match ref_policy {
425            ReferenceableAuthenticationPolicy::Reference(r) => {
426                assert_eq!(r.use_, "myAuthPolicy");
427            }
428            _ => panic!("Expected Reference variant"),
429        }
430    }
431
432    #[test]
433    fn test_referenceable_auth_policy_inline() {
434        let json = r#"{
435            "basic": {
436                "username": "john",
437                "password": "secret"
438            }
439        }"#;
440        let ref_policy: ReferenceableAuthenticationPolicy = serde_json::from_str(json).unwrap();
441        match ref_policy {
442            ReferenceableAuthenticationPolicy::Policy(p) => {
443                assert!(p.basic.is_some());
444            }
445            _ => panic!("Expected Policy variant"),
446        }
447    }
448
449    #[test]
450    fn test_oauth2_scheme_roundtrip() {
451        let json = r#"{
452            "authority": "https://auth.example.com",
453            "grant": "client_credentials",
454            "scopes": ["scope1", "scope2"]
455        }"#;
456        let oauth2: OAuth2AuthenticationSchemeDefinition = serde_json::from_str(json).unwrap();
457        let serialized = serde_json::to_string(&oauth2).unwrap();
458        let deserialized: OAuth2AuthenticationSchemeDefinition =
459            serde_json::from_str(&serialized).unwrap();
460        assert_eq!(oauth2, deserialized);
461    }
462
463    // Additional tests matching Go SDK's authentication_test.go
464
465    #[test]
466    fn test_auth_policy_basic_roundtrip() {
467        // Matches Go SDK's TestAuthenticationPolicy "Valid Basic Authentication Inline"
468        let json = r#"{"basic":{"username":"john","password":"12345"}}"#;
469        let policy: AuthenticationPolicyDefinition = serde_json::from_str(json).unwrap();
470        assert!(policy.basic.is_some());
471        let basic = policy.basic.as_ref().unwrap();
472        assert_eq!(basic.username, Some("john".to_string()));
473        assert_eq!(basic.password, Some("12345".to_string()));
474        let serialized = serde_json::to_string(&policy).unwrap();
475        let deserialized: AuthenticationPolicyDefinition =
476            serde_json::from_str(&serialized).unwrap();
477        assert_eq!(policy, deserialized);
478    }
479
480    #[test]
481    fn test_auth_policy_digest_roundtrip() {
482        // Matches Go SDK's TestAuthenticationPolicy "Valid Digest Authentication Inline"
483        let json = r#"{"digest":{"username":"digestUser","password":"digestPass"}}"#;
484        let policy: AuthenticationPolicyDefinition = serde_json::from_str(json).unwrap();
485        assert!(policy.digest.is_some());
486        let digest = policy.digest.as_ref().unwrap();
487        assert_eq!(digest.username, Some("digestUser".to_string()));
488        assert_eq!(digest.password, Some("digestPass".to_string()));
489        let serialized = serde_json::to_string(&policy).unwrap();
490        let deserialized: AuthenticationPolicyDefinition =
491            serde_json::from_str(&serialized).unwrap();
492        assert_eq!(policy, deserialized);
493    }
494
495    #[test]
496    fn test_oauth2_use_secret() {
497        // Matches Go SDK's TestAuthenticationOAuth2Policy "Valid OAuth2 Authentication Use"
498        let json = r#"{"oauth2":{"use":"mysecret"}}"#;
499        let policy: AuthenticationPolicyDefinition = serde_json::from_str(json).unwrap();
500        assert!(policy.oauth2.is_some());
501        let oauth2 = policy.oauth2.as_ref().unwrap();
502        assert_eq!(oauth2.use_, Some("mysecret".to_string()));
503    }
504
505    #[test]
506    fn test_oauth2_inline_properties() {
507        // Matches Go SDK's TestAuthenticationOAuth2Policy "Valid OAuth2 Authentication Inline"
508        let json = r#"{"oauth2":{"authority":"https://auth.example.com","grant":"client_credentials","scopes":["scope1","scope2"]}}"#;
509        let policy: AuthenticationPolicyDefinition = serde_json::from_str(json).unwrap();
510        assert!(policy.oauth2.is_some());
511        let oauth2 = policy.oauth2.as_ref().unwrap();
512        assert_eq!(
513            oauth2.authority,
514            Some("https://auth.example.com".to_string())
515        );
516        assert_eq!(oauth2.grant, Some("client_credentials".to_string()));
517        assert_eq!(
518            oauth2.scopes,
519            Some(vec!["scope1".to_string(), "scope2".to_string()])
520        );
521    }
522
523    #[test]
524    fn test_bearer_auth_roundtrip() {
525        let bearer = BearerAuthenticationSchemeDefinition {
526            use_: None,
527            token: Some("mytoken123".to_string()),
528        };
529        let serialized = serde_json::to_string(&bearer).unwrap();
530        let deserialized: BearerAuthenticationSchemeDefinition =
531            serde_json::from_str(&serialized).unwrap();
532        assert_eq!(bearer, deserialized);
533    }
534
535    #[test]
536    fn test_referenceable_auth_policy_roundtrip() {
537        // Test inline auth policy roundtrip through ReferenceableAuthenticationPolicy
538        let json = r#"{"basic":{"username":"admin","password":"admin"}}"#;
539        let ref_policy: ReferenceableAuthenticationPolicy = serde_json::from_str(json).unwrap();
540        let serialized = serde_json::to_string(&ref_policy).unwrap();
541        let deserialized: ReferenceableAuthenticationPolicy =
542            serde_json::from_str(&serialized).unwrap();
543        assert_eq!(ref_policy, deserialized);
544    }
545
546    #[test]
547    fn test_basic_auth_use_vs_credentials() {
548        // Basic auth can have either "use" (secret reference) or username/password
549        let use_json = r#"{"use":"mySecret"}"#;
550        let basic_use: BasicAuthenticationSchemeDefinition =
551            serde_json::from_str(use_json).unwrap();
552        assert_eq!(basic_use.use_, Some("mySecret".to_string()));
553        assert!(basic_use.username.is_none());
554
555        let cred_json = r#"{"username":"admin","password":"secret"}"#;
556        let basic_cred: BasicAuthenticationSchemeDefinition =
557            serde_json::from_str(cred_json).unwrap();
558        assert!(basic_cred.use_.is_none());
559        assert_eq!(basic_cred.username, Some("admin".to_string()));
560        assert_eq!(basic_cred.password, Some("secret".to_string()));
561    }
562
563    #[test]
564    fn test_digest_auth_use_secret() {
565        let json = r#"{"use":"digestSecret"}"#;
566        let digest: DigestAuthenticationSchemeDefinition = serde_json::from_str(json).unwrap();
567        assert_eq!(digest.use_, Some("digestSecret".to_string()));
568        assert!(digest.username.is_none());
569    }
570
571    // Additional tests matching Go SDK's authentication_oauth_test.go and authentication_test.go
572
573    #[test]
574    fn test_oauth2_full_properties() {
575        // Matches Go SDK's TestOAuth2AuthenticationPolicyValidation - valid properties
576        let json = r#"{
577            "authority": "https://auth.example.com",
578            "grant": "client_credentials",
579            "scopes": ["scope1", "scope2"],
580            "client": {
581                "id": "my-client-id",
582                "secret": "my-client-secret",
583                "authentication": "client_secret_post"
584            },
585            "request": {
586                "encoding": "application/x-www-form-urlencoded"
587            },
588            "issuers": ["https://issuer1.example.com", "https://issuer2.example.com"],
589            "audiences": ["api1", "api2"],
590            "endpoints": {
591                "token": "/oauth2/token",
592                "revocation": "/oauth2/revoke",
593                "introspection": "/oauth2/introspect"
594            }
595        }"#;
596        let oauth2: OAuth2AuthenticationSchemeDefinition = serde_json::from_str(json).unwrap();
597        assert_eq!(
598            oauth2.authority,
599            Some("https://auth.example.com".to_string())
600        );
601        assert_eq!(oauth2.grant, Some("client_credentials".to_string()));
602        assert_eq!(
603            oauth2.scopes,
604            Some(vec!["scope1".to_string(), "scope2".to_string()])
605        );
606        assert!(oauth2.client.is_some());
607        let client = oauth2.client.as_ref().unwrap();
608        assert_eq!(client.id, Some("my-client-id".to_string()));
609        assert_eq!(client.secret, Some("my-client-secret".to_string()));
610        assert_eq!(
611            client.authentication,
612            Some("client_secret_post".to_string())
613        );
614        assert!(oauth2.request.is_some());
615        let req = oauth2.request.as_ref().unwrap();
616        assert_eq!(req.encoding, "application/x-www-form-urlencoded");
617        assert_eq!(
618            oauth2.issuers,
619            Some(vec![
620                "https://issuer1.example.com".to_string(),
621                "https://issuer2.example.com".to_string()
622            ])
623        );
624        assert_eq!(
625            oauth2.audiences,
626            Some(vec!["api1".to_string(), "api2".to_string()])
627        );
628        assert!(oauth2.endpoints.is_some());
629        let endpoints = oauth2.endpoints.as_ref().unwrap();
630        assert_eq!(endpoints.token, "/oauth2/token");
631        assert_eq!(endpoints.revocation, "/oauth2/revoke");
632        assert_eq!(endpoints.introspection, "/oauth2/introspect");
633    }
634
635    #[test]
636    fn test_oauth2_full_properties_roundtrip() {
637        let json = r#"{
638            "authority": "https://auth.example.com",
639            "grant": "client_credentials",
640            "scopes": ["scope1"],
641            "client": {"id": "client1", "secret": "secret1"}
642        }"#;
643        let oauth2: OAuth2AuthenticationSchemeDefinition = serde_json::from_str(json).unwrap();
644        let serialized = serde_json::to_string(&oauth2).unwrap();
645        let deserialized: OAuth2AuthenticationSchemeDefinition =
646            serde_json::from_str(&serialized).unwrap();
647        assert_eq!(oauth2, deserialized);
648    }
649
650    #[test]
651    fn test_oauth2_password_grant() {
652        // OAuth2 with password grant type
653        let json = r#"{
654            "authority": "https://auth.example.com",
655            "grant": "password",
656            "username": "user1",
657            "password": "pass1",
658            "scopes": ["read", "write"]
659        }"#;
660        let oauth2: OAuth2AuthenticationSchemeDefinition = serde_json::from_str(json).unwrap();
661        assert_eq!(oauth2.grant, Some("password".to_string()));
662        assert_eq!(oauth2.username, Some("user1".to_string()));
663        assert_eq!(oauth2.password, Some("pass1".to_string()));
664    }
665
666    #[test]
667    fn test_oauth2_token_exchange_grant() {
668        // OAuth2 with token exchange (urn:ietf:params:oauth:grant-type:token-exchange)
669        let json = r#"{
670            "authority": "https://auth.example.com",
671            "grant": "urn:ietf:params:oauth:grant-type:token-exchange",
672            "subject": {"token": "subject-token", "type": "urn:ietf:params:oauth:token-type:access_token"},
673            "actor": {"token": "actor-token", "type": "urn:ietf:params:oauth:token-type:access_token"}
674        }"#;
675        let oauth2: OAuth2AuthenticationSchemeDefinition = serde_json::from_str(json).unwrap();
676        assert_eq!(
677            oauth2.grant,
678            Some("urn:ietf:params:oauth:grant-type:token-exchange".to_string())
679        );
680        assert!(oauth2.subject.is_some());
681        let subject = oauth2.subject.as_ref().unwrap();
682        assert_eq!(subject.token, "subject-token");
683        assert_eq!(
684            subject.type_,
685            "urn:ietf:params:oauth:token-type:access_token"
686        );
687        assert!(oauth2.actor.is_some());
688        let actor = oauth2.actor.as_ref().unwrap();
689        assert_eq!(actor.token, "actor-token");
690    }
691
692    #[test]
693    fn test_oauth2_endpoints_defaults() {
694        // OAuth2 endpoints serde defaults (applied during deserialization, not Default trait)
695        let json = r#"{}"#;
696        let endpoints: OAuth2AuthenticationEndpointsDefinition =
697            serde_json::from_str(json).unwrap();
698        assert_eq!(endpoints.token, "/oauth2/token");
699        assert_eq!(endpoints.revocation, "/oauth2/revoke");
700        assert_eq!(endpoints.introspection, "/oauth2/introspect");
701    }
702
703    #[test]
704    fn test_oauth2_request_encoding_defaults() {
705        // OAuth2 request encoding serde defaults (applied during deserialization, not Default trait)
706        let json = r#"{}"#;
707        let request: OAuth2AuthenticationRequestDefinition = serde_json::from_str(json).unwrap();
708        assert_eq!(request.encoding, "application/x-www-form-urlencoded");
709    }
710
711    #[test]
712    fn test_oauth2_request_encoding_json() {
713        let json = r#"{"encoding": "application/json"}"#;
714        let request: OAuth2AuthenticationRequestDefinition = serde_json::from_str(json).unwrap();
715        assert_eq!(request.encoding, "application/json");
716    }
717
718    #[test]
719    fn test_oidc_full_properties() {
720        // OIDC shares similar structure with OAuth2
721        let json = r#"{
722            "authority": "https://oidc.example.com",
723            "grant": "authorization_code",
724            "scopes": ["openid", "profile"],
725            "client": {"id": "oidc-client"}
726        }"#;
727        let oidc: OpenIDConnectSchemeDefinition = serde_json::from_str(json).unwrap();
728        assert_eq!(oidc.authority, Some("https://oidc.example.com".to_string()));
729        assert_eq!(oidc.grant, Some("authorization_code".to_string()));
730        assert_eq!(
731            oidc.scopes,
732            Some(vec!["openid".to_string(), "profile".to_string()])
733        );
734        assert!(oidc.client.is_some());
735    }
736
737    #[test]
738    fn test_oidc_roundtrip() {
739        let json = r#"{
740            "authority": "https://oidc.example.com",
741            "grant": "authorization_code",
742            "scopes": ["openid"]
743        }"#;
744        let oidc: OpenIDConnectSchemeDefinition = serde_json::from_str(json).unwrap();
745        let serialized = serde_json::to_string(&oidc).unwrap();
746        let deserialized: OpenIDConnectSchemeDefinition =
747            serde_json::from_str(&serialized).unwrap();
748        assert_eq!(oidc, deserialized);
749    }
750
751    #[test]
752    fn test_certificate_auth() {
753        let json = r#"{"use": "certSecret"}"#;
754        let cert: CertificateAuthenticationSchemeDefinition = serde_json::from_str(json).unwrap();
755        assert_eq!(cert.use_, Some("certSecret".to_string()));
756        assert_eq!(AuthenticationScheme::CERTIFICATE, "Certificate");
757    }
758
759    #[test]
760    fn test_auth_policy_bearer_inline() {
761        // Full auth policy with bearer token
762        let json = r#"{
763            "bearer": {
764                "token": "my-bearer-token"
765            }
766        }"#;
767        let policy: AuthenticationPolicyDefinition = serde_json::from_str(json).unwrap();
768        assert!(policy.bearer.is_some());
769        let bearer = policy.bearer.as_ref().unwrap();
770        assert_eq!(bearer.token, Some("my-bearer-token".to_string()));
771        assert!(bearer.use_.is_none());
772    }
773
774    #[test]
775    fn test_auth_policy_certificate_inline() {
776        let json = r#"{
777            "certificate": {
778                "use": "myCertSecret"
779            }
780        }"#;
781        let policy: AuthenticationPolicyDefinition = serde_json::from_str(json).unwrap();
782        assert!(policy.certificate.is_some());
783        let cert = policy.certificate.as_ref().unwrap();
784        assert_eq!(cert.use_, Some("myCertSecret".to_string()));
785    }
786
787    #[test]
788    fn test_oauth2_client_assertion() {
789        // OAuth2 client with JWT assertion
790        let json = r#"{
791            "id": "client-id",
792            "assertion": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
793            "authentication": "private_key_jwt"
794        }"#;
795        let client: OAuth2AuthenticationClientDefinition = serde_json::from_str(json).unwrap();
796        assert_eq!(client.id, Some("client-id".to_string()));
797        assert_eq!(
798            client.assertion,
799            Some("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...".to_string())
800        );
801        assert_eq!(client.authentication, Some("private_key_jwt".to_string()));
802    }
803
804    #[test]
805    fn test_oauth2_token_definition() {
806        let json = r#"{
807            "token": "my-token-value",
808            "type": "urn:ietf:params:oauth:token-type:access_token"
809        }"#;
810        let token: OAuth2TokenDefinition = serde_json::from_str(json).unwrap();
811        assert_eq!(token.token, "my-token-value");
812        assert_eq!(token.type_, "urn:ietf:params:oauth:token-type:access_token");
813    }
814}