lib/oauthutil: handle fatal errors better

PR #6678
This commit is contained in:
Alex Chen 2023-01-12 00:50:14 +08:00 committed by GitHub
parent 02d6d28ec4
commit d66f5e8db0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 36 additions and 1 deletions

View File

@ -204,6 +204,37 @@ func (ts *TokenSource) reReadToken() (changed bool) {
return changed
}
type retrieveErrResponse struct {
Error string `json:"error"`
}
// If err is nil or an error other than fatal OAuth errors, returns err itself.
// Otherwise returns a more user-friendly error.
func maybeWrapOAuthError(err error, remoteName string) (newErr error) {
newErr = err
if rErr, ok := err.(*oauth2.RetrieveError); ok {
if rErr.Response.StatusCode == 400 || rErr.Response.StatusCode == 401 {
fs.Debugf(remoteName, "got fatal oauth error: %v", rErr)
var resp retrieveErrResponse
if err = json.Unmarshal(rErr.Body, &resp); err != nil {
newErr = fmt.Errorf("(can't decode error info) - try refreshing token with \"rclone config reconnect %s:\"", remoteName)
return
}
var suggestion string
switch resp.Error {
case "invalid_client", "unauthorized_client", "unsupported_grant_type", "invalid_scope":
suggestion = fmt.Sprintf("if you're using your own client id/secret, make sure they're properly set up following the docs")
case "invalid_grant":
fallthrough
default:
suggestion = fmt.Sprintf("maybe token expired? - try refreshing with \"rclone config reconnect %s:\"", remoteName)
}
newErr = fmt.Errorf("%s: %s", resp.Error, suggestion)
}
}
return
}
// Token returns a token or an error.
// Token must be safe for concurrent use by multiple goroutines.
// The returned Token must not be modified.
@ -242,11 +273,15 @@ func (ts *TokenSource) Token() (*oauth2.Token, error) {
if err == nil {
break
}
if newErr := maybeWrapOAuthError(err, ts.name); newErr != err {
err = newErr // Fatal OAuth error
break
}
fs.Debugf(ts.name, "Token refresh failed try %d/%d: %v", i, maxTries, err)
time.Sleep(1 * time.Second)
}
if err != nil {
return nil, fmt.Errorf("couldn't fetch token - maybe it has expired? - refresh with \"rclone config reconnect %s:\": %w", ts.name, err)
return nil, fmt.Errorf("couldn't fetch token: %w", err)
}
changed = changed || (*token != *ts.token)
ts.token = token