@@ -15,6 +15,7 @@ import (
1515 "github.com/cozy/cozy-stack/pkg/consts"
1616 "github.com/cozy/cozy-stack/pkg/couchdb"
1717 "github.com/cozy/cozy-stack/pkg/couchdb/mango"
18+ "github.com/cozy/cozy-stack/pkg/logger"
1819 "github.com/cozy/cozy-stack/pkg/prefixer"
1920 "github.com/labstack/echo/v4"
2021)
@@ -338,57 +339,116 @@ func (at *AccountType) RequestAccessToken(i *instance.Instance, accessCode, stat
338339// RefreshAccount requires a new AccessToken using the RefreshToken
339340// as specified in https://tools.ietf.org/html/rfc6749#section-6
340341func (at * AccountType ) RefreshAccount (a Account ) error {
342+ log := logger .WithNamespace ("account-refresh" )
343+
341344 if a .Oauth == nil {
345+ log .Debugf ("Account %s: no OAuth info, cannot refresh" , a .ID ())
342346 return ErrUnrefreshable
343347 }
344348
345349 // If no endpoint is specified for the account type, the stack just sends
346350 // the client ID and client secret to the konnector and let it fetch the
347351 // token its-self.
348352 if a .Oauth .RefreshToken == "" {
353+ log .Debugf ("Account %s (type: %s): no refresh token, delegating to konnector" , a .ID (), at .Slug )
349354 a .Oauth .ClientID = at .ClientID
350355 a .Oauth .ClientSecret = at .ClientSecret
351356 return nil
352357 }
353358
354- res , err := http .PostForm (at .TokenEndpoint , url.Values {
359+ authMode := at .TokenAuthMode
360+ if authMode == "" {
361+ authMode = FormTokenAuthMode
362+ }
363+ log .Debugf ("Account %s (type: %s): refreshing OAuth token using %s auth mode" , a .ID (), at .Slug , authMode )
364+
365+ params := url.Values {
355366 "grant_type" : []string {RefreshToken },
356367 "refresh_token" : []string {a .Oauth .RefreshToken },
357- "client_id" : []string {at .ClientID },
358- "client_secret" : []string {at .ClientSecret },
359- })
368+ }
369+ if authMode != BasicTokenAuthMode {
370+ params .Add ("client_id" , at .ClientID )
371+ params .Add ("client_secret" , at .ClientSecret )
372+ }
373+
374+ var res * http.Response
375+ var err error
376+
377+ switch authMode {
378+ case BasicTokenAuthMode :
379+ log .Debugf ("Account %s: using Basic authentication mode" , a .ID ())
380+ var req * http.Request
381+ req , err = http .NewRequest ("POST" , at .TokenEndpoint , strings .NewReader (params .Encode ()))
382+ if err != nil {
383+ log .Errorf ("Account %s: failed to create POST request: %s" , a .ID (), err )
384+ return err
385+ }
386+ req .Header .Add (echo .HeaderContentType , echo .MIMEApplicationForm )
387+ req .Header .Add (echo .HeaderAccept , echo .MIMEApplicationJSON )
388+ auth := []byte (at .ClientID + ":" + at .ClientSecret )
389+ req .Header .Add (echo .HeaderAuthorization , "Basic " + base64 .StdEncoding .EncodeToString (auth ))
390+ res , err = accountsClient .Do (req )
391+ case GetTokenAuthMode :
392+ log .Debugf ("Account %s: using GET authentication mode" , a .ID ())
393+ var req * http.Request
394+ req , err = http .NewRequest ("GET" , at .TokenEndpoint + "?" + params .Encode (), nil )
395+ if err != nil {
396+ log .Errorf ("Account %s: failed to create GET request: %s" , a .ID (), err )
397+ return err
398+ }
399+ res , err = accountsClient .Do (req )
400+ default :
401+ log .Debugf ("Account %s: using form-based authentication mode" , a .ID ())
402+ res , err = http .PostForm (at .TokenEndpoint , params )
403+ }
360404
361405 if err != nil {
406+ log .Errorf ("Account %s: HTTP request to token endpoint failed: %s" , a .ID (), err )
407+ return err
408+ }
409+
410+ log .Debugf ("Account %s: token endpoint responded with status %d" , a .ID (), res .StatusCode )
411+ defer res .Body .Close ()
412+
413+ resBody , err := io .ReadAll (res .Body )
414+ if err != nil {
415+ log .Errorf ("Account %s: failed to read response body: %s" , a .ID (), err )
362416 return err
363417 }
364418
365419 if res .StatusCode != 200 {
366- resBody , _ := io . ReadAll ( res .Body )
367- return errors . New ("oauth services responded with non-200 res: " + string (resBody ))
420+ log . Errorf ( "Account %s: OAuth service error (status %d): %s" , a . ID (), res .StatusCode , string ( resBody ) )
421+ return fmt . Errorf ("oauth service responded with status %d: %s" , res . StatusCode , string (resBody ))
368422 }
369423
370424 var out tokenEndpointResponse
371- err = json .NewDecoder ( res . Body ). Decode ( & out )
425+ err = json .Unmarshal ( resBody , & out )
372426 if err != nil {
427+ log .Errorf ("Account %s: failed to parse JSON response: %s" , a .ID (), err )
373428 return err
374429 }
375430
376431 if out .Error != "" {
432+ log .Errorf ("Account %s: OAuth error from service: %s - %s" , a .ID (), out .Error , out .ErrorDescription )
377433 return fmt .Errorf ("OauthError(%s) %s" , out .Error , out .ErrorDescription )
378434 }
379435
380436 if out .AccessToken != "" {
437+ log .Debugf ("Account %s: successfully received new access token" , a .ID ())
381438 a .Oauth .AccessToken = out .AccessToken
382439 }
383440
384441 if out .ExpiresIn != 0 {
385442 a .Oauth .ExpiresAt = time .Now ().Add (time .Duration (out .ExpiresIn ) * time .Second )
443+ log .Debugf ("Account %s: token expires in %d seconds" , a .ID (), out .ExpiresIn )
386444 }
387445
388446 if out .RefreshToken != "" {
447+ log .Debugf ("Account %s: received new refresh token" , a .ID ())
389448 a .Oauth .RefreshToken = out .RefreshToken
390449 }
391450
451+ log .Infof ("Account %s: successfully refreshed OAuth token" , a .ID ())
392452 return nil
393453}
394454
0 commit comments