Skip to content

Gdch oauth2 prototype#16117

Draft
scotthart wants to merge 11 commits into
googleapis:mainfrom
scotthart:gdch_oauth2_prototype
Draft

Gdch oauth2 prototype#16117
scotthart wants to merge 11 commits into
googleapis:mainfrom
scotthart:gdch_oauth2_prototype

Conversation

@scotthart
Copy link
Copy Markdown
Member

No description provided.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces support for Google Distributed Cloud Hosting (GDCH) Service Account credentials, including REST client implementations, JWT assertion generation using raw ECDSA signatures via OpenSSL, and corresponding unit and integration tests. The review feedback identifies several important issues and areas for improvement: a declared overload of MakeGDCHServiceAccountCredentials is missing its implementation; hardcoding internal::InternalError in the service account credentials discards the HTTP status code; JSON parsing needs validation to ensure the input is an object to prevent crashes; platform-dependent std::time_t should be replaced with modern C++ duration casts; lifetime safety should be improved when passing temporary strings to client->Post; and commented-out pseudo-code in unified_grpc_credentials.cc should be removed.

Comment on lines +77 to +81
std::shared_ptr<Credentials> MakeGDCHServiceAccountCredentials(
std::string json_object, std::string audience, Options opts) {
return std::make_shared<internal::GDCHServiceAccountConfig>(
std::move(json_object), std::move(audience), std::move(opts));
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

The second overload of MakeGDCHServiceAccountCredentials (which takes only audience and opts) is declared in google/cloud/credentials.h but is missing its implementation in google/cloud/credentials.cc. This will lead to linker errors when users attempt to call it.

std::shared_ptr<Credentials> MakeGDCHServiceAccountCredentials(
    std::string json_object, std::string audience, Options opts) {
  return std::make_shared<internal::GDCHServiceAccountConfig>(
      std::move(json_object), std::move(audience), std::move(opts));
}

std::shared_ptr<Credentials> MakeGDCHServiceAccountCredentials(
    std::string audience, Options opts) {
  return std::make_shared<internal::GDCHServiceAccountConfig>(
      std::move(audience), std::move(opts));
}

Comment on lines +220 to +222
// TODO(sdhart): verify this is the error we want to return.
return internal::InternalError(error_payload, GCP_ERROR_INFO());
// return AsStatus(status_code, error_payload);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

Hardcoding internal::InternalError discards the actual HTTP status code returned by the server (e.g., mapping a 400 Bad Request to kInternal instead of kInvalidArgument). Restoring AsStatus(status_code, error_payload) preserves the correct status code mapping. Additionally, this cleans up the commented-out code and the temporary TODO.

    return AsStatus(status_code, error_payload);

Comment on lines +38 to +43
auto credentials = nlohmann::json::parse(content, nullptr, false);
if (credentials.is_discarded()) {
return internal::InvalidArgumentError(absl::StrCat(
"Invalid GDCHServiceAccountCredentials, parsing failed on ",
"data loaded from ", source));
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

If the input content is valid JSON but not a JSON object (e.g., a JSON array, string, or number), credentials.is_discarded() will be false, but subsequent calls like credentials.find() will throw a nlohmann::json::type_error exception. Checking !credentials.is_object() prevents these potential crashes.

  auto credentials = nlohmann::json::parse(content, nullptr, false);
  if (credentials.is_discarded() || !credentials.is_object()) {
    return internal::InvalidArgumentError(absl::StrCat(
        "Invalid GDCHServiceAccountCredentials, parsing failed on ",
        "data loaded from ", source));
  }

Comment on lines +131 to +134
auto const now_from_epoch =
static_cast<std::intmax_t>(std::chrono::system_clock::to_time_t(now));
auto const expiration_from_epoch = static_cast<std::intmax_t>(
std::chrono::system_clock::to_time_t(expiration));
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Using std::chrono::system_clock::to_time_t relies on std::time_t, which can be platform-dependent (e.g., 32-bit on some systems, leading to the Y2038 problem, or even floating-point). Using time_since_epoch() with std::chrono::duration_cast is the modern, standard, and safer C++ way to get the Unix timestamp.

  auto const now_from_epoch = std::chrono::duration_cast<std::chrono::seconds>(
                                  now.time_since_epoch()).count();
  auto const expiration_from_epoch =
      std::chrono::duration_cast<std::chrono::seconds>(
          expiration.time_since_epoch()).count();

auto payload = CreateRefreshPayload(info_, tp);
if (!payload) return std::move(payload).status();
rest_internal::RestContext context;
auto response = client->Post(context, request, {payload->dump()});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Passing {payload->dump()} directly to client->Post creates a temporary std::string whose lifetime is bound to the full expression. While technically valid during the call, initializing an absl::Span<char const> from a temporary can trigger compiler warnings or static analysis lifetime alerts. Storing the dumped string in a local variable first is safer and cleaner.

  std::string payload_str = payload->dump();
  auto response = client->Post(context, request, {payload_str});

Comment on lines +162 to +164
// if file path is specified, read json from it, handle errors
// else use json from cfg
// create
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

These commented-out developer notes/pseudo-code lines should be removed to keep the codebase clean and maintainable.

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