Credentials in the handshake
- send the request, token attached
- server decides
- client learns the verdict
—time to verdict
v154 · webtransport headers
Two ways to prove who you are: in the handshake, or in the first message after it. Both are run here against this server, for real, and timed — because the difference is not only how long the client waits but how long the server holds a session it has no reason to trust.
—time to verdict
—time to verdict
| shape | ms to verdict | round trips | unproven session held |
|---|---|---|---|
| Not run yet. | |||
The second shape is measured over a WebSocket, not a WebTransport session: a WebTransport server needs HTTP/3 and a certificate this host does not serve. The stand-in is honest about the thing being measured — a session that opens before credentials arrive — because that is a property of the shape, not of the protocol. The numbers are real; the protocol is not the point.
The interesting number is not the total. It is the gap between "session open" and "token acknowledged" — every millisecond of which the server spent tracking a connection from a peer that had proven nothing. Multiply it by your connection rate and it is a budget an attacker can spend for free.
headers removes the gap rather than shortening it. The token travels in the CONNECT, so a session either exists and is authenticated, or was never created. There is no half-trusted state to write code for, and no half-trusted state to attack.
// Before: connect first, prove yourself second.
const transport = new WebTransport(url);
await transport.ready; // the server now has a session
const writer = transport.datagrams.writable.getWriter();
await writer.write(encode({ token })); // …and only now knows who you are
// After: the CONNECT carries it, so an unauthenticated session never exists.
const transport = new WebTransport(url, {
headers: { authorization: `Bearer ${token}` },
});
await transport.ready; // resolving means you were accepted