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 resources to request the token for.
253            #[serde(skip_serializing_if = "Option::is_none")]
254            pub resources: Option<Vec<String>>,
255
256            /// Gets/sets the username to use (for Password grant).
257            #[serde(skip_serializing_if = "Option::is_none")]
258            pub username: Option<String>,
259
260            /// Gets/sets the password to use (for Password grant).
261            #[serde(skip_serializing_if = "Option::is_none")]
262            pub password: Option<String>,
263
264            /// Gets/sets the token representing the identity of the party on whose behalf the request is made.
265            #[serde(skip_serializing_if = "Option::is_none")]
266            pub subject: Option<OAuth2TokenDefinition>,
267
268            /// Gets/sets the token representing the acting party's identity.
269            #[serde(skip_serializing_if = "Option::is_none")]
270            pub actor: Option<OAuth2TokenDefinition>,
271        }
272    };
273}
274
275oauth2_like_auth_scheme!(
276    /// Represents the definition of an OAUTH2 authentication scheme
277    OAuth2AuthenticationSchemeDefinition {
278        /// Gets/sets the configuration of the OAUTH2 endpoints to use
279        #[serde(skip_serializing_if = "Option::is_none")]
280        pub endpoints: Option<OAuth2AuthenticationEndpointsDefinition>,
281    }
282);
283
284oauth2_like_auth_scheme!(
285    /// Represents the definition of an OpenIDConnect authentication scheme
286    OpenIDConnectSchemeDefinition {
287        /// Gets/sets the configuration of the OIDC endpoints to use
288        #[serde(skip_serializing_if = "Option::is_none")]
289        pub endpoints: Option<OAuth2AuthenticationEndpointsDefinition>,
290    }
291);
292
293#[cfg(test)]
294mod tests {
295    use super::*;
296
297    #[test]
298    fn test_basic_auth_serialize() {
299        let basic = BasicAuthenticationSchemeDefinition {
300            use_: None,
301            username: Some("john".to_string()),
302            password: Some("12345".to_string()),
303        };
304        let json = serde_json::to_string(&basic).unwrap();
305        assert!(json.contains("\"username\":\"john\""));
306        assert!(json.contains("\"password\":\"12345\""));
307        assert!(!json.contains("\"use\""));
308    }
309
310    #[test]
311    fn test_basic_auth_deserialize() {
312        let json = r#"{"username": "admin", "password": "secret"}"#;
313        let basic: BasicAuthenticationSchemeDefinition = serde_json::from_str(json).unwrap();
314        assert_eq!(basic.username, Some("admin".to_string()));
315        assert_eq!(basic.password, Some("secret".to_string()));
316    }
317
318    #[test]
319    fn test_basic_auth_with_use_secret() {
320        let json = r#"{"use": "mySecret"}"#;
321        let basic: BasicAuthenticationSchemeDefinition = serde_json::from_str(json).unwrap();
322        assert_eq!(basic.use_, Some("mySecret".to_string()));
323        assert!(basic.username.is_none());
324    }
325
326    #[test]
327    fn test_bearer_auth_serialize() {
328        let bearer = BearerAuthenticationSchemeDefinition {
329            use_: None,
330            token: Some("mytoken123".to_string()),
331        };
332        let json = serde_json::to_string(&bearer).unwrap();
333        assert!(json.contains("\"token\":\"mytoken123\""));
334    }
335
336    #[test]
337    fn test_bearer_auth_with_use_secret() {
338        let json = r#"{"use": "bearerSecret"}"#;
339        let bearer: BearerAuthenticationSchemeDefinition = serde_json::from_str(json).unwrap();
340        assert_eq!(bearer.use_, Some("bearerSecret".to_string()));
341    }
342
343    #[test]
344    fn test_digest_auth_serialize() {
345        let digest = DigestAuthenticationSchemeDefinition {
346            use_: None,
347            username: Some("digestUser".to_string()),
348            password: Some("digestPass".to_string()),
349        };
350        let json = serde_json::to_string(&digest).unwrap();
351        assert!(json.contains("\"username\":\"digestUser\""));
352        assert!(json.contains("\"password\":\"digestPass\""));
353    }
354
355    #[test]
356    fn test_auth_policy_basic() {
357        let policy = AuthenticationPolicyDefinition {
358            use_: None,
359            basic: Some(BasicAuthenticationSchemeDefinition {
360                use_: None,
361                username: Some("john".to_string()),
362                password: Some("12345".to_string()),
363            }),
364            bearer: None,
365            certificate: None,
366            digest: None,
367            oauth2: None,
368            oidc: None,
369        };
370        let json = serde_json::to_string(&policy).unwrap();
371        assert!(json.contains("\"basic\""));
372        assert!(json.contains("\"username\":\"john\""));
373        assert!(!json.contains("\"bearer\""));
374    }
375
376    #[test]
377    fn test_auth_policy_digest_deserialize() {
378        let json = r#"{
379            "digest": {
380                "username": "digestUser",
381                "password": "digestPass"
382            }
383        }"#;
384        let policy: AuthenticationPolicyDefinition = serde_json::from_str(json).unwrap();
385        assert!(policy.digest.is_some());
386        assert!(policy.basic.is_none());
387        let digest = policy.digest.unwrap();
388        assert_eq!(digest.username, Some("digestUser".to_string()));
389        assert_eq!(digest.password, Some("digestPass".to_string()));
390    }
391
392    #[test]
393    fn test_auth_policy_oauth2_inline() {
394        let json = r#"{
395            "oauth2": {
396                "authority": "https://auth.example.com",
397                "grant": "client_credentials",
398                "scopes": ["scope1", "scope2"]
399            }
400        }"#;
401        let policy: AuthenticationPolicyDefinition = serde_json::from_str(json).unwrap();
402        assert!(policy.oauth2.is_some());
403        let oauth2 = policy.oauth2.unwrap();
404        assert_eq!(
405            oauth2.authority,
406            Some("https://auth.example.com".to_string())
407        );
408        assert_eq!(oauth2.grant, Some("client_credentials".to_string()));
409        assert_eq!(
410            oauth2.scopes,
411            Some(vec!["scope1".to_string(), "scope2".to_string()])
412        );
413    }
414
415    #[test]
416    fn test_auth_policy_oauth2_use() {
417        let json = r#"{
418            "oauth2": {
419                "use": "mysecret"
420            }
421        }"#;
422        let policy: AuthenticationPolicyDefinition = serde_json::from_str(json).unwrap();
423        assert!(policy.oauth2.is_some());
424        let oauth2 = policy.oauth2.unwrap();
425        assert_eq!(oauth2.use_, Some("mysecret".to_string()));
426    }
427
428    #[test]
429    fn test_referenceable_auth_policy_reference() {
430        let json = r#"{"use": "myAuthPolicy"}"#;
431        let ref_policy: ReferenceableAuthenticationPolicy = serde_json::from_str(json).unwrap();
432        match ref_policy {
433            ReferenceableAuthenticationPolicy::Reference(r) => {
434                assert_eq!(r.use_, "myAuthPolicy");
435            }
436            _ => panic!("Expected Reference variant"),
437        }
438    }
439
440    #[test]
441    fn test_referenceable_auth_policy_inline() {
442        let json = r#"{
443            "basic": {
444                "username": "john",
445                "password": "secret"
446            }
447        }"#;
448        let ref_policy: ReferenceableAuthenticationPolicy = serde_json::from_str(json).unwrap();
449        match ref_policy {
450            ReferenceableAuthenticationPolicy::Policy(p) => {
451                assert!(p.basic.is_some());
452            }
453            _ => panic!("Expected Policy variant"),
454        }
455    }
456
457    #[test]
458    fn test_oauth2_scheme_roundtrip() {
459        let json = r#"{
460            "authority": "https://auth.example.com",
461            "grant": "client_credentials",
462            "scopes": ["scope1", "scope2"]
463        }"#;
464        let oauth2: OAuth2AuthenticationSchemeDefinition = serde_json::from_str(json).unwrap();
465        let serialized = serde_json::to_string(&oauth2).unwrap();
466        let deserialized: OAuth2AuthenticationSchemeDefinition =
467            serde_json::from_str(&serialized).unwrap();
468        assert_eq!(oauth2, deserialized);
469    }
470
471    // Additional tests matching Go SDK's authentication_test.go
472
473    #[test]
474    fn test_auth_policy_basic_roundtrip() {
475        // Matches Go SDK's TestAuthenticationPolicy "Valid Basic Authentication Inline"
476        let json = r#"{"basic":{"username":"john","password":"12345"}}"#;
477        let policy: AuthenticationPolicyDefinition = serde_json::from_str(json).unwrap();
478        assert!(policy.basic.is_some());
479        let basic = policy.basic.as_ref().unwrap();
480        assert_eq!(basic.username, Some("john".to_string()));
481        assert_eq!(basic.password, Some("12345".to_string()));
482        let serialized = serde_json::to_string(&policy).unwrap();
483        let deserialized: AuthenticationPolicyDefinition =
484            serde_json::from_str(&serialized).unwrap();
485        assert_eq!(policy, deserialized);
486    }
487
488    #[test]
489    fn test_auth_policy_digest_roundtrip() {
490        // Matches Go SDK's TestAuthenticationPolicy "Valid Digest Authentication Inline"
491        let json = r#"{"digest":{"username":"digestUser","password":"digestPass"}}"#;
492        let policy: AuthenticationPolicyDefinition = serde_json::from_str(json).unwrap();
493        assert!(policy.digest.is_some());
494        let digest = policy.digest.as_ref().unwrap();
495        assert_eq!(digest.username, Some("digestUser".to_string()));
496        assert_eq!(digest.password, Some("digestPass".to_string()));
497        let serialized = serde_json::to_string(&policy).unwrap();
498        let deserialized: AuthenticationPolicyDefinition =
499            serde_json::from_str(&serialized).unwrap();
500        assert_eq!(policy, deserialized);
501    }
502
503    #[test]
504    fn test_oauth2_use_secret() {
505        // Matches Go SDK's TestAuthenticationOAuth2Policy "Valid OAuth2 Authentication Use"
506        let json = r#"{"oauth2":{"use":"mysecret"}}"#;
507        let policy: AuthenticationPolicyDefinition = serde_json::from_str(json).unwrap();
508        assert!(policy.oauth2.is_some());
509        let oauth2 = policy.oauth2.as_ref().unwrap();
510        assert_eq!(oauth2.use_, Some("mysecret".to_string()));
511    }
512
513    #[test]
514    fn test_oauth2_inline_properties() {
515        // Matches Go SDK's TestAuthenticationOAuth2Policy "Valid OAuth2 Authentication Inline"
516        let json = r#"{"oauth2":{"authority":"https://auth.example.com","grant":"client_credentials","scopes":["scope1","scope2"]}}"#;
517        let policy: AuthenticationPolicyDefinition = serde_json::from_str(json).unwrap();
518        assert!(policy.oauth2.is_some());
519        let oauth2 = policy.oauth2.as_ref().unwrap();
520        assert_eq!(
521            oauth2.authority,
522            Some("https://auth.example.com".to_string())
523        );
524        assert_eq!(oauth2.grant, Some("client_credentials".to_string()));
525        assert_eq!(
526            oauth2.scopes,
527            Some(vec!["scope1".to_string(), "scope2".to_string()])
528        );
529    }
530
531    #[test]
532    fn test_bearer_auth_roundtrip() {
533        let bearer = BearerAuthenticationSchemeDefinition {
534            use_: None,
535            token: Some("mytoken123".to_string()),
536        };
537        let serialized = serde_json::to_string(&bearer).unwrap();
538        let deserialized: BearerAuthenticationSchemeDefinition =
539            serde_json::from_str(&serialized).unwrap();
540        assert_eq!(bearer, deserialized);
541    }
542
543    #[test]
544    fn test_referenceable_auth_policy_roundtrip() {
545        // Test inline auth policy roundtrip through ReferenceableAuthenticationPolicy
546        let json = r#"{"basic":{"username":"admin","password":"admin"}}"#;
547        let ref_policy: ReferenceableAuthenticationPolicy = serde_json::from_str(json).unwrap();
548        let serialized = serde_json::to_string(&ref_policy).unwrap();
549        let deserialized: ReferenceableAuthenticationPolicy =
550            serde_json::from_str(&serialized).unwrap();
551        assert_eq!(ref_policy, deserialized);
552    }
553
554    #[test]
555    fn test_basic_auth_use_vs_credentials() {
556        // Basic auth can have either "use" (secret reference) or username/password
557        let use_json = r#"{"use":"mySecret"}"#;
558        let basic_use: BasicAuthenticationSchemeDefinition =
559            serde_json::from_str(use_json).unwrap();
560        assert_eq!(basic_use.use_, Some("mySecret".to_string()));
561        assert!(basic_use.username.is_none());
562
563        let cred_json = r#"{"username":"admin","password":"secret"}"#;
564        let basic_cred: BasicAuthenticationSchemeDefinition =
565            serde_json::from_str(cred_json).unwrap();
566        assert!(basic_cred.use_.is_none());
567        assert_eq!(basic_cred.username, Some("admin".to_string()));
568        assert_eq!(basic_cred.password, Some("secret".to_string()));
569    }
570
571    #[test]
572    fn test_digest_auth_use_secret() {
573        let json = r#"{"use":"digestSecret"}"#;
574        let digest: DigestAuthenticationSchemeDefinition = serde_json::from_str(json).unwrap();
575        assert_eq!(digest.use_, Some("digestSecret".to_string()));
576        assert!(digest.username.is_none());
577    }
578
579    // Additional tests matching Go SDK's authentication_oauth_test.go and authentication_test.go
580
581    #[test]
582    fn test_oauth2_full_properties() {
583        // Matches Go SDK's TestOAuth2AuthenticationPolicyValidation - valid properties
584        let json = r#"{
585            "authority": "https://auth.example.com",
586            "grant": "client_credentials",
587            "scopes": ["scope1", "scope2"],
588            "client": {
589                "id": "my-client-id",
590                "secret": "my-client-secret",
591                "authentication": "client_secret_post"
592            },
593            "request": {
594                "encoding": "application/x-www-form-urlencoded"
595            },
596            "issuers": ["https://issuer1.example.com", "https://issuer2.example.com"],
597            "audiences": ["api1", "api2"],
598            "endpoints": {
599                "token": "/oauth2/token",
600                "revocation": "/oauth2/revoke",
601                "introspection": "/oauth2/introspect"
602            }
603        }"#;
604        let oauth2: OAuth2AuthenticationSchemeDefinition = serde_json::from_str(json).unwrap();
605        assert_eq!(
606            oauth2.authority,
607            Some("https://auth.example.com".to_string())
608        );
609        assert_eq!(oauth2.grant, Some("client_credentials".to_string()));
610        assert_eq!(
611            oauth2.scopes,
612            Some(vec!["scope1".to_string(), "scope2".to_string()])
613        );
614        assert!(oauth2.client.is_some());
615        let client = oauth2.client.as_ref().unwrap();
616        assert_eq!(client.id, Some("my-client-id".to_string()));
617        assert_eq!(client.secret, Some("my-client-secret".to_string()));
618        assert_eq!(
619            client.authentication,
620            Some("client_secret_post".to_string())
621        );
622        assert!(oauth2.request.is_some());
623        let req = oauth2.request.as_ref().unwrap();
624        assert_eq!(req.encoding, "application/x-www-form-urlencoded");
625        assert_eq!(
626            oauth2.issuers,
627            Some(vec![
628                "https://issuer1.example.com".to_string(),
629                "https://issuer2.example.com".to_string()
630            ])
631        );
632        assert_eq!(
633            oauth2.audiences,
634            Some(vec!["api1".to_string(), "api2".to_string()])
635        );
636        assert!(oauth2.endpoints.is_some());
637        let endpoints = oauth2.endpoints.as_ref().unwrap();
638        assert_eq!(endpoints.token, "/oauth2/token");
639        assert_eq!(endpoints.revocation, "/oauth2/revoke");
640        assert_eq!(endpoints.introspection, "/oauth2/introspect");
641    }
642
643    #[test]
644    fn test_oauth2_full_properties_roundtrip() {
645        let json = r#"{
646            "authority": "https://auth.example.com",
647            "grant": "client_credentials",
648            "scopes": ["scope1"],
649            "client": {"id": "client1", "secret": "secret1"}
650        }"#;
651        let oauth2: OAuth2AuthenticationSchemeDefinition = serde_json::from_str(json).unwrap();
652        let serialized = serde_json::to_string(&oauth2).unwrap();
653        let deserialized: OAuth2AuthenticationSchemeDefinition =
654            serde_json::from_str(&serialized).unwrap();
655        assert_eq!(oauth2, deserialized);
656    }
657
658    #[test]
659    fn test_oauth2_password_grant() {
660        // OAuth2 with password grant type
661        let json = r#"{
662            "authority": "https://auth.example.com",
663            "grant": "password",
664            "username": "user1",
665            "password": "pass1",
666            "scopes": ["read", "write"]
667        }"#;
668        let oauth2: OAuth2AuthenticationSchemeDefinition = serde_json::from_str(json).unwrap();
669        assert_eq!(oauth2.grant, Some("password".to_string()));
670        assert_eq!(oauth2.username, Some("user1".to_string()));
671        assert_eq!(oauth2.password, Some("pass1".to_string()));
672    }
673
674    #[test]
675    fn test_oauth2_token_exchange_grant() {
676        // OAuth2 with token exchange (urn:ietf:params:oauth:grant-type:token-exchange)
677        let json = r#"{
678            "authority": "https://auth.example.com",
679            "grant": "urn:ietf:params:oauth:grant-type:token-exchange",
680            "subject": {"token": "subject-token", "type": "urn:ietf:params:oauth:token-type:access_token"},
681            "actor": {"token": "actor-token", "type": "urn:ietf:params:oauth:token-type:access_token"}
682        }"#;
683        let oauth2: OAuth2AuthenticationSchemeDefinition = serde_json::from_str(json).unwrap();
684        assert_eq!(
685            oauth2.grant,
686            Some("urn:ietf:params:oauth:grant-type:token-exchange".to_string())
687        );
688        assert!(oauth2.subject.is_some());
689        let subject = oauth2.subject.as_ref().unwrap();
690        assert_eq!(subject.token, "subject-token");
691        assert_eq!(
692            subject.type_,
693            "urn:ietf:params:oauth:token-type:access_token"
694        );
695        assert!(oauth2.actor.is_some());
696        let actor = oauth2.actor.as_ref().unwrap();
697        assert_eq!(actor.token, "actor-token");
698    }
699
700    #[test]
701    fn test_oauth2_endpoints_defaults() {
702        // OAuth2 endpoints serde defaults (applied during deserialization, not Default trait)
703        let json = r#"{}"#;
704        let endpoints: OAuth2AuthenticationEndpointsDefinition =
705            serde_json::from_str(json).unwrap();
706        assert_eq!(endpoints.token, "/oauth2/token");
707        assert_eq!(endpoints.revocation, "/oauth2/revoke");
708        assert_eq!(endpoints.introspection, "/oauth2/introspect");
709    }
710
711    #[test]
712    fn test_oauth2_request_encoding_defaults() {
713        // OAuth2 request encoding serde defaults (applied during deserialization, not Default trait)
714        let json = r#"{}"#;
715        let request: OAuth2AuthenticationRequestDefinition = serde_json::from_str(json).unwrap();
716        assert_eq!(request.encoding, "application/x-www-form-urlencoded");
717    }
718
719    #[test]
720    fn test_oauth2_request_encoding_json() {
721        let json = r#"{"encoding": "application/json"}"#;
722        let request: OAuth2AuthenticationRequestDefinition = serde_json::from_str(json).unwrap();
723        assert_eq!(request.encoding, "application/json");
724    }
725
726    #[test]
727    fn test_oidc_full_properties() {
728        // OIDC shares similar structure with OAuth2
729        let json = r#"{
730            "authority": "https://oidc.example.com",
731            "grant": "authorization_code",
732            "scopes": ["openid", "profile"],
733            "client": {"id": "oidc-client"}
734        }"#;
735        let oidc: OpenIDConnectSchemeDefinition = serde_json::from_str(json).unwrap();
736        assert_eq!(oidc.authority, Some("https://oidc.example.com".to_string()));
737        assert_eq!(oidc.grant, Some("authorization_code".to_string()));
738        assert_eq!(
739            oidc.scopes,
740            Some(vec!["openid".to_string(), "profile".to_string()])
741        );
742        assert!(oidc.client.is_some());
743    }
744
745    #[test]
746    fn test_oidc_roundtrip() {
747        let json = r#"{
748            "authority": "https://oidc.example.com",
749            "grant": "authorization_code",
750            "scopes": ["openid"]
751        }"#;
752        let oidc: OpenIDConnectSchemeDefinition = serde_json::from_str(json).unwrap();
753        let serialized = serde_json::to_string(&oidc).unwrap();
754        let deserialized: OpenIDConnectSchemeDefinition =
755            serde_json::from_str(&serialized).unwrap();
756        assert_eq!(oidc, deserialized);
757    }
758
759    #[test]
760    fn test_certificate_auth() {
761        let json = r#"{"use": "certSecret"}"#;
762        let cert: CertificateAuthenticationSchemeDefinition = serde_json::from_str(json).unwrap();
763        assert_eq!(cert.use_, Some("certSecret".to_string()));
764        assert_eq!(AuthenticationScheme::CERTIFICATE, "Certificate");
765    }
766
767    #[test]
768    fn test_auth_policy_bearer_inline() {
769        // Full auth policy with bearer token
770        let json = r#"{
771            "bearer": {
772                "token": "my-bearer-token"
773            }
774        }"#;
775        let policy: AuthenticationPolicyDefinition = serde_json::from_str(json).unwrap();
776        assert!(policy.bearer.is_some());
777        let bearer = policy.bearer.as_ref().unwrap();
778        assert_eq!(bearer.token, Some("my-bearer-token".to_string()));
779        assert!(bearer.use_.is_none());
780    }
781
782    #[test]
783    fn test_auth_policy_certificate_inline() {
784        let json = r#"{
785            "certificate": {
786                "use": "myCertSecret"
787            }
788        }"#;
789        let policy: AuthenticationPolicyDefinition = serde_json::from_str(json).unwrap();
790        assert!(policy.certificate.is_some());
791        let cert = policy.certificate.as_ref().unwrap();
792        assert_eq!(cert.use_, Some("myCertSecret".to_string()));
793    }
794
795    #[test]
796    fn test_oauth2_client_assertion() {
797        // OAuth2 client with JWT assertion
798        let json = r#"{
799            "id": "client-id",
800            "assertion": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
801            "authentication": "private_key_jwt"
802        }"#;
803        let client: OAuth2AuthenticationClientDefinition = serde_json::from_str(json).unwrap();
804        assert_eq!(client.id, Some("client-id".to_string()));
805        assert_eq!(
806            client.assertion,
807            Some("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...".to_string())
808        );
809        assert_eq!(client.authentication, Some("private_key_jwt".to_string()));
810    }
811
812    #[test]
813    fn test_oauth2_token_definition() {
814        let json = r#"{
815            "token": "my-token-value",
816            "type": "urn:ietf:params:oauth:token-type:access_token"
817        }"#;
818        let token: OAuth2TokenDefinition = serde_json::from_str(json).unwrap();
819        assert_eq!(token.token, "my-token-value");
820        assert_eq!(token.type_, "urn:ietf:params:oauth:token-type:access_token");
821    }
822}