Implement MoQT AUTHORIZATION_TAG format but do not allow caching of tags.

Remove AUTHORIZATION_INFO parameter.

PiperOrigin-RevId: 754859021
diff --git a/quiche/quic/moqt/moqt_framer.cc b/quiche/quic/moqt/moqt_framer.cc
index 430616e..c1f84bd 100644
--- a/quiche/quic/moqt/moqt_framer.cc
+++ b/quiche/quic/moqt/moqt_framer.cc
@@ -222,17 +222,23 @@
     const VersionSpecificParameters& parameters, KeyValuePairList& out) {
   out.clear();
   for (const auto& it : parameters.authorization_token) {
-    out.insert(VersionSpecificParameter::kAuthorizationToken, it);
+    if (it.type > AuthTokenType::kMaxAuthTokenType) {
+      QUICHE_BUG(moqt_invalid_auth_token_type)
+          << "Invalid Auth Token Type: " << static_cast<uint64_t>(it.type);
+      continue;
+    }
+    // Just support USE_VALUE for now.
+    quiche::QuicheBuffer parameter_value =
+        Serialize(WireVarInt62(AuthTokenAliasType::kUseValue),
+                  WireVarInt62(it.type), WireBytes(it.token));
+    out.insert(VersionSpecificParameter::kAuthorizationToken,
+               std::string(parameter_value.AsStringView()));
   }
   if (!parameters.delivery_timeout.IsInfinite()) {
     out.insert(
         VersionSpecificParameter::kDeliveryTimeout,
         static_cast<uint64_t>(parameters.delivery_timeout.ToMilliseconds()));
   }
-  if (parameters.authorization_info.has_value()) {
-    out.insert(VersionSpecificParameter::kAuthorizationInfo,
-               *parameters.authorization_info);
-  }
   if (!parameters.max_cache_duration.IsInfinite()) {
     out.insert(
         VersionSpecificParameter::kMaxCacheDuration,
diff --git a/quiche/quic/moqt/moqt_framer_test.cc b/quiche/quic/moqt/moqt_framer_test.cc
index 690418b..96780f6 100644
--- a/quiche/quic/moqt/moqt_framer_test.cc
+++ b/quiche/quic/moqt/moqt_framer_test.cc
@@ -415,7 +415,7 @@
           /*group_order=*/std::nullopt,
           start,
           end_group,
-          VersionSpecificParameters("bar"),
+          VersionSpecificParameters(AuthTokenType::kOutOfBand, "bar"),
       };
       quiche::QuicheBuffer buffer;
       MoqtFilterType expected_filter_type = GetFilterType(subscribe);
@@ -443,7 +443,7 @@
       /*group_order=*/std::nullopt,
       /*start=*/Location(4, 3),
       /*end_group=*/3,
-      VersionSpecificParameters("bar"),
+      VersionSpecificParameters(AuthTokenType::kOutOfBand, "bar"),
   };
   quiche::QuicheBuffer buffer;
   EXPECT_QUIC_BUG(buffer = framer_.SerializeSubscribe(subscribe),
@@ -462,7 +462,7 @@
       /*end_group=*/1,
       /*end_object=*/1,
       /*parameters=*/
-      VersionSpecificParameters("baz"),
+      VersionSpecificParameters(AuthTokenType::kOutOfBand, "baz"),
   };
   quiche::QuicheBuffer buffer;
   EXPECT_QUIC_BUG(buffer = framer_.SerializeFetch(fetch),
diff --git a/quiche/quic/moqt/moqt_messages.cc b/quiche/quic/moqt/moqt_messages.cc
index 516c62a..cc57440 100644
--- a/quiche/quic/moqt/moqt_messages.cc
+++ b/quiche/quic/moqt/moqt_messages.cc
@@ -168,16 +168,13 @@
       parameters.count(VersionSpecificParameter::kAuthorizationToken);
   size_t delivery_timeout =
       parameters.count(VersionSpecificParameter::kDeliveryTimeout);
-  size_t authorization_info =
-      parameters.count(VersionSpecificParameter::kAuthorizationInfo);
   size_t max_cache_duration =
       parameters.count(VersionSpecificParameter::kMaxCacheDuration);
-  if (delivery_timeout > 1 || authorization_info > 1 ||
-      max_cache_duration > 1) {
+  if (delivery_timeout > 1 || max_cache_duration > 1) {
     // Disallowed duplicate.
     return false;
   }
-  if ((authorization_token > 0 || authorization_info > 0) &&
+  if (authorization_token > 0 &&
       !absl::c_linear_search(kAllowsAuthorization, message_type)) {
     return false;
   }
diff --git a/quiche/quic/moqt/moqt_messages.h b/quiche/quic/moqt/moqt_messages.h
index 77d916e..76c74cf 100644
--- a/quiche/quic/moqt/moqt_messages.h
+++ b/quiche/quic/moqt/moqt_messages.h
@@ -144,12 +144,18 @@
   kInternalError = 0x1,
   kUnauthorized = 0x2,
   kProtocolViolation = 0x3,
-  kDuplicateTrackAlias = 0x4,
-  kParameterLengthMismatch = 0x5,
-  kTooManySubscribes = 0x6,
+  kInvalidRequestId = 0x4,
+  kDuplicateTrackAlias = 0x5,
+  kKeyValueFormattingError = 0x6,
+  kTooManySubscribes = 0x7,
+  kInvalidPath = 0x8,
+  kMalformedPath = 0x9,
   kGoawayTimeout = 0x10,
   kControlMessageTimeout = 0x11,
   kDataStreamTimeout = 0x12,
+  kAuthTokenCacheOverflow = 0x13,
+  kDuplicateAuthTokenAlias = 0x14,
+  kVersionNegotiationFailed = 0x15,
 };
 
 // Error codes used by MoQT to reset streams.
@@ -173,13 +179,37 @@
 enum class QUICHE_EXPORT VersionSpecificParameter : uint64_t {
   kAuthorizationToken = 0x1,
   kDeliveryTimeout = 0x2,
-  kAuthorizationInfo = 0x3,
   kMaxCacheDuration = 0x4,
 
   // QUICHE-specific extensions.
   kOackWindowSize = 0xbbf1438,
 };
 
+enum AuthTokenType : uint64_t {
+  kOutOfBand = 0x0,
+
+  kMaxAuthTokenType = 0x0,
+};
+
+enum AuthTokenAliasType : uint64_t {
+  kDelete = 0x0,
+  kRegister = 0x1,
+  kUseAlias = 0x2,
+  kUseValue = 0x3,
+
+  kMaxValue = 0x3,
+};
+
+struct AuthToken {
+  AuthToken(AuthTokenType token_type, absl::string_view token)
+      : type(token_type), token(token) {}
+  bool operator==(const AuthToken& other) const {
+    return type == other.type && token == other.token;
+  }
+  AuthTokenType type;
+  std::string token;
+};
+
 struct VersionSpecificParameters {
   VersionSpecificParameters() = default;
   // Likely parameter combinations.
@@ -187,24 +217,24 @@
                             quic::QuicTimeDelta max_cache_duration)
       : delivery_timeout(delivery_timeout),
         max_cache_duration(max_cache_duration) {}
-  explicit VersionSpecificParameters(std::string authorization_info)
-      : authorization_info(authorization_info) {}
+  VersionSpecificParameters(AuthTokenType token_type, absl::string_view token) {
+    authorization_token.emplace_back(token_type, token);
+  };
   VersionSpecificParameters(quic::QuicTimeDelta delivery_timeout,
-                            std::string authorization_info)
-      : delivery_timeout(delivery_timeout),
-        authorization_info(authorization_info) {}
+                            AuthTokenType token_type, absl::string_view token)
+      : delivery_timeout(delivery_timeout) {
+    authorization_token.emplace_back(token_type, token);
+  }
 
   // TODO(martinduke): Turn auth_token into structured data.
-  std::vector<std::string> authorization_token;
+  std::vector<AuthToken> authorization_token;
   quic::QuicTimeDelta delivery_timeout = quic::QuicTimeDelta::Infinite();
-  std::optional<std::string> authorization_info;
   quic::QuicTimeDelta max_cache_duration = quic::QuicTimeDelta::Infinite();
   std::optional<quic::QuicTimeDelta> oack_window_size;
 
   bool operator==(const VersionSpecificParameters& other) const {
     return authorization_token == other.authorization_token &&
            delivery_timeout == other.delivery_timeout &&
-           authorization_info == other.authorization_info &&
            max_cache_duration == other.max_cache_duration &&
            oack_window_size == other.oack_window_size;
   }
@@ -222,6 +252,9 @@
   kDoesNotExist = 0x4,     // Can also mean "not interested" or "unknown".
   kInvalidRange = 0x5,     // SUBSCRIBE_ERROR and FETCH_ERROR only.
   kRetryTrackAlias = 0x6,  // SUBSCRIBE_ERROR only.
+  kMalformedAuthToken = 0x10,
+  kUnknownAuthTokenAlias = 0x11,
+  kExpiredAuthToken = 0x12,
 };
 
 struct MoqtSubscribeErrorReason {
@@ -710,7 +743,8 @@
 bool ValidateSetupParameters(const KeyValuePairList& parameters, bool webtrans,
                              quic::Perspective perspective);
 // Returns false if the parameters contain a protocol violation, or a
-// parameter cannot be in |message type|.
+// parameter cannot be in |message type|. Does not validate the internal
+// structure of Authorization Token values.
 bool ValidateVersionSpecificParameters(const KeyValuePairList& parameters,
                                        MoqtMessageType message_type);
 
diff --git a/quiche/quic/moqt/moqt_parser.cc b/quiche/quic/moqt/moqt_parser.cc
index 390ceec..27d9c81 100644
--- a/quiche/quic/moqt/moqt_parser.cc
+++ b/quiche/quic/moqt/moqt_parser.cc
@@ -165,45 +165,6 @@
       });
 }
 
-void KeyValuePairListToVersionSpecificParameters(
-    const KeyValuePairList& parameters, VersionSpecificParameters& out) {
-  parameters.ForEach(
-      [&](uint64_t key, uint64_t value) {
-        VersionSpecificParameter parameter =
-            static_cast<VersionSpecificParameter>(key);
-        switch (parameter) {
-          case VersionSpecificParameter::kDeliveryTimeout:
-            out.delivery_timeout = quic::QuicTimeDelta::FromMilliseconds(value);
-            break;
-          case VersionSpecificParameter::kMaxCacheDuration:
-            out.max_cache_duration =
-                quic::QuicTimeDelta::FromMilliseconds(value);
-            break;
-          case VersionSpecificParameter::kOackWindowSize:
-            out.oack_window_size = quic::QuicTimeDelta::FromMicroseconds(value);
-            break;
-          default:
-            break;
-        }
-        return true;
-      },
-      [&](uint64_t key, absl::string_view value) {
-        VersionSpecificParameter parameter =
-            static_cast<VersionSpecificParameter>(key);
-        switch (parameter) {
-          case VersionSpecificParameter::kAuthorizationToken:
-            out.authorization_token.push_back(std::string(value));
-            break;
-          case VersionSpecificParameter::kAuthorizationInfo:
-            out.authorization_info = value;
-            break;
-          default:
-            break;
-        }
-        return true;
-      });
-}
-
 }  // namespace
 
 void MoqtControlParser::ReadAndDispatchMessages() {
@@ -477,13 +438,15 @@
   if (!ParseKeyValuePairList(reader, parameters)) {
     return 0;
   }
-  // TODO(martinduke): Parse kAuthorizationToken.
   if (!ValidateVersionSpecificParameters(parameters,
                                          MoqtMessageType::kSubscribe)) {
     ParseError("SUBSCRIBE contains invalid parameters");
     return 0;
   }
-  KeyValuePairListToVersionSpecificParameters(parameters, subscribe.parameters);
+  if (!KeyValuePairListToVersionSpecificParameters(parameters,
+                                                   subscribe.parameters)) {
+    return 0;
+  }
   visitor_.OnSubscribeMessage(subscribe);
   return reader.PreviouslyReadPayload().length();
 }
@@ -524,8 +487,10 @@
     ParseError("SUBSCRIBE_OK contains invalid parameters");
     return 0;
   }
-  KeyValuePairListToVersionSpecificParameters(parameters,
-                                              subscribe_ok.parameters);
+  if (!KeyValuePairListToVersionSpecificParameters(parameters,
+                                                   subscribe_ok.parameters)) {
+    return 0;
+  }
   visitor_.OnSubscribeOkMessage(subscribe_ok);
   return reader.PreviouslyReadPayload().length();
 }
@@ -585,8 +550,10 @@
     ParseError("SUBSCRIBE_UPDATE contains invalid parameters");
     return 0;
   }
-  KeyValuePairListToVersionSpecificParameters(parameters,
-                                              subscribe_update.parameters);
+  if (!KeyValuePairListToVersionSpecificParameters(
+          parameters, subscribe_update.parameters)) {
+    return 0;
+  }
   subscribe_update.start = Location(start_group, start_object);
   if (end_group > 0) {
     subscribe_update.end_group = end_group - 1;
@@ -613,7 +580,10 @@
     ParseError("ANNOUNCE contains invalid parameters");
     return 0;
   }
-  KeyValuePairListToVersionSpecificParameters(parameters, announce.parameters);
+  if (!KeyValuePairListToVersionSpecificParameters(parameters,
+                                                   announce.parameters)) {
+    return 0;
+  }
   visitor_.OnAnnounceMessage(announce);
   return reader.PreviouslyReadPayload().length();
 }
@@ -677,8 +647,10 @@
     ParseError("TRACK_STATUS_REQUEST message contains invalid parameters");
     return 0;
   }
-  KeyValuePairListToVersionSpecificParameters(parameters,
-                                              track_status_request.parameters);
+  if (!KeyValuePairListToVersionSpecificParameters(
+          parameters, track_status_request.parameters)) {
+    return 0;
+  }
   visitor_.OnTrackStatusRequestMessage(track_status_request);
   return reader.PreviouslyReadPayload().length();
 }
@@ -718,8 +690,10 @@
     ParseError("TRACK_STATUS message contains invalid parameters");
     return 0;
   }
-  KeyValuePairListToVersionSpecificParameters(parameters,
-                                              track_status.parameters);
+  if (!KeyValuePairListToVersionSpecificParameters(parameters,
+                                                   track_status.parameters)) {
+    return 0;
+  }
   visitor_.OnTrackStatusMessage(track_status);
   return reader.PreviouslyReadPayload().length();
 }
@@ -748,8 +722,10 @@
     ParseError("SUBSCRIBE_ANNOUNCES message contains invalid parameters");
     return 0;
   }
-  KeyValuePairListToVersionSpecificParameters(parameters,
-                                              subscribe_announces.parameters);
+  if (!KeyValuePairListToVersionSpecificParameters(
+          parameters, subscribe_announces.parameters)) {
+    return 0;
+  }
   visitor_.OnSubscribeAnnouncesMessage(subscribe_announces);
   return reader.PreviouslyReadPayload().length();
 }
@@ -860,7 +836,10 @@
     ParseError("FETCH message contains invalid parameters");
     return 0;
   }
-  KeyValuePairListToVersionSpecificParameters(parameters, fetch.parameters);
+  if (!KeyValuePairListToVersionSpecificParameters(parameters,
+                                                   fetch.parameters)) {
+    return 0;
+  };
   visitor_.OnFetchMessage(fetch);
   return reader.PreviouslyReadPayload().length();
 }
@@ -895,7 +874,10 @@
     return 0;
   }
   fetch_ok.group_order = static_cast<MoqtDeliveryOrder>(group_order);
-  KeyValuePairListToVersionSpecificParameters(parameters, fetch_ok.parameters);
+  if (!KeyValuePairListToVersionSpecificParameters(parameters,
+                                                   fetch_ok.parameters)) {
+    return 0;
+  }
   visitor_.OnFetchOkMessage(fetch_ok);
   return reader.PreviouslyReadPayload().length();
 }
@@ -974,6 +956,122 @@
   return true;
 }
 
+// Returns false if there is a protocol violation.
+bool MoqtControlParser::KeyValuePairListToVersionSpecificParameters(
+    const KeyValuePairList& parameters, VersionSpecificParameters& out) {
+  return parameters.ForEach(
+      [&](uint64_t key, uint64_t value) {
+        VersionSpecificParameter parameter =
+            static_cast<VersionSpecificParameter>(key);
+        switch (parameter) {
+          case VersionSpecificParameter::kDeliveryTimeout:
+            out.delivery_timeout = quic::QuicTimeDelta::FromMilliseconds(value);
+            break;
+          case VersionSpecificParameter::kMaxCacheDuration:
+            out.max_cache_duration =
+                quic::QuicTimeDelta::FromMilliseconds(value);
+            break;
+          case VersionSpecificParameter::kOackWindowSize:
+            out.oack_window_size = quic::QuicTimeDelta::FromMicroseconds(value);
+            break;
+          default:
+            break;
+        }
+        return true;
+      },
+      [&](uint64_t key, absl::string_view value) {
+        VersionSpecificParameter parameter =
+            static_cast<VersionSpecificParameter>(key);
+        switch (parameter) {
+          case VersionSpecificParameter::kAuthorizationToken:
+            if (!ParseAuthTokenParameter(value, out)) {
+              return false;
+            }
+            break;
+          default:
+            break;
+        }
+        return true;
+      });
+}
+
+bool MoqtControlParser::ParseAuthTokenParameter(
+    absl::string_view field, VersionSpecificParameters& out) {
+  quic::QuicDataReader reader(field);
+  AuthTokenType token_type;
+  absl::string_view token;
+  uint64_t value;
+  if (!reader.ReadVarInt62(&value) || value > AuthTokenAliasType::kMaxValue) {
+    ParseError(MoqtError::kKeyValueFormattingError,
+               "Invalid Authorization Token Alias type");
+    return false;
+  }
+  AuthTokenAliasType alias_type = static_cast<AuthTokenAliasType>(value);
+  switch (alias_type) {
+    case AuthTokenAliasType::kUseValue:
+      if (!reader.ReadVarInt62(&value)) {
+        ParseError(MoqtError::kKeyValueFormattingError,
+                   "Malformed Authorization Token Parameter");
+        return false;
+      }
+      if (value > AuthTokenType::kMaxAuthTokenType) {
+        ParseError(MoqtError::kKeyValueFormattingError,
+                   "Invalid Authorization Token Type");
+        return false;
+      }
+      token_type = static_cast<AuthTokenType>(value);
+      token = reader.PeekRemainingPayload();
+      break;
+    case AuthTokenAliasType::kUseAlias:
+      if (!reader.ReadVarInt62(&value)) {
+        ParseError(MoqtError::kKeyValueFormattingError,
+                   "Malformed Authorization Token Parameter");
+        return false;
+      }
+      // TODO: Implement support for cache_size > 0
+      ParseError(MoqtError::kKeyValueFormattingError,
+                 "Unknown Auth Token Alias");
+      return false;
+    case AuthTokenAliasType::kRegister:
+      if (!reader.ReadVarInt62(&value)) {
+        ParseError(MoqtError::kKeyValueFormattingError,
+                   "Malformed Authorization Token Parameter");
+        return false;
+      }
+      if (!reader.ReadVarInt62(&value)) {
+        ParseError(MoqtError::kKeyValueFormattingError,
+                   "Malformed Authorization Token Parameter");
+        return false;
+      }
+      token_type = static_cast<AuthTokenType>(value);
+      token = reader.PeekRemainingPayload();
+      if (auth_token_cache_size_ + sizeof(uint64_t) + token.length() >
+          max_auth_token_cache_size_) {
+        ParseError(MoqtError::kAuthTokenCacheOverflow,
+                   "Too many authorization token tags");
+        return false;
+      }
+      break;
+      // TODO: Add to the cache.
+      // TODO: Check if the alias is already in use.
+      QUICHE_NOTREACHED();
+      break;
+    case AuthTokenAliasType::kDelete:
+      if (!reader.ReadVarInt62(&value)) {
+        ParseError(MoqtError::kKeyValueFormattingError,
+                   "Malformed Authorization Token Parameter");
+        return false;
+      }
+      // TODO: Implement support for cache_size > 0
+      ParseError(MoqtError::kKeyValueFormattingError,
+                 "Unknown Auth Token Alias");
+      return false;
+  }
+  // Validate cache operations.
+  out.authorization_token.push_back(AuthToken(token_type, token));
+  return true;
+}
+
 void MoqtDataParser::ParseError(absl::string_view reason) {
   if (parsing_error_) {
     return;  // Don't send multiple parse errors.
diff --git a/quiche/quic/moqt/moqt_parser.h b/quiche/quic/moqt/moqt_parser.h
index 137dd87..36c8f31 100644
--- a/quiche/quic/moqt/moqt_parser.h
+++ b/quiche/quic/moqt/moqt_parser.h
@@ -11,7 +11,6 @@
 #include <cstddef>
 #include <cstdint>
 #include <optional>
-#include <string>
 
 #include "absl/strings/string_view.h"
 #include "quiche/quic/core/quic_data_reader.h"
@@ -143,6 +142,16 @@
   // could not parse the full namespace field.
   bool ReadTrackNamespace(quic::QuicDataReader& reader,
                           FullTrackName& full_track_name);
+  // Translates raw key/value pairs into semantically meaningful formats.
+  // The spec defines many encoding errors in AUTHORIZATION TOKEN as
+  // request level. This treats them as session-level, unless they are a result
+  // of expiration, incorrect internal structure, or anything else not defined
+  // in the MoQT spec. It is allowed to promote request errors to session errors
+  // in MoQT. See also https://github.com/moq-wg/moq-transport/issues/964.
+  bool KeyValuePairListToVersionSpecificParameters(
+      const KeyValuePairList& parameters, VersionSpecificParameters& out);
+  bool ParseAuthTokenParameter(absl::string_view field,
+                               VersionSpecificParameters& out);
 
   MoqtControlParserVisitor& visitor_;
   quiche::ReadStream& stream_;
@@ -153,6 +162,8 @@
   std::optional<uint64_t> message_type_;
   std::optional<uint64_t> message_size_;
 
+  uint64_t max_auth_token_cache_size_ = 0;
+  uint64_t auth_token_cache_size_ = 0;
   bool processing_ = false;  // True if currently in ProcessData(), to prevent
                              // re-entrancy.
 };
diff --git a/quiche/quic/moqt/moqt_parser_test.cc b/quiche/quic/moqt/moqt_parser_test.cc
index 6ba8a22..bfe3c99 100644
--- a/quiche/quic/moqt/moqt_parser_test.cc
+++ b/quiche/quic/moqt/moqt_parser_test.cc
@@ -660,7 +660,7 @@
   EXPECT_EQ(visitor_.parsing_error_code_, MoqtError::kProtocolViolation);
 }
 
-TEST_F(MoqtMessageSpecificTest, SubscribeAuthorizationInfoTwice) {
+TEST_F(MoqtMessageSpecificTest, UnknownParameterTwiceIsOk) {
   webtransport::test::InMemoryStream stream(/*stream_id=*/0);
   MoqtControlParser parser(kWebTrans, &stream, visitor_);
   char subscribe[] = {
@@ -670,14 +670,12 @@
       0x20, 0x02,                    // priority = 0x20 descending
       0x02,                          // filter_type = kLatestObject
       0x02,                          // two params
-      0x03, 0x03, 0x62, 0x61, 0x72,  // authorization_info = "bar"
-      0x03, 0x03, 0x62, 0x61, 0x72,  // authorization_info = "bar"
+      0x1f, 0x03, 0x62, 0x61, 0x72,  // 0x1f = "bar"
+      0x1f, 0x03, 0x62, 0x61, 0x72,  // 0x1f = "bar"
   };
   stream.Receive(absl::string_view(subscribe, sizeof(subscribe)), false);
   parser.ReadAndDispatchMessages();
-  EXPECT_EQ(visitor_.messages_received_, 0);
-  EXPECT_EQ(visitor_.parsing_error_, "SUBSCRIBE contains invalid parameters");
-  EXPECT_EQ(visitor_.parsing_error_code_, MoqtError::kProtocolViolation);
+  EXPECT_EQ(visitor_.messages_received_, 1);
 }
 
 TEST_F(MoqtMessageSpecificTest, SubscribeDeliveryTimeoutTwice) {
@@ -720,16 +718,113 @@
   EXPECT_EQ(visitor_.parsing_error_code_, MoqtError::kProtocolViolation);
 }
 
-TEST_F(MoqtMessageSpecificTest, SubscribeOkHasAuthorizationInfo) {
+TEST_F(MoqtMessageSpecificTest, SubscribeAuthorizationTokenTagDelete) {
+  webtransport::test::InMemoryStream stream(/*stream_id=*/0);
+  MoqtControlParser parser(kRawQuic, &stream, visitor_);
+  char subscribe[] = {
+      0x03, 0x14, 0x01, 0x02, 0x01,
+      0x03, 0x66, 0x6f, 0x6f,        // track_namespace = "foo"
+      0x04, 0x61, 0x62, 0x63, 0x64,  // track_name = "abcd"
+      0x20, 0x02,                    // priority = 0x20 descending
+      0x02,                          // filter_type = kLatestObject
+      0x01,                          // one param
+      0x01, 0x02, 0x00, 0x00,        // authorization_token = DELETE 0;
+  };
+  stream.Receive(absl::string_view(subscribe, sizeof(subscribe)), false);
+  parser.ReadAndDispatchMessages();
+  EXPECT_EQ(visitor_.messages_received_, 0);
+  EXPECT_EQ(visitor_.parsing_error_, "Unknown Auth Token Alias");
+  EXPECT_EQ(visitor_.parsing_error_code_, MoqtError::kKeyValueFormattingError);
+}
+
+TEST_F(MoqtMessageSpecificTest, SubscribeAuthorizationTokenTagRegister) {
+  webtransport::test::InMemoryStream stream(/*stream_id=*/0);
+  MoqtControlParser parser(kRawQuic, &stream, visitor_);
+  char subscribe[] = {
+      0x03, 0x18, 0x01, 0x02, 0x01, 0x03, 0x66, 0x6f,
+      0x6f,                          // track_namespace = "foo"
+      0x04, 0x61, 0x62, 0x63, 0x64,  // track_name = "abcd"
+      0x20, 0x02,                    // priority = 0x20 descending
+      0x02,                          // filter_type = kLatestObject
+      0x01,                          // one param
+      0x01, 0x06, 0x01, 0x10, 0x00, 0x62, 0x61, 0x72,  // REGISTER 0x01
+  };
+  stream.Receive(absl::string_view(subscribe, sizeof(subscribe)), false);
+  parser.ReadAndDispatchMessages();
+  EXPECT_EQ(visitor_.messages_received_, 0);
+  EXPECT_EQ(visitor_.parsing_error_, "Too many authorization token tags");
+  EXPECT_EQ(visitor_.parsing_error_code_, MoqtError::kAuthTokenCacheOverflow);
+}
+
+TEST_F(MoqtMessageSpecificTest, SubscribeAuthorizationTokenTagUseAlias) {
+  webtransport::test::InMemoryStream stream(/*stream_id=*/0);
+  MoqtControlParser parser(kRawQuic, &stream, visitor_);
+  char subscribe[] = {
+      0x03, 0x14, 0x01, 0x02, 0x01,
+      0x03, 0x66, 0x6f, 0x6f,        // track_namespace = "foo"
+      0x04, 0x61, 0x62, 0x63, 0x64,  // track_name = "abcd"
+      0x20, 0x02,                    // priority = 0x20 descending
+      0x02,                          // filter_type = kLatestObject
+      0x01,                          // one param
+      0x01, 0x02, 0x02, 0x07,        // authorization_token = USE 7;
+  };
+  stream.Receive(absl::string_view(subscribe, sizeof(subscribe)), false);
+  parser.ReadAndDispatchMessages();
+  EXPECT_EQ(visitor_.messages_received_, 0);
+  EXPECT_EQ(visitor_.parsing_error_, "Unknown Auth Token Alias");
+  EXPECT_EQ(visitor_.parsing_error_code_, MoqtError::kKeyValueFormattingError);
+}
+
+TEST_F(MoqtMessageSpecificTest,
+       SubscribeAuthorizationTokenTagUnknownAliasType) {
+  webtransport::test::InMemoryStream stream(/*stream_id=*/0);
+  MoqtControlParser parser(kRawQuic, &stream, visitor_);
+  char subscribe[] = {
+      0x03, 0x14, 0x01, 0x02, 0x01,
+      0x03, 0x66, 0x6f, 0x6f,        // track_namespace = "foo"
+      0x04, 0x61, 0x62, 0x63, 0x64,  // track_name = "abcd"
+      0x20, 0x02,                    // priority = 0x20 descending
+      0x02,                          // filter_type = kLatestObject
+      0x01,                          // one param
+      0x01, 0x02, 0x04, 0x07,        // authorization_token type 4
+  };
+  stream.Receive(absl::string_view(subscribe, sizeof(subscribe)), false);
+  parser.ReadAndDispatchMessages();
+  EXPECT_EQ(visitor_.messages_received_, 0);
+  EXPECT_EQ(visitor_.parsing_error_, "Invalid Authorization Token Alias type");
+  EXPECT_EQ(visitor_.parsing_error_code_, MoqtError::kKeyValueFormattingError);
+}
+
+TEST_F(MoqtMessageSpecificTest,
+       SubscribeAuthorizationTokenTagUnknownTokenType) {
+  webtransport::test::InMemoryStream stream(/*stream_id=*/0);
+  MoqtControlParser parser(kRawQuic, &stream, visitor_);
+  char subscribe[] = {
+      0x03, 0x16, 0x01, 0x02, 0x01, 0x03,
+      0x66, 0x6f, 0x6f,                   // track_namespace = "foo"
+      0x04, 0x61, 0x62, 0x63, 0x64,       // track_name = "abcd"
+      0x20, 0x02,                         // priority = 0x20 descending
+      0x02,                               // filter_type = kLatestObject
+      0x01,                               // one param
+      0x01, 0x04, 0x03, 0x01, 0x00, 0x00  // authorization_token type 1
+  };
+  stream.Receive(absl::string_view(subscribe, sizeof(subscribe)), false);
+  parser.ReadAndDispatchMessages();
+  EXPECT_EQ(visitor_.messages_received_, 0);
+  EXPECT_EQ(visitor_.parsing_error_, "Invalid Authorization Token Type");
+  EXPECT_EQ(visitor_.parsing_error_code_, MoqtError::kKeyValueFormattingError);
+}
+
+TEST_F(MoqtMessageSpecificTest, SubscribeOkHasAuthorizationToken) {
   webtransport::test::InMemoryStream stream(/*stream_id=*/0);
   MoqtControlParser parser(kWebTrans, &stream, visitor_);
   char subscribe_ok[] = {
-      0x04, 0x0f, 0x01, 0x03,  // subscribe_id = 1, expires = 3
+      0x04, 0x11, 0x01, 0x03,  // subscribe_id = 1, expires = 3
       0x02, 0x01,              // group_order = 2, content exists
       0x0c, 0x14,              // largest_group_id = 12, largest_object_id = 20,
       0x02,                    // 2 parameters
       0x02, 0x67, 0x10,        // delivery_timeout = 10000
-      0x03, 0x03, 0x62, 0x61, 0x72,  // authorization_info = "bar"
+      0x01, 0x05, 0x03, 0x00, 0x62, 0x61, 0x72,  // authorization_token = "bar"
   };
   stream.Receive(absl::string_view(subscribe_ok, sizeof(subscribe_ok)), false);
   parser.ReadAndDispatchMessages();
@@ -739,14 +834,14 @@
   EXPECT_EQ(visitor_.parsing_error_code_, MoqtError::kProtocolViolation);
 }
 
-TEST_F(MoqtMessageSpecificTest, SubscribeUpdateHasAuthorizationInfo) {
+TEST_F(MoqtMessageSpecificTest, SubscribeUpdateHasAuthorizationToken) {
   webtransport::test::InMemoryStream stream(/*stream_id=*/0);
   MoqtControlParser parser(kWebTrans, &stream, visitor_);
   char subscribe_update[] = {
-      0x02, 0x0b, 0x02, 0x03, 0x01, 0x05,  // start and end sequences
-      0xaa,                                // priority = 0xaa
-      0x01,                                // 1 parameter
-      0x03, 0x03, 0x62, 0x61, 0x72,        // authorization_info = "bar"
+      0x02, 0x0d, 0x02, 0x03, 0x01, 0x05,        // start and end sequences
+      0xaa,                                      // priority = 0xaa
+      0x01,                                      // 1 parameter
+      0x01, 0x05, 0x03, 0x00, 0x62, 0x61, 0x72,  // authorization_token = "bar"
   };
   stream.Receive(absl::string_view(subscribe_update, sizeof(subscribe_update)),
                  false);
@@ -757,29 +852,27 @@
   EXPECT_EQ(visitor_.parsing_error_code_, MoqtError::kProtocolViolation);
 }
 
-TEST_F(MoqtMessageSpecificTest, AnnounceAuthorizationInfoTwice) {
+TEST_F(MoqtMessageSpecificTest, AnnounceAuthorizationTokenTwice) {
   webtransport::test::InMemoryStream stream(/*stream_id=*/0);
   MoqtControlParser parser(kWebTrans, &stream, visitor_);
   char announce[] = {
-      0x06, 0x10, 0x01, 0x03, 0x66, 0x6f, 0x6f,  // track_namespace = "foo"
+      0x06, 0x14, 0x01, 0x03, 0x66, 0x6f, 0x6f,  // track_namespace = "foo"
       0x02,                                      // 2 params
-      0x03, 0x03, 0x62, 0x61, 0x72,              // authorization_info = "bar"
-      0x03, 0x03, 0x62, 0x61, 0x72,              // authorization_info = "bar"
+      0x01, 0x05, 0x03, 0x00, 0x62, 0x61, 0x72,  // authorization = "bar"
+      0x01, 0x05, 0x03, 0x00, 0x62, 0x61, 0x72,  // authorization = "bar"
   };
   stream.Receive(absl::string_view(announce, sizeof(announce)), false);
   parser.ReadAndDispatchMessages();
-  EXPECT_EQ(visitor_.messages_received_, 0);
-  EXPECT_EQ(visitor_.parsing_error_, "ANNOUNCE contains invalid parameters");
-  EXPECT_EQ(visitor_.parsing_error_code_, MoqtError::kProtocolViolation);
+  EXPECT_EQ(visitor_.messages_received_, 1);
 }
 
 TEST_F(MoqtMessageSpecificTest, AnnounceHasDeliveryTimeout) {
   webtransport::test::InMemoryStream stream(/*stream_id=*/0);
   MoqtControlParser parser(kWebTrans, &stream, visitor_);
   char announce[] = {
-      0x06, 0x0e, 0x01, 0x03, 0x66, 0x6f, 0x6f,  // track_namespace = "foo"
+      0x06, 0x10, 0x01, 0x03, 0x66, 0x6f, 0x6f,  // track_namespace = "foo"
       0x02,                                      // 2 params
-      0x03, 0x03, 0x62, 0x61, 0x72,              // authorization_info = "bar"
+      0x01, 0x05, 0x03, 0x00, 0x62, 0x61, 0x72,  // authorization_info = "bar"
       0x02, 0x67, 0x10,                          // delivery_timeout = 10000
   };
   stream.Receive(absl::string_view(announce, sizeof(announce)), false);
diff --git a/quiche/quic/moqt/moqt_session_test.cc b/quiche/quic/moqt/moqt_session_test.cc
index 2513404..010926d 100644
--- a/quiche/quic/moqt/moqt_session_test.cc
+++ b/quiche/quic/moqt/moqt_session_test.cc
@@ -323,14 +323,13 @@
   bool reported_error = false;
   EXPECT_CALL(
       mock_session_,
-      CloseSession(static_cast<uint64_t>(MoqtError::kParameterLengthMismatch),
-                   "foo"))
+      CloseSession(static_cast<uint64_t>(MoqtError::kProtocolViolation), "foo"))
       .Times(1);
   EXPECT_CALL(session_callbacks_.session_terminated_callback, Call(_))
       .WillOnce([&](absl::string_view error_message) {
         reported_error = (error_message == "foo");
       });
-  session_.Error(MoqtError::kParameterLengthMismatch, "foo");
+  session_.Error(MoqtError::kProtocolViolation, "foo");
   EXPECT_TRUE(reported_error);
 }
 
diff --git a/quiche/quic/moqt/test_tools/moqt_test_message.h b/quiche/quic/moqt/test_tools/moqt_test_message.h
index 63b60ec..8478fcd 100644
--- a/quiche/quic/moqt/test_tools/moqt_test_message.h
+++ b/quiche/quic/moqt/test_tools/moqt_test_message.h
@@ -512,7 +512,7 @@
   }
 
   void ExpandVarints() override {
-    ExpandVarintsImpl("vvvvvv---v------vvvvvv---v--");
+    ExpandVarintsImpl("vvvvvv---v------vvvvv--vv-----");
   }
 
   MessageStructuredData structured_data() const override {
@@ -520,8 +520,8 @@
   }
 
  private:
-  uint8_t raw_packet_[28] = {
-      0x03, 0x1a, 0x01, 0x02,        // id and alias
+  uint8_t raw_packet_[30] = {
+      0x03, 0x1c, 0x01, 0x02,        // id and alias
       0x01, 0x03, 0x66, 0x6f, 0x6f,  // track_namespace = "foo"
       0x04, 0x61, 0x62, 0x63, 0x64,  // track_name = "abcd"
       0x20,                          // subscriber priority = 0x20
@@ -530,9 +530,9 @@
       0x04,                          // start_group = 4 (relative previous)
       0x01,                          // start_object = 1 (absolute)
       // No EndGroup or EndObject
-      0x02,                          // 2 parameters
-      0x02, 0x67, 0x10,              // delivery_timeout = 10000 ms
-      0x03, 0x03, 0x62, 0x61, 0x72,  // authorization_info = "bar"
+      0x02,                                      // 2 parameters
+      0x02, 0x67, 0x10,                          // delivery_timeout = 10000 ms
+      0x01, 0x05, 0x03, 0x00, 0x62, 0x61, 0x72,  // authorization_tag = "bar"
   };
 
   MoqtSubscribe subscribe_ = {
@@ -544,7 +544,7 @@
       /*start=*/Location(4, 1),
       /*end_group=*/std::nullopt,
       VersionSpecificParameters(quic::QuicTimeDelta::FromMilliseconds(10000),
-                                "bar"),
+                                AuthTokenType::kOutOfBand, "bar"),
   };
 };
 
@@ -819,22 +819,22 @@
     return true;
   }
 
-  void ExpandVarints() override { ExpandVarintsImpl("vvvv---vvv---"); }
+  void ExpandVarints() override { ExpandVarintsImpl("vvvv---vvv-----"); }
 
   MessageStructuredData structured_data() const override {
     return TestMessageBase::MessageStructuredData(announce_);
   }
 
  private:
-  uint8_t raw_packet_[13] = {
-      0x06, 0x0b, 0x01, 0x03, 0x66, 0x6f, 0x6f,  // track_namespace = "foo"
+  uint8_t raw_packet_[15] = {
+      0x06, 0x0d, 0x01, 0x03, 0x66, 0x6f, 0x6f,  // track_namespace = "foo"
       0x01,                                      // 1 parameter
-      0x03, 0x03, 0x62, 0x61, 0x72,              // authorization_info = "bar"
+      0x01, 0x05, 0x03, 0x00, 0x62, 0x61, 0x72,  // authorization_tag = "bar"
   };
 
   MoqtAnnounce announce_ = {
       /*track_namespace=*/FullTrackName{"foo"},
-      VersionSpecificParameters("bar"),
+      VersionSpecificParameters(AuthTokenType::kOutOfBand, "bar"),
   };
 };
 
@@ -974,23 +974,23 @@
     return true;
   }
 
-  void ExpandVarints() override { ExpandVarintsImpl("vvvv---v----vvv---"); }
+  void ExpandVarints() override { ExpandVarintsImpl("vvvv---v----vvv-----"); }
 
   MessageStructuredData structured_data() const override {
     return TestMessageBase::MessageStructuredData(track_status_request_);
   }
 
  private:
-  uint8_t raw_packet_[18] = {
-      0x0d, 0x10, 0x01, 0x03, 0x66, 0x6f, 0x6f,  // track_namespace = "foo"
+  uint8_t raw_packet_[20] = {
+      0x0d, 0x12, 0x01, 0x03, 0x66, 0x6f, 0x6f,  // track_namespace = "foo"
       0x04, 0x61, 0x62, 0x63, 0x64,              // track_name = "abcd"
       0x01,                                      // 1 parameter
-      0x03, 0x03, 0x62, 0x61, 0x72,              // authorization_info = "bar"
+      0x01, 0x05, 0x03, 0x00, 0x62, 0x61, 0x72,  // authorization_tag = "bar"
   };
 
   MoqtTrackStatusRequest track_status_request_ = {
       /*full_track_name=*/FullTrackName({"foo", "abcd"}),
-      VersionSpecificParameters("bar"),
+      VersionSpecificParameters(AuthTokenType::kOutOfBand, "bar"),
   };
 };
 
@@ -1132,22 +1132,22 @@
     return true;
   }
 
-  void ExpandVarints() override { ExpandVarintsImpl("vvvv---vvv---"); }
+  void ExpandVarints() override { ExpandVarintsImpl("vvvv---vvv-----"); }
 
   MessageStructuredData structured_data() const override {
     return TestMessageBase::MessageStructuredData(subscribe_namespace_);
   }
 
  private:
-  uint8_t raw_packet_[13] = {
-      0x11, 0x0b, 0x01, 0x03, 0x66, 0x6f, 0x6f,  // namespace = "foo"
+  uint8_t raw_packet_[15] = {
+      0x11, 0x0d, 0x01, 0x03, 0x66, 0x6f, 0x6f,  // namespace = "foo"
       0x01,                                      // 1 parameter
-      0x03, 0x03, 0x62, 0x61, 0x72,              // authorization_info = "bar"
+      0x01, 0x05, 0x03, 0x00, 0x62, 0x61, 0x72,  // authorization_tag = "bar"
   };
 
   MoqtSubscribeAnnounces subscribe_namespace_ = {
       /*track_namespace=*/FullTrackName{"foo"},
-      VersionSpecificParameters("bar"),
+      VersionSpecificParameters(AuthTokenType::kOutOfBand, "bar"),
   };
 };
 
@@ -1349,7 +1349,7 @@
   }
 
   void ExpandVarints() override {
-    ExpandVarintsImpl("vvv--vvv---v---vvvvvv---");
+    ExpandVarintsImpl("vvv--vvv---v---vvvvvv-----");
   }
 
   MessageStructuredData structured_data() const override {
@@ -1373,17 +1373,17 @@
   }
 
  private:
-  uint8_t raw_packet_[25] = {
-      0x16, 0x17,
-      0x01,                                // fetch_id = 1
-      0x02,                                // priority = kHigh
-      0x01,                                // group_order = kAscending
-      0x01,                                // type = kStandalone
-      0x01, 0x03, 0x66, 0x6f, 0x6f,        // track_namespace = "foo"
-      0x03, 0x62, 0x61, 0x72,              // track_name = "bar"
-      0x01, 0x02,                          // start_object = 1, 2
-      0x05, 0x07,                          // end_object = 5, 6
-      0x01, 0x03, 0x03, 0x62, 0x61, 0x7a,  // parameters = "baz"
+  uint8_t raw_packet_[27] = {
+      0x16, 0x19,
+      0x01,                          // fetch_id = 1
+      0x02,                          // priority = kHigh
+      0x01,                          // group_order = kAscending
+      0x01,                          // type = kStandalone
+      0x01, 0x03, 0x66, 0x6f, 0x6f,  // track_namespace = "foo"
+      0x03, 0x62, 0x61, 0x72,        // track_name = "bar"
+      0x01, 0x02,                    // start_object = 1, 2
+      0x05, 0x07,                    // end_object = 5, 6
+      0x01, 0x01, 0x05, 0x03, 0x00, 0x62, 0x61, 0x7a,  // parameters = "baz"
   };
 
   MoqtFetch fetch_ = {
@@ -1395,7 +1395,7 @@
       /*start_object=*/Location{1, 2},
       /*end_group=*/5,
       /*end_object=*/6,
-      VersionSpecificParameters("baz"),
+      VersionSpecificParameters(AuthTokenType::kOutOfBand, "baz"),
   };
 };
 
@@ -1461,7 +1461,7 @@
   }
 
   void ExpandVarints() override {
-    ExpandVarintsImpl("vvv--vvv---v---vvvvvv---");
+    ExpandVarintsImpl("vvv--vvv---v---vvvvvv-----");
   }
 
   MessageStructuredData structured_data() const override {
@@ -1474,14 +1474,14 @@
   }
 
  private:
-  uint8_t raw_packet_[14] = {
-      0x16, 0x0c,
-      0x01,                                // fetch_id = 1
-      0x02,                                // priority = kHigh
-      0x01,                                // group_order = kAscending
-      0x02,                                // type = kJoining
-      0x02, 0x02,                          // joining_subscribe_id = 2, 2 groups
-      0x01, 0x03, 0x03, 0x62, 0x61, 0x7a,  // parameters = "baz"
+  uint8_t raw_packet_[16] = {
+      0x16, 0x0e,
+      0x01,        // fetch_id = 1
+      0x02,        // priority = kHigh
+      0x01,        // group_order = kAscending
+      0x02,        // type = kJoining
+      0x02, 0x02,  // joining_subscribe_id = 2, 2 groups
+      0x01, 0x01, 0x05, 0x03, 0x00, 0x62, 0x61, 0x7a,  // parameters = "baz"
   };
 
   MoqtFetch fetch_ = {
@@ -1494,7 +1494,7 @@
       /*start_object=*/Location{1, 2},
       /*end_group=*/5,
       /*end_object=*/6,
-      VersionSpecificParameters("baz"),
+      VersionSpecificParameters(AuthTokenType::kOutOfBand, "baz"),
   };
 };
 
diff --git a/quiche/quic/moqt/tools/chat_client.cc b/quiche/quic/moqt/tools/chat_client.cc
index e10ae33..cbcfc3a 100644
--- a/quiche/quic/moqt/tools/chat_client.cc
+++ b/quiche/quic/moqt/tools/chat_client.cc
@@ -70,7 +70,7 @@
     return std::nullopt;
   }
   VersionSpecificParameters parameters(
-      std::string(GetUsername(my_track_name_)));
+      AuthTokenType::kOutOfBand, std::string(GetUsername(my_track_name_)));
   if (session_->SubscribeCurrentObject(*track_name, &remote_track_visitor_,
                                        parameters)) {
     ++subscribes_to_make_;
@@ -249,7 +249,7 @@
         return;
       };
   VersionSpecificParameters parameters(
-      std::string(GetUsername(my_track_name_)));
+      AuthTokenType::kOutOfBand, std::string(GetUsername(my_track_name_)));
   session_->SubscribeAnnounces(GetChatNamespace(my_track_name_),
                                std::move(subscribe_announces_callback),
                                parameters);