From 603fc68add2af36f1b8a9d422d22e7eabf04e709 Mon Sep 17 00:00:00 2001 From: Asela Date: Mon, 14 Aug 2023 10:51:17 +1000 Subject: [PATCH] box: Fixed refresh of tokens with OAuth2.0 and JWT If you use the authentication method OAuth2.0 with JWT on the Box backend rclone fails to refresh the token before Box expires it. If this happens mid-transfer the transfer is aborted. This fix expires the tokens from Box earlier (2 minutes) than expected. Fixes #7214 --- backend/box/box.go | 4 +++- lib/jwtutil/jwtutil.go | 4 ++-- lib/oauthutil/oauthutil.go | 13 +++++++------ 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/backend/box/box.go b/backend/box/box.go index 4f38955eb..659ecf7de 100644 --- a/backend/box/box.go +++ b/backend/box/box.go @@ -202,7 +202,9 @@ func refreshJWTToken(ctx context.Context, jsonFile string, boxSubType string, na signingHeaders := getSigningHeaders(boxConfig) queryParams := getQueryParams(boxConfig) client := fshttp.NewClient(ctx) - err = jwtutil.Config("box", name, tokenURL, *claims, signingHeaders, queryParams, privateKey, m, client) + // When using OAuth2.0 with JWT Box appears to expire their tokens earlier than expected. + // To counter this, we manually set the token to expire 2 minutes earlier than expected + err = jwtutil.Config("box", name, tokenURL, *claims, signingHeaders, queryParams, privateKey, m, client, 2*time.Minute) return err } diff --git a/lib/jwtutil/jwtutil.go b/lib/jwtutil/jwtutil.go index 43ae3f4bb..f99b0e43e 100644 --- a/lib/jwtutil/jwtutil.go +++ b/lib/jwtutil/jwtutil.go @@ -32,7 +32,7 @@ func RandomHex(n int) (string, error) { } // Config configures rclone using JWT -func Config(id, name, url string, claims jwt.Claims, headerParams map[string]interface{}, queryParams map[string]string, privateKey *rsa.PrivateKey, m configmap.Mapper, client *http.Client) (err error) { +func Config(id, name, url string, claims jwt.Claims, headerParams map[string]interface{}, queryParams map[string]string, privateKey *rsa.PrivateKey, m configmap.Mapper, client *http.Client, earlyExpire time.Duration) (err error) { jwtToken := jwt.NewWithClaims(jwt.SigningMethodRS256, claims) for key, value := range headerParams { jwtToken.Header[key] = value @@ -93,7 +93,7 @@ func Config(id, name, url string, claims jwt.Claims, headerParams map[string]int } e := result.ExpiresIn if e != 0 { - token.Expiry = time.Now().Add(time.Duration(e) * time.Second) + token.Expiry = time.Now().Add(time.Duration(e)*time.Second - earlyExpire) } return oauthutil.PutToken(name, m, token, true) } diff --git a/lib/oauthutil/oauthutil.go b/lib/oauthutil/oauthutil.go index 2f3d5278f..b1a525ae5 100644 --- a/lib/oauthutil/oauthutil.go +++ b/lib/oauthutil/oauthutil.go @@ -18,7 +18,6 @@ import ( "github.com/rclone/rclone/fs" "github.com/rclone/rclone/fs/config" "github.com/rclone/rclone/fs/config/configmap" - "github.com/rclone/rclone/fs/fserrors" "github.com/rclone/rclone/fs/fshttp" "github.com/rclone/rclone/lib/random" "github.com/skratchdot/open-golang/open" @@ -266,11 +265,13 @@ func (ts *TokenSource) Token() (*oauth2.Token, error) { if !ts.token.Valid() { if ts.reReadToken() { changed = true - } else if ts.token.RefreshToken == "" { - return nil, fserrors.FatalError( - fmt.Errorf("token expired and there's no refresh token - manually refresh with \"rclone config reconnect %s:\"", ts.name), - ) - } + } //else if ts.token.RefreshToken == "" { + // FIXME need to detect JWT here + // Box authentication OAuth2.0 with JWT does not provide refresh tokens + // return nil, fserrors.FatalError( + // fmt.Errorf("token expired and there's no refresh token - manually refresh with \"rclone config reconnect %s:\"", ts.name), + //) + //} } // Make a new token source if required