Skip to content

Commit d358f90

Browse files
author
alist-dev
committed
fix(thunder): tolerate refresh responses without rotated refresh token
After completing the review flow, saving the driver works but a restart still prompts for verification: when Init falls back to username/password login, risk control triggers the review flow again. RefreshToken() treated a refresh response without a refresh_token field as failure (EmptyToken), which sent Init back to login even when the refresh succeeded without rotating the token. - judge refresh success by access token presence, keep the old refresh token when the server does not rotate it - add diagnostics logs distinguishing: no saved refresh token, session restored via refresh token, refresh failed with fallback to login - add test for refresh response without rotated refresh token
1 parent dd472cd commit d358f90

2 files changed

Lines changed: 53 additions & 4 deletions

File tree

drivers/thunder/driver.go

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"github.com/aws/aws-sdk-go/aws/session"
2121
"github.com/aws/aws-sdk-go/service/s3/s3manager"
2222
"github.com/go-resty/resty/v2"
23+
log "github.com/sirupsen/logrus"
2324
)
2425

2526
type Thunder struct {
@@ -121,16 +122,26 @@ func (x *Thunder) Init(ctx context.Context) (err error) {
121122
// 优先使用已保存的 RefreshToken 恢复登录态,避免每次重启都触发风控验证
122123
if x.Addition.RefreshToken != "" {
123124
token, err := x.XunLeiCommon.RefreshToken(x.Addition.RefreshToken)
124-
if err == nil && token != nil && token.RefreshToken != "" {
125+
if err == nil && token != nil && token.AccessToken != "" {
125126
x.SetTokenResp(token)
126-
// 更新并持久化最新的 refresh token
127-
x.Addition.RefreshToken = token.RefreshToken
127+
// 更新并持久化最新的 refresh token(服务端未轮换时保留旧值)
128+
if token.RefreshToken != "" {
129+
x.Addition.RefreshToken = token.RefreshToken
130+
}
128131
// 清空已消费的信任密钥并落库
129132
x.Addition.CreditKey = ""
130133
op.MustSaveDriverStorage(x)
134+
log.Infof("thunder: session restored via refresh token for %s", x.MountPath)
131135
return nil
132136
}
133137
// 刷新失败,回退到账号密码登录
138+
if err != nil {
139+
log.Warnf("thunder: refresh token failed for %s: %v, fallback to login", x.MountPath, err)
140+
} else {
141+
log.Warnf("thunder: refresh token response has no access token for %s, fallback to login", x.MountPath)
142+
}
143+
} else {
144+
log.Infof("thunder: no saved refresh token for %s, login with username/password", x.MountPath)
134145
}
135146
// 登录
136147
token, err := x.Login(x.Username, x.Password)
@@ -142,6 +153,7 @@ func (x *Thunder) Init(ctx context.Context) (err error) {
142153
x.SetTokenResp(token)
143154
x.Addition.RefreshToken = token.RefreshToken
144155
op.MustSaveDriverStorage(x)
156+
log.Infof("thunder: logged in for %s", x.MountPath)
145157
}
146158
return nil
147159
}
@@ -544,7 +556,9 @@ func (xc *XunLeiCommon) RefreshToken(refreshToken string) (*TokenResp, error) {
544556
return nil, err
545557
}
546558

547-
if resp.RefreshToken == "" {
559+
// 以 access token 是否有效作为刷新成功的判据:
560+
// 部分场景下服务端不轮换 refresh token(响应里该字段为空),此时保留旧值即可
561+
if resp.AccessToken == "" {
548562
return nil, errs.EmptyToken
549563
}
550564
return &resp, nil

drivers/thunder/driver_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,41 @@ func TestThunderInitRestoresSessionWithRefreshToken(t *testing.T) {
118118
}
119119
}
120120

121+
func TestThunderInitRestoresSessionWithoutRotatedRefreshToken(t *testing.T) {
122+
ms := newMockServer()
123+
ms.handler = func(path string, w http.ResponseWriter, r *http.Request) {
124+
switch path {
125+
case "/v1/auth/token":
126+
// 服务端刷新成功但未轮换 refresh token(响应里该字段为空)
127+
writeJSON(w, TokenResp{
128+
TokenType: "Bearer", AccessToken: "at-1",
129+
ExpiresIn: 7200, UserID: "u1",
130+
})
131+
default:
132+
http.NotFound(w, r)
133+
}
134+
}
135+
defer ms.Close()
136+
137+
x := newTestThunder("thunder-test-no-rotate")
138+
x.Addition.RefreshToken = "rt-old"
139+
140+
if err := x.Init(nil); err != nil {
141+
t.Fatalf("Init: %v", err)
142+
}
143+
// 刷新成功:不应回退到账号密码登录
144+
if got := ms.Paths(); !reflect.DeepEqual(got, []string{"/v1/auth/token"}) {
145+
t.Fatalf("unexpected requests: %v", got)
146+
}
147+
// refresh token 未轮换时应保留旧值
148+
if x.Addition.RefreshToken != "rt-old" {
149+
t.Fatalf("RefreshToken = %q, want preserved %q", x.Addition.RefreshToken, "rt-old")
150+
}
151+
if x.Token() != "Bearer at-1" {
152+
t.Fatalf("Token() = %q, want %q", x.Token(), "Bearer at-1")
153+
}
154+
}
155+
121156
func TestThunderInitFallsBackToLoginAndPersists(t *testing.T) {
122157
ms := newMockServer()
123158
ms.handler = func(path string, w http.ResponseWriter, r *http.Request) {

0 commit comments

Comments
 (0)