diff --git a/google/cloud/grpc_options.cc b/google/cloud/grpc_options.cc index c5f42ed01c0f9..20422296f75e8 100644 --- a/google/cloud/grpc_options.cc +++ b/google/cloud/grpc_options.cc @@ -160,6 +160,30 @@ BackgroundThreadsFactory MakeBackgroundThreadsFactory(Options const& opts) { }; } +namespace experimental { + +bool GrpcEnableHardBoundTokensIsSafe(int major, int minor, int patch) { + // Never happens. No 0.x version is supported or implements the version + // macros, but it makes the rest more readable. + if (major < 1) return false; + if (major > 1) return true; + if (minor <= 62) return false; + if (minor == 63) return patch >= 1; + if (minor == 64) return patch >= 1; + return true; +} + +bool GrpcEnableHardBoundTokensIsSafe() { +#ifndef GRPC_CPP_VERSION_MAJOR + return false; +#else + return GrpcEnableHardBoundTokensIsSafe( + GRPC_CPP_VERSION_MAJOR, GRPC_CPP_VERSION_MINOR, GRPC_CPP_VERSION_PATCH); +#endif // GRPC_CPP_VERSION_MAJOR +} + +} + } // namespace internal GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END } // namespace cloud diff --git a/google/cloud/grpc_options.h b/google/cloud/grpc_options.h index 92eee8969f044..f2459a1101491 100644 --- a/google/cloud/grpc_options.h +++ b/google/cloud/grpc_options.h @@ -262,6 +262,19 @@ absl::optional GetStringChannelArgument( */ BackgroundThreadsFactory MakeBackgroundThreadsFactory(Options const& opts = {}); +namespace experimental{ + +/** + * Enable gRPC Bound Tokens Authentication. + */ +struct EnableGrpcHardBoundTokensAuthenticationOption { + using Type = bool; +}; + +bool GrpcEnableHardBoundTokensIsSafe(); + +} // namespace experimental + } // namespace internal GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END } // namespace cloud diff --git a/google/cloud/internal/unified_grpc_credentials.cc b/google/cloud/internal/unified_grpc_credentials.cc index b1c82e99f2586..0433de94d9d41 100644 --- a/google/cloud/internal/unified_grpc_credentials.cc +++ b/google/cloud/internal/unified_grpc_credentials.cc @@ -102,8 +102,15 @@ std::shared_ptr CreateAuthenticationStrategy( grpc::InsecureChannelCredentials()); } void visit(GoogleDefaultCredentialsConfig const&) override { + bool use_bound_tokens = + google::cloud::internal::experimental:: + GrpcEnableHardBoundTokensIsSafe() && + options.get(); + grpc::GoogleDefaultCredentialsOptions credentials_options = {}; + credentials_options.use_alts_call_credentials = use_bound_tokens; result = std::make_unique( - grpc::GoogleDefaultCredentials()); + grpc::GoogleDefaultCredentials(&credentials_options)); } void visit(AccessTokenConfig const& cfg) override { result = std::make_unique( diff --git a/google/cloud/internal/unified_grpc_credentials_test.cc b/google/cloud/internal/unified_grpc_credentials_test.cc index 4aa2f6ca1e53e..27781f096cff4 100644 --- a/google/cloud/internal/unified_grpc_credentials_test.cc +++ b/google/cloud/internal/unified_grpc_credentials_test.cc @@ -98,6 +98,23 @@ TEST(UnifiedGrpcCredentialsTest, WithDefaultCredentials) { ASSERT_EQ(nullptr, context.credentials()); } +TEST(UnifiedGrpcCredentialsTest, WithDefaultCredentialsAndHardBoundToken) { + // Create a filename for a file that (most likely) does not exist. We just + // want to initialize the default credentials, the filename won't be used by + // the test. + ScopedEnvironment env("GOOGLE_APPLICATION_CREDENTIALS", "unused.json"); + + CompletionQueue cq; + auto result = CreateAuthenticationStrategy( + *MakeGoogleDefaultCredentials(), cq, + Options{}.set(true)); + ASSERT_NE(nullptr, result.get()); + grpc::ClientContext context; + auto status = result->ConfigureContext(context); + EXPECT_THAT(status, IsOk()); + ASSERT_EQ(nullptr, context.credentials()); +} + TEST(UnifiedGrpcCredentialsTest, WithAccessTokenCredentials) { auto const expiration = std::chrono::system_clock::now() + std::chrono::hours(1);