From 7248f65c36b5c2dedc336a75057925245113c979 Mon Sep 17 00:00:00 2001 From: Luis Pater Date: Thu, 15 Jan 2026 14:05:23 +0800 Subject: [PATCH] feat(auth): prevent filestore writes on unchanged metadata - Added `metadataEqualIgnoringTimestamps` to compare metadata while ignoring volatile fields. - Prevented redundant writes caused by changes in timestamp-related fields. - Improved efficiency in filestore operations by skipping unnecessary updates. --- sdk/auth/filestore.go | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/sdk/auth/filestore.go b/sdk/auth/filestore.go index 9634fa37..db9f7148 100644 --- a/sdk/auth/filestore.go +++ b/sdk/auth/filestore.go @@ -8,6 +8,7 @@ import ( "net/http" "os" "path/filepath" + "reflect" "strings" "sync" "time" @@ -72,9 +73,12 @@ func (s *FileTokenStore) Save(ctx context.Context, auth *cliproxyauth.Auth) (str if errMarshal != nil { return "", fmt.Errorf("auth filestore: marshal metadata failed: %w", errMarshal) } - if _, errRead := os.ReadFile(path); errRead == nil { + if existing, errRead := os.ReadFile(path); errRead == nil { // Use metadataEqualIgnoringTimestamps to skip writes when only timestamp fields change. // This prevents the token refresh loop caused by timestamp/expired/expires_in changes. + if metadataEqualIgnoringTimestamps(existing, raw) { + return path, nil + } file, errOpen := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0o600) if errOpen != nil { return "", fmt.Errorf("auth filestore: open existing failed: %w", errOpen) @@ -295,3 +299,29 @@ func (s *FileTokenStore) baseDirSnapshot() string { defer s.dirLock.RUnlock() return s.baseDir } + +// metadataEqualIgnoringTimestamps compares two metadata JSON blobs, ignoring volatile fields that +// change on every refresh but don't affect authentication logic. +func metadataEqualIgnoringTimestamps(a, b []byte) bool { + var objA map[string]any + var objB map[string]any + if errUnmarshalA := json.Unmarshal(a, &objA); errUnmarshalA != nil { + return false + } + if errUnmarshalB := json.Unmarshal(b, &objB); errUnmarshalB != nil { + return false + } + stripVolatileMetadataFields(objA) + stripVolatileMetadataFields(objB) + return reflect.DeepEqual(objA, objB) +} + +func stripVolatileMetadataFields(metadata map[string]any) { + if metadata == nil { + return + } + // These fields change on refresh and would otherwise trigger watcher reload loops. + for _, field := range []string{"timestamp", "expired", "expires_in", "last_refresh", "access_token"} { + delete(metadata, field) + } +}