Repository navigation
fix(net): end a lapsed request token's requests with EXPIRED_AUTH_TOKEN - #7703
Conversation
Quest-Claim: d9ffbf0d3ff9086e
…UTH_TOKEN When a moq-transport request token's grant lapses, the subscription it admitted is dropped from outside, so every open group stream fell back to the writer's CANCELLED reset. The streams now share a Lapse flag, set just before the drop when the grant ended by expiry (its deadline, or an acceptor revoking it as expired), and reset with the draft's EXPIRED_AUTH_TOKEN (0x7 in drafts 18, 20, and 22) instead: group streams through a GroupServe Drop, the fill and END_OF_TRACK streams through Ranked. A revoked or narrowed grant still cancels. PUBLISH_DONE stays UNAUTHORIZED. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…OKEN Per the maintainer's decisions on the PR: - FETCH: once its request token lapses after FETCH_OK, the fetch's data stream and its request stream reset with EXPIRED_AUTH_TOKEN (0x7). A token end found anywhere in the fetch now carries the token's own refusal, so a lapse before FETCH_OK, including while resolving through Gate::guard, is refused with REQUEST_ERROR EXPIRED_AUTH_TOKEN. - PUBLISH_NAMESPACE: an accepted announce whose token lapses after its OK is cancelled with RESET_STREAM and STOP_SENDING carrying 0x7 rather than finished (draft-22 sections 6.4.2.3 and 9.14). A revoked or narrowed token ends both as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 19 minutes. View limit details
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. CodeRabbit Verify
|
kixelated
left a comment
There was a problem hiding this comment.
Automated review by review (OpenAI)
Reviewed head d12041d. No actionable findings found in the expiry handling for subscriptions, FETCH, announces, and token gating.
Verification: GitHub-only static review; I did not run tests or independently verify the draft registries. The PR reports passing moq-net tests, clippy, and formatting checks.
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — the full 9-file diff: a moq-transport wire behavior change so a request whose own token lapsed ends distinguishably from a denial, plus its tests and docs.
EXPIRED_AUTH_TOKEN = 0x7(ietf/error.rs): new stream reset code and atoken_expiredpredicate (Error::Expired | Error::Session(SessionError::Expired)). Verified 0x7 is the registered EXPIRED_AUTH_TOKEN stream code in draft-ietf-moq-transport-18 §3.3.3 and -22 §12.5/§16.11.4;to_stream_codestill never emits it andfrom_stream_codekeeps itUnknown.Lapseflag (ietf/publisher.rs): a sharedArc<AtomicBool>set where the grant end is observed, before the serving future drops.GroupServe::Drop, theRanked::resetoverride (fill / END_OF_TRACK / FETCH data), andLapse::abort(the answered request stream) read it. Ordering is sound:set()runs in theauth::untilguard beforeserveis dropped.- FETCH coverage:
token_grantnow falls back togate.token_guard(), so a standalone FETCH's own token is watched; lapse-before-OK refuses with REQUEST_ERROR 0x5, lapse-after-OK resets the data and request streams with 0x7. - Announce cancellation (
subscriber.rs): an acked PUBLISH_NAMESPACE whose token lapsed is cancelled both ways with 0x7 per draft-22 §6.4.2.3, rather than FIN. The route is released first. Gate::guard: pollspoll_token_endedahead of the gate it closes, so resolve-time lapse surfaces the token's own error.- Tests + docs: mocked-time tests across drafts 18/20/22 for lapsed, revoked-as-expired, and revoked;
doc/bin/relay/auth.mdupdated; quest file deleted.
cargo check -p moq-net --lib passes on the head. Public API unchanged. No js/net mirror is needed: its ietf path only carries the SETUP-level AUTHORIZATION_TOKEN, not per-request token grants.
deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏

Implements quest/m1/auth-expiry-reset.md, point 1 of moq-dev/moq#7646. The issue's other points landed in moq-dev/moq#7211, which this builds on. With the FETCH and announce cases below also covered, this finishes the issue.
Closes #7646
Summary
When a moq-transport request token's grant lapses, the requests it admitted now tell the peer so with the draft's EXPIRED_AUTH_TOKEN. Before, they ended with codes indistinguishable from a denial (or with a plain FIN). This lets a client tell "renew and retry" from "denied".
What a lapsed token now sends
Gate::guard)A revoked or narrowed token ends all of these as before. The quest described the subscription case as INTERNAL_ERROR, but on
mainits streams actually reset with CANCELLED:auth::untildropped the serve future, so each stream fell back toWriter's Drop.How
ietf::error::token_expiredis the one check. It is true for the deadline passing, or the acceptor revoking withSessionError::Expired, and it already decided PUBLISH_DONE's "token expired".Lapseflag (ietf/publisher.rs) is set where the grant end is seen, just before the request's serving future drops. Group streams read it in aGroupServeDrop; fill, END_OF_TRACK and FETCH data streams opt in throughRanked. The FETCH request stream goes throughLapse::abort.Reader::stopandWriter::reset. Both arepub(crate), and both take a code the caller already took from the registry.StreamError::Unknown(0x7), and noStreamErrorencodes to it. So a relay passes an upstream's expiry on as INTERNAL_ERROR, rather than telling its own peer that their token lapsed.doc/bin/relay/auth.mddescribes what a lapse sends.quest/m1/README.md.Decisions
serve_fetch_stream. (Maintainer, 2026-11-03)Choices made without asking
SessionError::Expiredcounts as an expiry, matching the "token expired" PUBLISH_DONE reason it already got.Gate::guarddoes the same, which also makes a TRACK_STATUS whose token lapses while it resolves refuse as EXPIRED_AUTH_TOKEN. TheResulta FETCH returns now carries the acceptor's own code, as a subscription's already did. Three tests that pinnedError::Unauthorizedwere updated to match.a_lapsed_announce_grant_retracts_before_the_peer_readsasserted that a lapse waits on the peer's FIN acknowledgement. A reset waits on nothing, so it is nowa_lapsed_announce_grant_retracts_without_waiting_on_the_peer. The FIN-and-wait behavior keeps its revoke test (a_revoked_token_retracts_an_announce_whose_ok_is_blocked).Checks
cargo nextest run --locked -p moq-net: 3352 passed, 13 skipped, on the final head.cargo clippy --locked -p moq-net --all-targets -- -D warnings, with default and with no default features: clean.cargo fmt: clean.quest check: ok.a_lapsed_token_resets_its_groups_as_expired: subscription group resets, PUBLISH_DONE, and stream order.a_lapsed_token_ends_a_fetch_as_expired: lapse while resolving gives REQUEST_ERROR; lapse after FETCH_OK gives data and request stream resets.a_lapsed_announce_token_cancels_its_request_as_expired: RESET_STREAM and STOP_SENDING with 0x7, or a FIN when revoked.a_lapsed_subscriptions_ranked_streams_reset_as_expired: the fill and END_OF_TRACK path.Review
quest review, Codexgpt-6.1-solat medium effort.6e9d61f6ce: no findings.origin/main(moq-dev/moq#7681, which touches nors/moq-netfile).git diff origin/main...d12041da57matches the reviewed diff except for one hunk offset inquest/m1/README.md.Suggested follow-ups
🤖 Generated with Claude Code
(Written by Claude Opus 5.5)