Skip to content

fetchQuota 的 token 守卫未判空串,新装设备会发出空 Bearer 请求 #46

Description

@yunkst

位置lib/services/device/device_auth_service.dart:167-168

Future<DeviceQuotaInfo?> fetchQuota() async {
  if (!kHasBundledBackend) return null;
  final token = _cachedToken;
  if (token == null) return null;          // ← 只判 null,未判空串
  return fetchMeWithToken(token);
}

问题token 守卫只过滤 null,没过滤空串。loadCached()(line 80-82)走 PreferencesService.instance.getString(_kDeviceToken),而该方法在 key 缺失时默认返回 ''(见 preferences_service.dart:46-54)。新装/未注册设备首次启动后 _cachedToken'' 而非 null,守卫被绕过,fetchMeWithToken('') 会用 Authorization: Bearer (空)请求 /api/v1/devices/me,后端必 401。

影响

  1. 每次新装用户首次进入「AI 配置」页都会发起一个注定 401 的请求,浪费一次 RTT、污染后端日志。
  2. fetchMeWithToken 收到 401 后会打 LoggerService warning 日志(device/quota 标签),增加无意义告警量。
  3. 测试 device_auth_quota_test.dart:155-173 的「无缓存 token 守卫」用例因 kHasBundledBackend 是编译期常量(build_config.dart:20)而被死代码化——守卫逻辑实际从未在测试中真正执行过,未来翻车也没回归兜底。

修复建议

final token = _cachedToken;
if (token == null || token.isEmpty) return null;

顺手把 _cachedToken 的空串状态在 loadCached 里就归一化为 null(与字段默认一致),避免后续 ensureRegistered 等其它路径也踩同样的坑(仓库中 ensureRegisteredif (cached != null) return cached; 同样会返回空串,性质一致;本次仅修 fetchQuota,剩余路径可另开 issue)。


忽略指南:在 lib/services/device/device_auth_service.dart:168 添加注释 // cr-ignore <CR_IGNORE_IID_HASH>: <你的理由>,下次审查会自动关闭。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions