Add `GeoHint` to `BlindSignToken`.

As a part of the impl of Token caching by Geo, it is required that the `BlindSideToken` contains the geo.

More details about the implementation can be found in the [design](https://docs.google.com/document/d/1mQHfJvhDItCXk5LdkMxn1DQ0pQvNiItzIWTXSbcv9pU/edit?usp=sharing).

PiperOrigin-RevId: 642286269
diff --git a/quiche/blind_sign_auth/blind_sign_auth.cc b/quiche/blind_sign_auth/blind_sign_auth.cc
index b52367e..9142276 100644
--- a/quiche/blind_sign_auth/blind_sign_auth.cc
+++ b/quiche/blind_sign_auth/blind_sign_auth.cc
@@ -148,6 +148,9 @@
   std::vector<uint16_t> kExpectedExtensionTypes = {
       /*ExpirationTimestamp=*/0x0001, /*GeoHint=*/0x0002,
       /*ServiceType=*/0xF001, /*DebugMode=*/0xF002, /*ProxyLayer=*/0xF003};
+  // TODO(b/345801768): Improve the API of
+  // `anonymous_tokens::ValidateExtensionsOrderAndValues` to
+  // avoid any possible TOCTOU problems.
   absl::Status result =
       anonymous_tokens::ValidateExtensionsOrderAndValues(
           *extensions, absl::MakeSpan(kExpectedExtensionTypes), absl::Now());
@@ -168,6 +171,11 @@
   absl::Time public_metadata_expiry_time =
       absl::FromUnixSeconds(expiration_timestamp->timestamp);
 
+  absl::StatusOr<anonymous_tokens::GeoHint> geo_hint =
+      anonymous_tokens::GeoHint::FromExtension(
+          extensions->extensions.at(1));
+  QUICHE_CHECK(geo_hint.ok());
+
   // Create token challenge.
   anonymous_tokens::TokenChallenge challenge;
   challenge.issuer_name = kIssuerHostname;
@@ -248,7 +256,7 @@
       absl::bind_front(&BlindSignAuth::PrivacyPassAuthAndSignCallback, this,
                        std::move(initial_data_response.privacy_pass_data()
                                      .public_metadata_extensions()),
-                       public_metadata_expiry_time, *use_case,
+                       public_metadata_expiry_time, *geo_hint, *use_case,
                        std::move(privacy_pass_clients), std::move(callback));
   // TODO(b/304811277): remove other usages of string.data()
   fetcher_->DoRequest(BlindSignMessageRequestType::kAuthAndSign, oauth_token,
@@ -258,6 +266,7 @@
 
 void BlindSignAuth::PrivacyPassAuthAndSignCallback(
     std::string encoded_extensions, absl::Time public_key_expiry_time,
+    anonymous_tokens::GeoHint geo_hint,
     anonymous_tokens::AnonymousTokensUseCase use_case,
     std::vector<std::unique_ptr<anonymous_tokens::
                                     PrivacyPassRsaBssaPublicMetadataClient>>
@@ -331,8 +340,9 @@
     privacy_pass_token_data.mutable_encoded_extensions()->assign(
         absl::WebSafeBase64Escape(encoded_extensions));
     privacy_pass_token_data.set_use_case_override(use_case);
-    tokens_vec.push_back(BlindSignToken{
-        privacy_pass_token_data.SerializeAsString(), public_key_expiry_time});
+    tokens_vec.push_back(
+        BlindSignToken{privacy_pass_token_data.SerializeAsString(),
+                       public_key_expiry_time, geo_hint});
   }
 
   std::move(callback)(absl::Span<BlindSignToken>(tokens_vec));
diff --git a/quiche/blind_sign_auth/blind_sign_auth.h b/quiche/blind_sign_auth/blind_sign_auth.h
index 552f2c1..39d8fa6 100644
--- a/quiche/blind_sign_auth/blind_sign_auth.h
+++ b/quiche/blind_sign_auth/blind_sign_auth.h
@@ -12,6 +12,7 @@
 #include "absl/status/statusor.h"
 #include "absl/time/time.h"
 #include "anonymous_tokens/cpp/privacy_pass/rsa_bssa_public_metadata_client.h"
+#include "anonymous_tokens/cpp/privacy_pass/token_encodings.h"
 #include "quiche/blind_sign_auth/blind_sign_auth_interface.h"
 #include "quiche/blind_sign_auth/blind_sign_auth_protos.h"
 #include "quiche/blind_sign_auth/blind_sign_message_interface.h"
@@ -27,7 +28,8 @@
                          privacy::ppn::BlindSignAuthOptions auth_options)
       : fetcher_(fetcher), auth_options_(std::move(auth_options)) {}
 
-  // Returns signed unblinded tokens and their expiration time in a callback.
+  // Returns signed unblinded tokens, their expiration time, and their geo in a
+  // callback.
   // Tokens are single-use.
   // The GetTokens callback will run on the same thread as the
   // BlindSignMessageInterface callbacks.
@@ -49,6 +51,7 @@
       SignedTokenCallback callback);
   void PrivacyPassAuthAndSignCallback(
       std::string encoded_extensions, absl::Time public_key_expiry_time,
+      anonymous_tokens::GeoHint geo_hint,
       anonymous_tokens::AnonymousTokensUseCase use_case,
       std::vector<std::unique_ptr<anonymous_tokens::
                                       PrivacyPassRsaBssaPublicMetadataClient>>
diff --git a/quiche/blind_sign_auth/blind_sign_auth_interface.h b/quiche/blind_sign_auth/blind_sign_auth_interface.h
index c46df3e..12b6204 100644
--- a/quiche/blind_sign_auth/blind_sign_auth_interface.h
+++ b/quiche/blind_sign_auth/blind_sign_auth_interface.h
@@ -11,11 +11,14 @@
 #include "absl/status/statusor.h"
 #include "absl/time/time.h"
 #include "absl/types/span.h"
+#include "anonymous_tokens/cpp/privacy_pass/token_encodings.h"
 #include "quiche/common/platform/api/quiche_export.h"
 #include "quiche/common/quiche_callbacks.h"
 
 namespace quiche {
 
+using ::anonymous_tokens::GeoHint;
+
 // ProxyLayer indicates which proxy layer that tokens will be used with.
 enum class ProxyLayer : int {
   kProxyA,
@@ -36,6 +39,7 @@
 struct QUICHE_EXPORT BlindSignToken {
   std::string token;
   absl::Time expiration;
+  GeoHint geo_hint;
 };
 
 using SignedTokenCallback =
diff --git a/quiche/blind_sign_auth/blind_sign_auth_test.cc b/quiche/blind_sign_auth/blind_sign_auth_test.cc
index 6e01b5c..f293bca 100644
--- a/quiche/blind_sign_auth/blind_sign_auth_test.cc
+++ b/quiche/blind_sign_auth/blind_sign_auth_test.cc
@@ -13,7 +13,9 @@
 #include "absl/status/statusor.h"
 #include "absl/strings/escaping.h"
 #include "absl/strings/string_view.h"
+#include "absl/time/clock.h"
 #include "absl/time/time.h"
+#include "absl/types/span.h"
 #include "anonymous_tokens/cpp/crypto/crypto_utils.h"
 #include "anonymous_tokens/cpp/privacy_pass/token_encodings.h"
 #include "anonymous_tokens/cpp/testing/utils.h"
@@ -243,6 +245,11 @@
       std::string decoded_extensions;
       ASSERT_TRUE(absl::WebSafeBase64Unescape(
           privacy_pass_token_data.encoded_extensions(), &decoded_extensions));
+      // Validate GeoHint in BlindSignToken.
+      EXPECT_EQ(token.geo_hint.geo_hint, "US,US-AL,ALABASTER");
+      EXPECT_EQ(token.geo_hint.country_code, "US");
+      EXPECT_EQ(token.geo_hint.region, "US-AL");
+      EXPECT_EQ(token.geo_hint.city, "ALABASTER");
     }
   }