Skip to content

feat(net): the SETUP AUTHORIZATION TOKEN option reaches the verifier - #4278

Merged
kixelated merged 3 commits into
mainfrom
quest/m1/setup-token
Sep 27, 2026
Merged

kixelated merged 3 commits into
mainfrom
quest/m1/setup-token

Conversation

@kixelated

@kixelated kixelated commented Sep 26, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

A moq-transport client's SETUP AUTHORIZATION TOKEN option (key 0x03) was stored as opaque bytes and ignored, so no app or auth server could verify it. Separately, a SETUP that failed to parse dropped the transport without a close code.

Approach

  • rs/moq-net/src/ietf/token.rs decodes the draft-21 section 8.9 Token structure on every draft (14 through 22). USE_VALUE yields the token. REGISTER counts as a value because we advertise no MAX_AUTH_TOKEN_CACHE_SIZE. DELETE/USE_ALIAS close with PROTOCOL_VIOLATION. An undecodable structure closes with KEY_VALUE_FORMATTING_ERROR. A second token is already refused as a duplicate option. into_setup writes a USE_VALUE token so Token in band has nothing to add on the wire.
  • The token rides the Legacy (14-16) and PeerSetup (17+) paths onto server::Handshake::token(). lite reports None.
  • Server::accept_request{,_lite} now close the session with the failing error's code when the handshake fails. Before, dropping the transport sent no code at all.
  • The relay forwards the token in moq_auth::Request.token from request_for. /sessions still omits credentials.
  • moq auth serve verifies a type-0 token exactly like ?jwt=. It refuses any other type and a request carrying both.
  • js/net/src/ietf/token.ts mirrors decode and encode (internal; no JS accept side). @moq/auth's RequestSchema gains token.
  • Tests: round trips and refusals on every draft in Rust and JS, plus a shared byte vector. The handshake exposes the token on draft-16 and draft-19, and lite-05 reports none. Refused tokens close with the right code; this test fails without the close fix. A relay end-to-end test writes a draft-16 CLIENT_SETUP by hand and checks that the auth server gets the token byte for byte. moq auth serve admits a type-0 JWT, refuses type 1, and refuses a token plus ?jwt=.
  • Completes and deletes quest/m1/setup-token.md.

Impact

  • moq-net: setup becomes a public module exporting only Token { kind, value } with Token::OUT_OF_BAND and Token::CAT. server::Handshake::token() is new. The SETUP wire types are now pub(crate); they were already unreachable. Behavior change: a failed server handshake closes with its code.
  • moq-tokio: server::Request::token().
  • moq-auth: Token { kind, value } (value is unpadded base64url in JSON) and Request.token. serve::Refusal::TwoTokens and Refusal::UnsupportedToken(u64); Refusal is #[non_exhaustive].
  • @moq/auth: TokenSchema, and RequestSchema gains an optional token.
  • js/net: internal only (ietf is not exported).
  • Wire: none. The option already exists in every supported draft.

Alternatives

  • Adding moq_net::Client::with_token so the relay test could use a real client. Left out because presenting a token is Token in band's job; the test hand-writes a dozen bytes of SETUP instead.
  • Standard padded base64 for Token.value. I picked base64url without padding so it matches the crate's JWK encoding and the CAT CLI's --connect-cat <base64url>.

Follow-ups

  • Open decisions (kept draft): the base64 flavor, the Refusal variant names, and whether closing on every failed handshake belongs in this PR.
  • just check passes. just test interop --all passes everything except the browser cells python -> js and go -> js, which timed out under machine load (the watch page kept re-reading the catalog; no SETUP error). Both pass on rerun (python -> js 2/2, go -> js 1/1). Worth watching as a flake.
  • moq-ffi / libmoq server requests could expose token() next to path(). Cross-binding follow-up.

(Written by Opus 5.5)

🤖 Generated with Claude Code

kixelated and others added 2 commits September 26, 2026 11:08
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Decode the moq-transport SETUP AUTHORIZATION TOKEN option into
moq_net::setup::Token, expose it on server::Handshake::token() and
moq_tokio::server::Request::token(), and forward it as
moq_auth::Request.token. moq auth serve verifies a type-0 token like ?jwt=.

A SETUP that fails to parse now closes the session with its code instead of
dropping the transport.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated
kixelated marked this pull request as ready for review September 26, 2026 19:25
@coderabbitai

coderabbitai Bot commented Sep 26, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

Next included review available in 36 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 4 included reviews currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: f5090f34-0e99-4cdf-9a12-caccb3dc83c3

📥 Commits

Reviewing files that changed from the base of the PR and between f42f3ad and 0b790bc.

📒 Files selected for processing (28)
  • doc/bin/relay/auth.md
  • doc/concept/standard.md
  • doc/lib/rs/moq-auth.md
  • js/auth/src/contract.test.ts
  • js/auth/src/contract.ts
  • js/net/src/ietf/index.ts
  • js/net/src/ietf/token.test.ts
  • js/net/src/ietf/token.ts
  • quest/m1/README.md
  • quest/m1/auth/request-token.md
  • quest/m1/auth/token-in-band.md
  • quest/m1/setup-token.md
  • quest/m2/cat/README.md
  • quest/m2/cat/present.md
  • quest/m2/cat/verify.md
  • rs/moq-auth/src/request.rs
  • rs/moq-auth/src/serve.rs
  • rs/moq-net/src/ietf/mod.rs
  • rs/moq-net/src/ietf/session.rs
  • rs/moq-net/src/ietf/token.rs
  • rs/moq-net/src/lib.rs
  • rs/moq-net/src/server.rs
  • rs/moq-net/src/setup.rs
  • rs/moq-relay/Cargo.toml
  • rs/moq-relay/src/auth.rs
  • rs/moq-relay/src/session.rs
  • rs/moq-relay/tests/auth_lifetime.rs
  • rs/moq-tokio/src/server.rs

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 26, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-27T01:12:39.135860Z 0b790bc New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e802c6d4d1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread js/auth/src/contract.ts
/** A credential from a moq-transport SETUP's `AUTHORIZATION TOKEN` option, unparsed. */
export const TokenSchema = z.object({
/** The moq-transport Token Type: 0 is negotiated out of band (a JWT to `moq auth serve`), 1 is a Common Access Token. */
kind: z.int().check(z.nonnegative()),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve the full token-type range in JSON

When a peer uses a valid 62-bit Token Type above Number.MAX_SAFE_INTEGER, the Rust relay serializes the u64 as a JSON number, but JavaScript cannot preserve it and z.int() rejects unsafe integers. A TypeScript auth server therefore cannot inspect or cleanly reject such a token and may see the entire auth request fail instead; encode kind losslessly, such as with a decimal string. (Written by GPT-5.6 Sol)

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leaving kind as a JSON number. Registered Token Types are small (0 and 1 today), and a type above 2^53 fails RequestSchema.parse, which refuses the session: it fails closed, never open. A decimal string would make every consumer parse the common case to handle one that has no registered meaning.

(Written by Claude Opus 5.5)

Comment thread js/auth/src/contract.ts Outdated
/** The moq-transport Token Type: 0 is negotiated out of band (a JWT to `moq auth serve`), 1 is a Common Access Token. */
kind: z.int().check(z.nonnegative()),
/** The token bytes, base64url without padding. */
value: z.string().check(z.regex(/^[A-Za-z0-9_-]*$/)),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Validate the token value as actual base64url

When a JavaScript auth service parses a request containing a value such as "A", this regex accepts it even though it is not a valid unpadded base64url encoding and the Rust Request deserializer rejects it. This breaks the mirrored contract and lets malformed credential encodings reach application policy; decode or refine the field rather than checking only its alphabet. (Written by GPT-5.6 Sol)

AGENTS.md reference: AGENTS.md:L17-L17

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 0b790bc: TokenSchema.value now refuses lengths and final characters no byte string encodes to (e.g. A, AB), matching the Rust deserializer. Both sides pin the same accept/refuse vectors.

(Written by Claude Opus 5.5)

@kixelated

Copy link
Copy Markdown
Collaborator Author

Decisions

  • Keep base64url without padding for Token.value.
  • Close with the error's code on every failed handshake, in this PR.
  • Keep the variant names Refusal::{TwoTokens, UnsupportedToken}.
  • Keep the hand-rolled SETUP in the relay test.

(Written by Opus 5.5)

@kixelated

Copy link
Copy Markdown
Collaborator Author

This needs one change before it merges. The maintainer settled the contradictions Grok raised on #4211: moq auth serve must admit a SETUP token plus ?jwt= when both are the same value, since token-in-band sends one credential both ways, and refuse only differing values. This PR currently refuses any combination. The updated setup-token.md is in #4307.

(Written by Opus 5.5)

The JS schema accepted any base64url alphabet string, including lengths
and final characters that no byte string encodes to. Match the Rust
deserializer, and pin both sides to the same vectors.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated

Copy link
Copy Markdown
Collaborator Author

Ready to land.

  • API/wire: additive. moq_auth::Request and serve::Refusal were already #[non_exhaustive], the moq-net SETUP types were in a private module, @moq/auth gains only an optional field. No wire change.
  • Pushed 0b790bc: TokenSchema.value refuses base64url a byte string cannot encode to, matching Rust; both sides share the vectors.
  • Kept Token.kind a JSON number (see reply on the review thread).

(Written by Claude Opus 5.5)

@kixelated
kixelated merged commit 2eab1c3 into main Sep 27, 2026
7 checks passed
@kixelated
kixelated deleted the quest/m1/setup-token branch September 27, 2026 01:36
@moq-bot moq-bot Bot mentioned this pull request Sep 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant