1
1
use std:: {
2
+ collections:: HashMap ,
2
3
sync:: Arc ,
3
4
time:: { Duration , Instant } ,
4
5
} ;
@@ -70,6 +71,9 @@ pub struct AuthorizationMetadata {
70
71
pub issuer : Option < String > ,
71
72
pub jwks_uri : Option < String > ,
72
73
pub scopes_supported : Option < Vec < String > > ,
74
+ // allow additional fields
75
+ #[ serde( flatten) ]
76
+ pub additional_fields : HashMap < String , serde_json:: Value > ,
73
77
}
74
78
75
79
/// oauth2 client config
@@ -100,6 +104,7 @@ type OAuthClient = oauth2::Client<
100
104
oauth2:: EndpointNotSet ,
101
105
oauth2:: EndpointSet ,
102
106
> ;
107
+ type Credentials = ( String , Option < OAuthTokenResponse > ) ;
103
108
104
109
/// oauth2 auth manager
105
110
pub struct AuthorizationManager {
@@ -124,9 +129,12 @@ pub struct ClientRegistrationRequest {
124
129
#[ derive( Debug , Clone , Serialize , Deserialize ) ]
125
130
pub struct ClientRegistrationResponse {
126
131
pub client_id : String ,
127
- pub client_secret : String ,
132
+ pub client_secret : Option < String > ,
128
133
pub client_name : String ,
129
134
pub redirect_uris : Vec < String > ,
135
+ // allow additional fields
136
+ #[ serde( flatten) ]
137
+ pub additional_fields : HashMap < String , serde_json:: Value > ,
130
138
}
131
139
132
140
impl AuthorizationManager {
@@ -191,10 +199,22 @@ impl AuthorizationManager {
191
199
issuer : None ,
192
200
jwks_uri : None ,
193
201
scopes_supported : None ,
202
+ additional_fields : HashMap :: new ( ) ,
194
203
} )
195
204
}
196
205
}
197
206
207
+ /// get client id and credentials
208
+ pub async fn get_credentials ( & self ) -> Result < Credentials , AuthError > {
209
+ let credentials = self . credentials . read ( ) . await ;
210
+ let client_id = self
211
+ . oauth_client
212
+ . as_ref ( )
213
+ . ok_or_else ( || AuthError :: InternalError ( "OAuth client not configured" . to_string ( ) ) ) ?
214
+ . client_id ( ) ;
215
+ Ok ( ( client_id. to_string ( ) , credentials. clone ( ) ) )
216
+ }
217
+
198
218
/// configure oauth2 client with client credentials
199
219
pub fn configure_client ( & mut self , config : OAuthClientConfig ) -> Result < ( ) , AuthError > {
200
220
if self . metadata . is_none ( ) {
@@ -287,6 +307,7 @@ impl AuthorizationManager {
287
307
status, error_text
288
308
) ) ) ;
289
309
}
310
+
290
311
debug ! ( "registration response: {:?}" , response) ;
291
312
let reg_response = match response. json :: < ClientRegistrationResponse > ( ) . await {
292
313
Ok ( response) => response,
@@ -301,7 +322,7 @@ impl AuthorizationManager {
301
322
302
323
let config = OAuthClientConfig {
303
324
client_id : reg_response. client_id ,
304
- client_secret : Some ( reg_response. client_secret ) ,
325
+ client_secret : reg_response. client_secret ,
305
326
redirect_uri : redirect_uri. to_string ( ) ,
306
327
scopes : vec ! [ ] ,
307
328
} ;
@@ -310,6 +331,18 @@ impl AuthorizationManager {
310
331
Ok ( config)
311
332
}
312
333
334
+ /// use provided client id to configure oauth2 client instead of dynamic registration
335
+ /// this is useful when you have a stored client id from previous registration
336
+ pub fn configure_client_id ( & mut self , client_id : & str ) -> Result < ( ) , AuthError > {
337
+ let config = OAuthClientConfig {
338
+ client_id : client_id. to_string ( ) ,
339
+ client_secret : None ,
340
+ scopes : vec ! [ ] ,
341
+ redirect_uri : self . base_url . to_string ( ) ,
342
+ } ;
343
+ self . configure_client ( config)
344
+ }
345
+
313
346
/// generate authorization url
314
347
pub async fn get_authorization_url ( & self , scopes : & [ & str ] ) -> Result < String , AuthError > {
315
348
let oauth_client = self
@@ -513,6 +546,11 @@ impl AuthorizationSession {
513
546
} )
514
547
}
515
548
549
+ /// get client_id and credentials
550
+ pub async fn get_credentials ( & self ) -> Result < Credentials , AuthError > {
551
+ self . auth_manager . get_credentials ( ) . await
552
+ }
553
+
516
554
/// get authorization url
517
555
pub fn get_authorization_url ( & self ) -> & str {
518
556
& self . auth_url
@@ -590,9 +628,54 @@ impl OAuthState {
590
628
if let Some ( client) = client {
591
629
manager. with_client ( client) ?;
592
630
}
631
+
593
632
Ok ( OAuthState :: Unauthorized ( manager) )
594
633
}
595
634
635
+ /// Get client_id and OAuth credentials
636
+ pub async fn get_credentials ( & self ) -> Result < Credentials , AuthError > {
637
+ // return client_id and credentials
638
+ match self {
639
+ OAuthState :: Unauthorized ( manager) | OAuthState :: Authorized ( manager) => {
640
+ manager. get_credentials ( ) . await
641
+ }
642
+ OAuthState :: Session ( session) => session. get_credentials ( ) . await ,
643
+ OAuthState :: AuthorizedHttpClient ( client) => client. auth_manager . get_credentials ( ) . await ,
644
+ }
645
+ }
646
+
647
+ /// Manually set credentials and move into authorized state
648
+ /// Useful if you're caching credentials externally and wish to reuse them
649
+ pub async fn set_credentials (
650
+ & mut self ,
651
+ client_id : & str ,
652
+ credentials : OAuthTokenResponse ,
653
+ ) -> Result < ( ) , AuthError > {
654
+ if let OAuthState :: Unauthorized ( manager) = self {
655
+ let mut manager = std:: mem:: replace (
656
+ manager,
657
+ AuthorizationManager :: new ( "http://localhost" ) . await ?,
658
+ ) ;
659
+
660
+ // write credentials
661
+ * manager. credentials . write ( ) . await = Some ( credentials) ;
662
+
663
+ // discover metadata
664
+ let metadata = manager. discover_metadata ( ) . await ?;
665
+ manager. metadata = Some ( metadata) ;
666
+
667
+ // set client id and secret
668
+ manager. configure_client_id ( client_id) ?;
669
+
670
+ * self = OAuthState :: Authorized ( manager) ;
671
+ Ok ( ( ) )
672
+ } else {
673
+ Err ( AuthError :: InternalError (
674
+ "Cannot set credentials in this state" . to_string ( ) ,
675
+ ) )
676
+ }
677
+ }
678
+
596
679
/// start authorization
597
680
pub async fn start_authorization (
598
681
& mut self ,
0 commit comments