@@ -10,11 +10,11 @@ use camino::Utf8PathBuf;
1010use  mas_iana:: jose:: JsonWebSignatureAlg ; 
1111use  schemars:: JsonSchema ; 
1212use  serde:: { Deserialize ,  Serialize ,  de:: Error } ; 
13- use  serde_with:: skip_serializing_none; 
13+ use  serde_with:: { serde_as ,   skip_serializing_none} ; 
1414use  ulid:: Ulid ; 
1515use  url:: Url ; 
1616
17- use  crate :: ConfigurationSection ; 
17+ use  crate :: { ConfigurationSection ,   ClientSecret ,   ClientSecretRaw } ; 
1818
1919/// Upstream OAuth 2.0 providers configuration 
2020#[ derive( Debug ,  Clone ,  Serialize ,  Deserialize ,  JsonSchema ,  Default ) ]  
@@ -475,6 +475,7 @@ impl OnBackchannelLogout {
475475} 
476476
477477/// Configuration for one upstream OAuth 2 provider. 
478+ #[ serde_as]  
478479#[ skip_serializing_none]  
479480#[ derive( Debug ,  Clone ,  Serialize ,  Deserialize ,  JsonSchema ) ]  
480481pub  struct  Provider  { 
@@ -541,8 +542,10 @@ pub struct Provider {
541542     /// 
542543     /// Used by the `client_secret_basic`, `client_secret_post`, and 
543544     /// `client_secret_jwt` methods 
544-      #[ serde( skip_serializing_if = "Option::is_none" ) ]  
545-     pub  client_secret :  Option < String > , 
545+      #[ schemars( with = "ClientSecretRaw" ) ]  
546+     #[ serde_as( as  = "serde_with::TryFromInto<ClientSecretRaw>" ) ]  
547+     #[ serde( flatten) ]  
548+     pub  client_secret :  Option < ClientSecret > , 
546549
547550    /// The method to authenticate the client with the provider 
548551     pub  token_endpoint_auth_method :  TokenAuthMethod , 
@@ -656,3 +659,110 @@ pub struct Provider {
656659     #[ serde( default ,  skip_serializing_if = "OnBackchannelLogout::is_default" ) ]  
657660    pub  on_backchannel_logout :  OnBackchannelLogout , 
658661} 
662+ 
663+ impl  Provider  { 
664+     /// Returns the client secret. 
665+      /// 
666+      /// If `client_secret_file` was given, the secret is read from that file. 
667+      /// 
668+      /// # Errors 
669+      /// 
670+      /// Returns an error when the client secret could not be read from file. 
671+      pub  async  fn  client_secret ( & self )  -> anyhow:: Result < Option < String > >  { 
672+         Ok ( match  & self . client_secret  { 
673+             Some ( client_secret)  => Some ( client_secret. value ( ) . await ?) , 
674+             None  => None , 
675+         } ) 
676+     } 
677+ } 
678+ 
679+ #[ cfg( test) ]  
680+ mod  tests { 
681+     use  std:: str:: FromStr ; 
682+ 
683+     use  figment:: { 
684+         Figment ,  Jail , 
685+         providers:: { Format ,  Yaml } , 
686+     } ; 
687+     use  tokio:: { runtime:: Handle ,  task} ; 
688+ 
689+     use  super :: * ; 
690+ 
691+     #[ tokio:: test]  
692+     async  fn  load_config ( )  { 
693+         task:: spawn_blocking ( || { 
694+             Jail :: expect_with ( |jail| { 
695+                 jail. create_file ( 
696+                     "config.yaml" , 
697+                     r#" 
698+                       upstream_oauth2: 
699+                         providers: 
700+                           - id: 01GFWR28C4KNE04WG3HKXB7C9R 
701+                             client_id: upstream-oauth2 
702+                             token_endpoint_auth_method: none 
703+ 
704+                           - id: 01GFWR32NCQ12B8Z0J8CPXRRB6 
705+                             client_id: upstream-oauth2 
706+                             client_secret_file: secret 
707+                             token_endpoint_auth_method: client_secret_basic 
708+ 
709+                           - id: 01GFWR3WHR93Y5HK389H28VHZ9 
710+                             client_id: upstream-oauth2 
711+                             client_secret: c1!3n753c237 
712+                             token_endpoint_auth_method: client_secret_post 
713+ 
714+                           - id: 01GFWR43R2ZZ8HX9CVBNW9TJWG 
715+                             client_id: upstream-oauth2 
716+                             client_secret_file: secret 
717+                             token_endpoint_auth_method: client_secret_jwt 
718+ 
719+                           - id: 01GFWR4BNFDCC4QDG6AMSP1VRR 
720+                             client_id: upstream-oauth2 
721+                             token_endpoint_auth_method: private_key_jwt 
722+                             jwks: 
723+                               keys: 
724+                               - kid: "03e84aed4ef4431014e8617567864c4efaaaede9" 
725+                                 kty: "RSA" 
726+                                 alg: "RS256" 
727+                                 use: "sig" 
728+                                 e: "AQAB" 
729+                                 n: "ma2uRyBeSEOatGuDpCiV9oIxlDWix_KypDYuhQfEzqi_BiF4fV266OWfyjcABbam59aJMNvOnKW3u_eZM-PhMCBij5MZ-vcBJ4GfxDJeKSn-GP_dJ09rpDcILh8HaWAnPmMoi4DC0nrfE241wPISvZaaZnGHkOrfN_EnA5DligLgVUbrA5rJhQ1aSEQO_gf1raEOW3DZ_ACU3qhtgO0ZBG3a5h7BPiRs2sXqb2UCmBBgwyvYLDebnpE7AotF6_xBIlR-Cykdap3GHVMXhrIpvU195HF30ZoBU4dMd-AeG6HgRt4Cqy1moGoDgMQfbmQ48Hlunv9_Vi2e2CLvYECcBw" 
730+ 
731+                               - kid: "d01c1abe249269f72ef7ca2613a86c9f05e59567" 
732+                                 kty: "RSA" 
733+                                 alg: "RS256" 
734+                                 use: "sig" 
735+                                 e: "AQAB" 
736+                                 n: "0hukqytPwrj1RbMYhYoepCi3CN5k7DwYkTe_Cmb7cP9_qv4ok78KdvFXt5AnQxCRwBD7-qTNkkfMWO2RxUMBdQD0ED6tsSb1n5dp0XY8dSWiBDCX8f6Hr-KolOpvMLZKRy01HdAWcM6RoL9ikbjYHUEW1C8IJnw3MzVHkpKFDL354aptdNLaAdTCBvKzU9WpXo10g-5ctzSlWWjQuecLMQ4G1mNdsR1LHhUENEnOvgT8cDkX0fJzLbEbyBYkdMgKggyVPEB1bg6evG4fTKawgnf0IDSPxIU-wdS9wdSP9ZCJJPLi5CEp-6t6rE_sb2dGcnzjCGlembC57VwpkUvyMw" 
737+                     "# , 
738+                 ) ?; 
739+                 jail. create_file ( "secret" ,  r"c1!3n753c237" ) ?; 
740+ 
741+                 let  config = Figment :: new ( ) 
742+                     . merge ( Yaml :: file ( "config.yaml" ) ) 
743+                     . extract_inner :: < UpstreamOAuth2Config > ( "upstream_oauth2" ) ?; 
744+ 
745+                 assert_eq ! ( config. providers. len( ) ,  5 ) ; 
746+ 
747+                 assert_eq ! ( 
748+                     config. providers[ 1 ] . id, 
749+                     Ulid :: from_str( "01GFWR32NCQ12B8Z0J8CPXRRB6" ) . unwrap( ) 
750+                 ) ; 
751+ 
752+                 assert ! ( config. providers[ 0 ] . client_secret. is_none( ) ) ; 
753+                 assert ! ( matches!( config. providers[ 1 ] . client_secret,  Some ( ClientSecret :: File ( ref p) )  if  p == "secret" ) ) ; 
754+                 assert ! ( matches!( config. providers[ 2 ] . client_secret,  Some ( ClientSecret :: Value ( ref v) )  if  v == "c1!3n753c237" ) ) ; 
755+                 assert ! ( matches!( config. providers[ 3 ] . client_secret,  Some ( ClientSecret :: File ( ref p) )  if  p == "secret" ) ) ; 
756+                 assert ! ( config. providers[ 4 ] . client_secret. is_none( ) ) ; 
757+ 
758+                 Handle :: current ( ) . block_on ( async  move  { 
759+                     assert_eq ! ( config. providers[ 1 ] . client_secret( ) . await . unwrap( ) . unwrap( ) ,  "c1!3n753c237" ) ; 
760+                     assert_eq ! ( config. providers[ 2 ] . client_secret( ) . await . unwrap( ) . unwrap( ) ,  "c1!3n753c237" ) ; 
761+                     assert_eq ! ( config. providers[ 3 ] . client_secret( ) . await . unwrap( ) . unwrap( ) ,  "c1!3n753c237" ) ; 
762+                 } ) ; 
763+ 
764+                 Ok ( ( ) ) 
765+             } ) ; 
766+         } ) . await . unwrap ( ) ; 
767+     } 
768+ } 
0 commit comments