11use std:: {
2- sync:: Arc ,
3- time:: { Duration , Instant } ,
2+ collections:: HashMap , sync:: Arc , time:: { Duration , Instant }
43} ;
54
65use oauth2:: {
@@ -70,6 +69,9 @@ pub struct AuthorizationMetadata {
7069 pub issuer : Option < String > ,
7170 pub jwks_uri : Option < String > ,
7271 pub scopes_supported : Option < Vec < String > > ,
72+ // allow additional fields
73+ #[ serde( flatten) ]
74+ pub additional_fields : HashMap < String , serde_json:: Value > ,
7375}
7476
7577/// oauth2 client config
@@ -124,9 +126,12 @@ pub struct ClientRegistrationRequest {
124126#[ derive( Debug , Clone , Serialize , Deserialize ) ]
125127pub struct ClientRegistrationResponse {
126128 pub client_id : String ,
127- pub client_secret : String ,
129+ pub client_secret : Option < String > ,
128130 pub client_name : String ,
129131 pub redirect_uris : Vec < String > ,
132+ // allow additional fields
133+ #[ serde( flatten) ]
134+ pub additional_fields : HashMap < String , serde_json:: Value > ,
130135}
131136
132137impl AuthorizationManager {
@@ -191,10 +196,21 @@ impl AuthorizationManager {
191196 issuer : None ,
192197 jwks_uri : None ,
193198 scopes_supported : None ,
199+ additional_fields : HashMap :: new ( ) ,
194200 } )
195201 }
196202 }
197203
204+ pub async fn get_credentials ( & self ) -> Result < ( String , Option < OAuthTokenResponse > ) , AuthError > {
205+ let credentials = self . credentials . read ( ) . await ;
206+ let client_id = self
207+ . oauth_client
208+ . as_ref ( )
209+ . ok_or_else ( || AuthError :: InternalError ( "OAuth client not configured" . to_string ( ) ) ) ?
210+ . client_id ( ) ;
211+ Ok ( ( client_id. to_string ( ) , credentials. clone ( ) ) )
212+ }
213+
198214 /// configure oauth2 client with client credentials
199215 pub fn configure_client ( & mut self , config : OAuthClientConfig ) -> Result < ( ) , AuthError > {
200216 if self . metadata . is_none ( ) {
@@ -287,6 +303,8 @@ impl AuthorizationManager {
287303 status, error_text
288304 ) ) ) ;
289305 }
306+
307+
290308 debug ! ( "registration response: {:?}" , response) ;
291309 let reg_response = match response. json :: < ClientRegistrationResponse > ( ) . await {
292310 Ok ( response) => response,
@@ -301,7 +319,7 @@ impl AuthorizationManager {
301319
302320 let config = OAuthClientConfig {
303321 client_id : reg_response. client_id ,
304- client_secret : Some ( reg_response. client_secret ) ,
322+ client_secret : reg_response. client_secret ,
305323 redirect_uri : redirect_uri. to_string ( ) ,
306324 scopes : vec ! [ ] ,
307325 } ;
@@ -310,6 +328,23 @@ impl AuthorizationManager {
310328 Ok ( config)
311329 }
312330
331+
332+ /// use provided client id to configure oauth2 client instead of dynamic registration
333+ /// this is useful when you have a stored client id from previous registration
334+ pub fn configure_client_id (
335+ & mut self ,
336+ client_id : & str ,
337+ ) -> Result < ( ) , AuthError > {
338+ let config = OAuthClientConfig {
339+ client_id : client_id. to_string ( ) ,
340+ client_secret : None ,
341+ scopes : vec ! [ ] ,
342+ redirect_uri : self . base_url . to_string ( ) ,
343+ } ;
344+ self . configure_client ( config)
345+ }
346+
347+
313348 /// generate authorization url
314349 pub async fn get_authorization_url ( & self , scopes : & [ & str ] ) -> Result < String , AuthError > {
315350 let oauth_client = self
@@ -513,6 +548,12 @@ impl AuthorizationSession {
513548 } )
514549 }
515550
551+
552+ /// get client_id and credentials
553+ pub async fn get_credentials ( & self ) -> Result < ( String , Option < OAuthTokenResponse > ) , AuthError > {
554+ self . auth_manager . get_credentials ( ) . await
555+ }
556+
516557 /// get authorization url
517558 pub fn get_authorization_url ( & self ) -> & str {
518559 & self . auth_url
@@ -590,9 +631,52 @@ impl OAuthState {
590631 if let Some ( client) = client {
591632 manager. with_client ( client) ?;
592633 }
634+
593635 Ok ( OAuthState :: Unauthorized ( manager) )
594636 }
595637
638+ /// Get client_id and OAuth credentials
639+ pub async fn get_credentials ( & self ) -> Result < ( String , Option < OAuthTokenResponse > ) , AuthError > {
640+ // return client_id and credentials
641+ match self {
642+ OAuthState :: Unauthorized ( manager) => manager. get_credentials ( ) . await ,
643+ OAuthState :: Session ( session) => session. get_credentials ( ) . await ,
644+ OAuthState :: Authorized ( manager) => manager. get_credentials ( ) . await ,
645+ OAuthState :: AuthorizedHttpClient ( client) => client. auth_manager . get_credentials ( ) . await ,
646+ }
647+
648+ }
649+
650+
651+ /// Manually set credentials and move into authorized state
652+ /// Useful if you're caching credentials externally and wish to reuse them
653+ pub async fn set_credentials (
654+ & mut self ,
655+ client_id : & str ,
656+ credentials : OAuthTokenResponse ,
657+ ) -> Result < ( ) , AuthError > {
658+ if let OAuthState :: Unauthorized ( manager) = self {
659+ let mut manager = std:: mem:: replace ( manager, AuthorizationManager :: new ( "http://localhost" ) . await ?) ;
660+
661+ // write credentials
662+ * manager. credentials . write ( ) . await = Some ( credentials) ;
663+
664+ // discover metadata
665+ let metadata = manager. discover_metadata ( ) . await ?;
666+ manager. metadata = Some ( metadata) ;
667+
668+ // set client id and secret
669+ manager. configure_client_id ( client_id) ?;
670+
671+ * self = OAuthState :: Authorized ( manager) ;
672+ Ok ( ( ) )
673+ } else {
674+ Err ( AuthError :: InternalError (
675+ "Cannot set credentials in this state" . to_string ( ) ,
676+ ) )
677+ }
678+ }
679+
596680 /// start authorization
597681 pub async fn start_authorization (
598682 & mut self ,
0 commit comments