From 66761670daf8a8780a8b1afd8584e972f2307058 Mon Sep 17 00:00:00 2001 From: 0rangeSeaW0lf <0sw-8m4k2v@waku.li> Date: Wed, 8 Jul 2026 17:59:43 +0200 Subject: [PATCH] internxt: persist rotated token returned by the user info call The refresh endpoint returns a rotated token with a fresh expiry on every successful call, but getUserInfo discarded it, so routine use never extended the stored token's life. Once the stored token aged out, accounts with 2FA enabled could not recover non-interactively and required a manual reconnect. Carry the rotated token out of getUserInfo and persist it in NewFs via the same jwtToOAuth2Token + oauthutil.PutToken path that refreshJWTToken uses, keeping f.cfg.Token in sync (same pattern as refreshOrReLogin). Fixes #9584 Co-Authored-By: Claude Fable 5 --- backend/internxt/auth.go | 2 ++ backend/internxt/internxt.go | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/backend/internxt/auth.go b/backend/internxt/auth.go index 63bdb5246..73ec9b22a 100644 --- a/backend/internxt/auth.go +++ b/backend/internxt/auth.go @@ -28,6 +28,7 @@ type userInfo struct { Bucket string BridgeUser string UserID string + NewToken string } type userInfoConfig struct { @@ -61,6 +62,7 @@ func getUserInfo(ctx context.Context, cfg *userInfoConfig) (*userInfo, error) { Bucket: resp.User.Bucket, BridgeUser: resp.User.BridgeUser, UserID: resp.User.UserID, + NewToken: resp.NewToken, } fs.Debugf(nil, "User info: rootFolderId=%s, bucket=%s", diff --git a/backend/internxt/internxt.go b/backend/internxt/internxt.go index aa22e3fe1..52ed492f2 100644 --- a/backend/internxt/internxt.go +++ b/backend/internxt/internxt.go @@ -366,6 +366,25 @@ func NewFs(ctx context.Context, name, root string, m configmap.Mapper) (fs.Fs, e f.bridgeUser = userInfo.BridgeUser f.userID = userInfo.UserID + // The refresh endpoint rotates the token on every successful call. + // Persist the rotated token so routine use keeps the stored token + // current; otherwise it keeps its original expiry and accounts that + // cannot re-login non-interactively (2FA) eventually strand. + if userInfo.NewToken != "" { + if rotated, rotErr := jwtToOAuth2Token(userInfo.NewToken); rotErr != nil { + fs.Debugf(f, "Not adopting rotated token from user info: %v", rotErr) + } else { + // Use the rotated token for this session even if saving it + // fails; persistence is best-effort. + f.cfg.Token = userInfo.NewToken + if putErr := oauthutil.PutToken(name, m, rotated, false); putErr != nil { + fs.Debugf(f, "Failed to save rotated token from user info: %v", putErr) + } else { + fs.Debugf(f, "Persisted rotated token from user info, expiry: %v", rotated.Expiry) + } + } + } + f.features = (&fs.Features{ CanHaveEmptyDirectories: true, }).Fill(ctx, f)