feat: harden the session lifecycle for the 1.0.0 stable release - #5
Merged
Merged
Conversation
- restore() keeps the persisted session when a renewal fails transiently, instead of clearing the store and signing the user out
- login/register/loginWith/updateSession bump the epoch, so an in-flight refresh can never overwrite the session they install
- single-flight refresh is scoped to the epoch it started in
- a renewal carries userId/displayName/claims over when the backend returns tokens only (preserveSessionDetails)
- clockSkew (default 30s) plus validAccessToken(leeway:) stop handing out tokens that die in flight
- an already-due proactive renewal is throttled by autoRefreshMinInterval instead of spinning
- mapAuthFailure reads the code of a bare AuthFail; claims compare structurally; AuthSession.tryFromJson never throws
- a TokenStore failing on save/clear can no longer strand the refresh future or leak a raw error
- AuthState.session, AuthManagerGroup tuning pass-through, activeIdChanges and restoreAll that keeps going
- onStateChanged errors are contained; the group forgets a manager disposed outside it
BREAKING CHANGE: AuthTokenSource gained validAccessToken({Duration? leeway}). Implementations that only expose accessToken must add:
@OverRide
Future<String?> validAccessToken({Duration? leeway}) async => accessToken;
- README.md / README_zh.md: version 1.0.0, stability callout, new knobs (clockSkew, preserveSessionDetails, autoRefreshMinInterval) and API tables - USAGE.md: renewing AuthTokenSource, clock skew and throttling rows, AuthFail mapping, new pitfalls - CHANGELOG.md: the 1.0.0 section with fixes, breaking change, additions and the stability promise - website: Network-Integration, Configuration, Errors, Multi-Account, Auth-State-Machine, Persistence, Token-Store, FAQ and index - KNOWN_RISKS.md: the accepted risks and the post-release watchlist (repo-internal, excluded via .pubignore) - AGENTS.md: epoch and transient-failure invariants for future agents
First stable release. The public surface is frozen under semver from here on; see the 1.0.0 changelog entry for the one migration step (AuthTokenSource.validAccessToken).
The root command skips sub-packages that own a pubspec, so server/ and example/ were never formatted before. Also re-runs the formatter with the current toolchain, which indents switch-expression cases like the rest of the code base. No behaviour change: dart analyze and all 129 tests still pass.
It has no analysis_options.yaml, so it inherited the root one, which includes package:flutter_lints - not a dependency here. Every dart command in server/ therefore printed a package-resolution warning. The new file mirrors the root rule set without the Flutter-only bits.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary / 摘要
First stable release: closes the remaining lifecycle holes (a transient renewal failure no longer signs the user out, a stale refresh can no longer overwrite a newer login) and adds clock-skew tolerance,
AuthSession.tryFromJsonand a renewingAuthTokenSource.validAccessToken().首个稳定版本:补上剩余的生命周期漏洞(续期瞬时失败不再让用户登出、迟到的刷新无法覆盖更新的登录),并新增时钟偏移容忍、
AuthSession.tryFromJson与会续期的AuthTokenSource.validAccessToken()。Changes / 变更
AuthTokenSourcegainedvalidAccessToken({Duration? leeway}). Implementations that only exposeaccessTokenmust add one line:@override Future<String?> validAccessToken({Duration? leeway}) async => accessToken;. Everything else is additive.破坏性:
AuthTokenSource新增validAccessToken({Duration? leeway})。 只暴露accessToken的实现需补一行(同上)。其余均为追加式变更。restore()keeps the persisted session on a transient renewal failure instead of clearing the store and landing onUnauthenticated— the opposite of whatrefreshFailurePolicypromised. Only a terminal failure clears it now. Concurrentrestore()calls share one attempt.restore()在续期瞬时失败时保留持久化会话,不再清空存储并落到Unauthenticated(那与refreshFailurePolicy的承诺相反)。现在只有终局失败才清空;并发restore()共享同一次尝试。login/register/loginWith/updateSessionbump the epoch, so an in-flight refresh cannot overwrite the session they install (or write back a token minted from an already-rotated refresh token). Single-flight sharing is now epoch-scoped.login/register/loginWith/updateSession会递增 epoch,进行中的刷新无法覆盖它们安装的会话(也不会写回由已轮换的刷新令牌换来的令牌)。单飞共享现在限定在同一 epoch 内。userId/displayName/claims) when the backend returns tokens only;preserveSessionDetails: falseopts out. This also fixes an emptySessionHandle.userIdon logout.续期会保留已登录身份(
userId/displayName/claims)当后端只返回令牌时;preserveSessionDetails: false可关闭。同时修复了登出时SessionHandle.userId为空的问题。clockSkew(default 30s) +validAccessToken(leeway:)stop handing out tokens that would die in flight;autoRefreshMinInterval(default 5s) throttles an already-due proactive renewal so very short-lived tokens cannot cause a tight loop.clockSkew(默认 30 秒)与validAccessToken(leeway:)不再发出途中会失效的令牌;autoRefreshMinInterval(默认 5 秒)为已到期的主动续期设下限,极短寿命令牌不会造成紧密循环。AuthSession.tryFromJsonreturnsnullon malformed input;claimsnow compare (and hash) structurally;mapAuthFailurereads the code of a bareAuthFail.AuthSession.tryFromJson遇畸形数据返回null;claims改为结构化比较与哈希;mapAuthFailure会读取裸AuthFail的 code。clear()can no longer strand the refresh future (it used to hang and leak an unhandled async error), andupdateSessionmaps a failingsave()toUnexpectedAuthException.存储失败被收敛:
clear()失败不再让刷新的 Future 悬挂(过去会挂住并泄漏未处理异步错误);updateSession中失败的save()会映射为UnexpectedAuthException。AuthManagerGroup: forwards every manager knob, addsmanagerFactory, an account-taggedonStateChanged,activeIdChanges,validAccessToken(), and arestoreAll()that keeps going when one account fails. Also forgets a manager disposed outside the group, and contains a throwingonStateChanged.AuthManagerGroup:完整转发管理器调参,新增managerFactory、带账号标记的onStateChanged、activeIdChanges、validAccessToken(),以及某账号失败也会继续的restoreAll();同时会遗忘在分组之外被释放的管理器,并隔离观察者抛出的异常。docs/rebuilt; newKNOWN_RISKS.md(repo-internal) lists the accepted risks and the post-release watchlist.文档:README / README_zh / USAGE / CHANGELOG / 站点页面与重建后的
docs/;新增KNOWN_RISKS.md(仓库内部)记录已接受的风险与发版后观察清单。Context / 背景
Ahead of tagging
1.0.0, the lifecycle was re-reviewed end to end. Four issues were reproduced with throwaway probes before being fixed: a transient renewal failure duringrestore()destroyed the persisted session; a refresh in flight overwrote a newer login; a renewal erased the identity; and a bareAuthFaillost itscode. Each now has a regression test intest/auth_manager_v1_test.dart.1.0.0freezes the public surface under semver, so the one breaking change (AuthTokenSource) had to ship now or wait for 2.0.在打
1.0.0之前对生命周期做了端到端复查。四个问题先用一次性探针复现再修复:restore()期间续期瞬时失败会毁掉持久化会话;飞行中的刷新覆盖更新的登录;续期抹掉身份;裸AuthFail丢失 code。每个都在test/auth_manager_v1_test.dart中留了回归测试。1.0.0会按 semver 冻结公共 API,因此唯一的破坏性变更(AuthTokenSource)只能现在发,否则要等到 2.0。Checklist / 检查项
dart analyze+flutter testpass / 静态分析与测试通过(dart analyze0 issue、dart test129 passed、example2 passed、server0 issue)README.mdandREADME_zh.mdkept in sync / 中英文档同步更新(含版本号四处与升级 callout)dart format .reports no changes / 格式化无变更dart pub publish --dry-run— only the "uncommitted files" warning / 发布预演仅剩「未提交文件」告警Test plan
dart analyze && dart test/ 包内静态分析与 129 个单元测试cd example && flutter analyze && flutter test/ 示例 App 分析与 widget 测试cd server && dart run bin/server.dart, thencd example && flutter run— log in withuser/user, press Call /me, then Expire in 10s and call again to watch the transparent renewal / 端到端:启动后端并运行示例,用user/user登录,点 Call /me,再点 Expire in 10s 后重新调用,观察透明续期pana≥ 120,link-check,pr-title-check/ CI 各项通过release/v1.0.0and tagv1.0.0to publish / 合并后切release/v1.0.0并打v1.0.0标签发布🤖 Generated with Zero Buddy