@@ -3,8 +3,7 @@ use std::time::Duration;
33
44use forge_app:: { AuthStrategy , ProviderAuthService , StrategyFactory } ;
55use forge_domain:: {
6- AuthContextRequest , AuthContextResponse , AuthCredential , AuthMethod , Provider , ProviderId ,
7- ProviderRepository ,
6+ AuthContextRequest , AuthContextResponse , AuthMethod , Provider , ProviderId , ProviderRepository ,
87} ;
98
109/// Forge Provider Authentication Service
@@ -103,37 +102,74 @@ where
103102 self . infra . upsert_credential ( credential) . await
104103 }
105104
106- /// Refresh provider credential
105+ /// Refreshes provider credentials if they're about to expire.
106+ /// Checks if credential needs refresh (5 minute buffer before expiry),
107+ /// iterates through provider's auth methods, and attempts to refresh.
108+ /// Returns the provider with updated credentials, or original if refresh
109+ /// fails or isn't needed.
107110 async fn refresh_provider_credential (
108111 & self ,
109- provider : & Provider < url:: Url > ,
110- auth_method : AuthMethod ,
111- ) -> anyhow:: Result < AuthCredential > {
112- // Get existing credential
113- let credential = self
114- . infra
115- . get_credential ( & provider. id )
116- . await ?
117- . ok_or_else ( || forge_domain:: Error :: ProviderNotAvailable {
118- provider : provider. id . clone ( ) ,
119- } ) ?;
120-
121- // Get required params (only used for API key, but needed for factory)
122- let required_params = if matches ! ( auth_method, AuthMethod :: ApiKey ) {
123- provider. url_params . clone ( )
124- } else {
125- vec ! [ ]
126- } ;
127-
128- // Create strategy and refresh credential
129- let strategy =
130- self . infra
131- . create_auth_strategy ( provider. id . clone ( ) , auth_method, required_params) ?;
132- let refreshed = strategy. refresh ( & credential) . await ?;
133-
134- // Store refreshed credential
135- self . infra . upsert_credential ( refreshed. clone ( ) ) . await ?;
112+ mut provider : Provider < url:: Url > ,
113+ ) -> anyhow:: Result < Provider < url:: Url > > {
114+ // Check if credential needs refresh (5 minute buffer before expiry)
115+ if let Some ( credential) = & provider. credential {
116+ let buffer = chrono:: Duration :: minutes ( 5 ) ;
117+
118+ if credential. needs_refresh ( buffer) {
119+ // Iterate through auth methods and try to refresh
120+ for auth_method in & provider. auth_methods {
121+ match auth_method {
122+ AuthMethod :: OAuthDevice ( _) | AuthMethod :: OAuthCode ( _) => {
123+ // Get existing credential
124+ let existing_credential =
125+ self . infra . get_credential ( & provider. id ) . await ?. ok_or_else (
126+ || forge_domain:: Error :: ProviderNotAvailable {
127+ provider : provider. id . clone ( ) ,
128+ } ,
129+ ) ?;
130+
131+ // Get required params (only used for API key, but needed for factory)
132+ let required_params = if matches ! ( auth_method, AuthMethod :: ApiKey ) {
133+ provider. url_params . clone ( )
134+ } else {
135+ vec ! [ ]
136+ } ;
137+
138+ // Create strategy and refresh credential
139+ if let Ok ( strategy) = self . infra . create_auth_strategy (
140+ provider. id . clone ( ) ,
141+ auth_method. clone ( ) ,
142+ required_params,
143+ ) {
144+ match strategy. refresh ( & existing_credential) . await {
145+ Ok ( refreshed) => {
146+ // Store refreshed credential
147+ if self
148+ . infra
149+ . upsert_credential ( refreshed. clone ( ) )
150+ . await
151+ . is_err ( )
152+ {
153+ continue ;
154+ }
155+
156+ // Update provider with refreshed credential
157+ provider. credential = Some ( refreshed) ;
158+ break ; // Success, stop trying other methods
159+ }
160+ Err ( _) => {
161+ // If refresh fails, continue with
162+ // existing credentials
163+ }
164+ }
165+ }
166+ }
167+ _ => { }
168+ }
169+ }
170+ }
171+ }
136172
137- Ok ( refreshed )
173+ Ok ( provider )
138174 }
139175}
0 commit comments