Skip to content

Commit

Permalink
impl(oauth2): port service account credential prerequisites (#8343)
Browse files Browse the repository at this point in the history
* impl(oauth2): port service account credential prerequisites
  • Loading branch information
scotthart authored Feb 11, 2022
1 parent 45a60a6 commit 5f0c674
Show file tree
Hide file tree
Showing 10 changed files with 478 additions and 1 deletion.
1 change: 1 addition & 0 deletions .typos.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ extend-exclude = [
# Files with crypto-keys in them
"google/cloud/internal/rest_client_integration_test.cc",
"google/cloud/internal/grpc_service_account_authentication_test.cc",
"google/cloud/testing_util/credentials_constants.h",
"google/cloud/storage/client_options_test.cc",
"google/cloud/storage/client_sign_policy_document_test.cc",
"google/cloud/storage/client_sign_url_test.cc",
Expand Down
9 changes: 8 additions & 1 deletion google/cloud/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,7 @@ endif ()
if (GOOGLE_CLOUD_CPP_ENABLE_REST)
include(IncludeNlohmannJson)
include(FindCurlWithTargets)
find_package(OpenSSL REQUIRED)

# the library
add_library(
Expand All @@ -639,6 +640,8 @@ if (GOOGLE_CLOUD_CPP_ENABLE_REST)
internal/curl_wrappers.h
internal/http_payload.cc
internal/http_payload.h
internal/make_jwt_assertion.cc
internal/make_jwt_assertion.h
internal/oauth2_access_token_credentials.cc
internal/oauth2_access_token_credentials.h
internal/oauth2_anonymous_credentials.cc
Expand All @@ -654,6 +657,8 @@ if (GOOGLE_CLOUD_CPP_ENABLE_REST)
internal/oauth2_error_credentials.h
internal/oauth2_refreshing_credentials_wrapper.cc
internal/oauth2_refreshing_credentials_wrapper.h
internal/openssl_util.cc
internal/openssl_util.h
internal/rest_client.cc
internal/rest_client.h
internal/rest_options.h
Expand All @@ -664,7 +669,7 @@ if (GOOGLE_CLOUD_CPP_ENABLE_REST)
target_link_libraries(
google_cloud_cpp_rest_internal
PUBLIC absl::span google-cloud-cpp::common CURL::libcurl
nlohmann_json::nlohmann_json)
nlohmann_json::nlohmann_json OpenSSL::SSL OpenSSL::Crypto)
google_cloud_cpp_add_common_options(google_cloud_cpp_rest_internal)
target_include_directories(
google_cloud_cpp_rest_internal
Expand Down Expand Up @@ -716,11 +721,13 @@ if (GOOGLE_CLOUD_CPP_ENABLE_REST)
internal/curl_impl_test.cc
internal/curl_wrappers_test.cc
internal/http_payload_test.cc
internal/make_jwt_assertion_test.cc
internal/oauth2_access_token_credentials_test.cc
internal/oauth2_anonymous_credentials_test.cc
internal/oauth2_authorized_user_credentials_test.cc
internal/oauth2_compute_engine_credentials_test.cc
internal/oauth2_refreshing_credentials_wrapper_test.cc
internal/openssl_util_test.cc
internal/rest_client_test.cc
internal/rest_request_test.cc
internal/rest_response_test.cc)
Expand Down
4 changes: 4 additions & 0 deletions google/cloud/google_cloud_cpp_rest_internal.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ google_cloud_cpp_rest_internal_hdrs = [
"internal/curl_options.h",
"internal/curl_wrappers.h",
"internal/http_payload.h",
"internal/make_jwt_assertion.h",
"internal/oauth2_access_token_credentials.h",
"internal/oauth2_anonymous_credentials.h",
"internal/oauth2_authorized_user_credentials.h",
Expand All @@ -32,6 +33,7 @@ google_cloud_cpp_rest_internal_hdrs = [
"internal/oauth2_credentials.h",
"internal/oauth2_error_credentials.h",
"internal/oauth2_refreshing_credentials_wrapper.h",
"internal/openssl_util.h",
"internal/rest_client.h",
"internal/rest_options.h",
"internal/rest_request.h",
Expand All @@ -45,13 +47,15 @@ google_cloud_cpp_rest_internal_srcs = [
"internal/curl_impl.cc",
"internal/curl_wrappers.cc",
"internal/http_payload.cc",
"internal/make_jwt_assertion.cc",
"internal/oauth2_access_token_credentials.cc",
"internal/oauth2_anonymous_credentials.cc",
"internal/oauth2_authorized_user_credentials.cc",
"internal/oauth2_compute_engine_credentials.cc",
"internal/oauth2_credentials.cc",
"internal/oauth2_error_credentials.cc",
"internal/oauth2_refreshing_credentials_wrapper.cc",
"internal/openssl_util.cc",
"internal/rest_client.cc",
"internal/rest_request.cc",
"internal/rest_response.cc",
Expand Down
2 changes: 2 additions & 0 deletions google/cloud/google_cloud_cpp_rest_internal_unit_tests.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@ google_cloud_cpp_rest_internal_unit_tests = [
"internal/curl_impl_test.cc",
"internal/curl_wrappers_test.cc",
"internal/http_payload_test.cc",
"internal/make_jwt_assertion_test.cc",
"internal/oauth2_access_token_credentials_test.cc",
"internal/oauth2_anonymous_credentials_test.cc",
"internal/oauth2_authorized_user_credentials_test.cc",
"internal/oauth2_compute_engine_credentials_test.cc",
"internal/oauth2_refreshing_credentials_wrapper_test.cc",
"internal/openssl_util_test.cc",
"internal/rest_client_test.cc",
"internal/rest_request_test.cc",
"internal/rest_response_test.cc",
Expand Down
36 changes: 36 additions & 0 deletions google/cloud/internal/make_jwt_assertion.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "google/cloud/internal/make_jwt_assertion.h"
#include "google/cloud/internal/openssl_util.h"

namespace google {
namespace cloud {
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
namespace internal {

StatusOr<std::string> MakeJWTAssertionNoThrow(std::string const& header,
std::string const& payload,
std::string const& pem_contents) {
auto const body =
UrlsafeBase64Encode(header) + '.' + UrlsafeBase64Encode(payload);
auto pem_signature = internal::SignUsingSha256(body, pem_contents);
if (!pem_signature) return std::move(pem_signature).status();
return body + '.' + UrlsafeBase64Encode(*pem_signature);
}

} // namespace internal
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
} // namespace cloud
} // namespace google
35 changes: 35 additions & 0 deletions google/cloud/internal/make_jwt_assertion.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_INTERNAL_MAKE_JWT_ASSERTION_H
#define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_INTERNAL_MAKE_JWT_ASSERTION_H

#include "google/cloud/status_or.h"
#include "google/cloud/version.h"
#include <string>

namespace google {
namespace cloud {
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
namespace internal {
StatusOr<std::string> MakeJWTAssertionNoThrow(std::string const& header,
std::string const& payload,
std::string const& pem_contents);

} // namespace internal
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
} // namespace cloud
} // namespace google

#endif // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_INTERNAL_MAKE_JWT_ASSERTION_H
98 changes: 98 additions & 0 deletions google/cloud/internal/make_jwt_assertion_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "google/cloud/internal/make_jwt_assertion.h"
#include "google/cloud/testing_util/status_matchers.h"
#include "absl/strings/str_split.h"
#include <gmock/gmock.h>
#include <nlohmann/json.hpp>

namespace google {
namespace cloud {
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
namespace internal {
namespace {

using ::google::cloud::testing_util::IsOk;

// This is an invalidated private key. It was created using the Google Cloud
// Platform console, but then the key (and service account) were deleted.
auto constexpr kWellFormattedKey = R"""(-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCltiF2oP3KJJ+S
tTc1McylY+TuAi3AdohX7mmqIjd8a3eBYDHs7FlnUrFC4CRijCr0rUqYfg2pmk4a
6TaKbQRAhWDJ7XD931g7EBvCtd8+JQBNWVKnP9ByJUaO0hWVniM50KTsWtyX3up/
fS0W2R8Cyx4yvasE8QHH8gnNGtr94iiORDC7De2BwHi/iU8FxMVJAIyDLNfyk0hN
eheYKfIDBgJV2v6VaCOGWaZyEuD0FJ6wFeLybFBwibrLIBE5Y/StCrZoVZ5LocFP
T4o8kT7bU6yonudSCyNMedYmqHj/iF8B2UN1WrYx8zvoDqZk0nxIglmEYKn/6U7U
gyETGcW9AgMBAAECggEAC231vmkpwA7JG9UYbviVmSW79UecsLzsOAZnbtbn1VLT
Pg7sup7tprD/LXHoyIxK7S/jqINvPU65iuUhgCg3Rhz8+UiBhd0pCH/arlIdiPuD
2xHpX8RIxAq6pGCsoPJ0kwkHSw8UTnxPV8ZCPSRyHV71oQHQgSl/WjNhRi6PQroB
Sqc/pS1m09cTwyKQIopBBVayRzmI2BtBxyhQp9I8t5b7PYkEZDQlbdq0j5Xipoov
9EW0+Zvkh1FGNig8IJ9Wp+SZi3rd7KLpkyKPY7BK/g0nXBkDxn019cET0SdJOHQG
DiHiv4yTRsDCHZhtEbAMKZEpku4WxtQ+JjR31l8ueQKBgQDkO2oC8gi6vQDcx/CX
Z23x2ZUyar6i0BQ8eJFAEN+IiUapEeCVazuxJSt4RjYfwSa/p117jdZGEWD0GxMC
+iAXlc5LlrrWs4MWUc0AHTgXna28/vii3ltcsI0AjWMqaybhBTTNbMFa2/fV2OX2
UimuFyBWbzVc3Zb9KAG4Y7OmJQKBgQC5324IjXPq5oH8UWZTdJPuO2cgRsvKmR/r
9zl4loRjkS7FiOMfzAgUiXfH9XCnvwXMqJpuMw2PEUjUT+OyWjJONEK4qGFJkbN5
3ykc7p5V7iPPc7Zxj4mFvJ1xjkcj+i5LY8Me+gL5mGIrJ2j8hbuv7f+PWIauyjnp
Nx/0GVFRuQKBgGNT4D1L7LSokPmFIpYh811wHliE0Fa3TDdNGZnSPhaD9/aYyy78
LkxYKuT7WY7UVvLN+gdNoVV5NsLGDa4cAV+CWPfYr5PFKGXMT/Wewcy1WOmJ5des
AgMC6zq0TdYmMBN6WpKUpEnQtbmh3eMnuvADLJWxbH3wCkg+4xDGg2bpAoGAYRNk
MGtQQzqoYNNSkfus1xuHPMA8508Z8O9pwKU795R3zQs1NAInpjI1sOVrNPD7Ymwc
W7mmNzZbxycCUL/yzg1VW4P1a6sBBYGbw1SMtWxun4ZbnuvMc2CTCh+43/1l+FHe
Mmt46kq/2rH2jwx5feTbOE6P6PINVNRJh/9BDWECgYEAsCWcH9D3cI/QDeLG1ao7
rE2NcknP8N783edM07Z/zxWsIsXhBPY3gjHVz2LDl+QHgPWhGML62M0ja/6SsJW3
YvLLIc82V7eqcVJTZtaFkuht68qu/Jn1ezbzJMJ4YXDYo1+KFi+2CAGR06QILb+I
lUtj+/nH3HDQjM4ltYfTPUg=
-----END PRIVATE KEY-----
)""";

TEST(MakeJWTAssertionNoThrow, Basic) {
auto header = nlohmann::json{
{"alg", "HS256"}, {"typ", "JWT"}, {"kid", "test-key-name"}};
auto payload = nlohmann::json{
{"iss", "[email protected]"},
{"sub", "[email protected]"},
{"aud", "https//not-a-service.googleapis.com"},
{"iat", "1511900000"},
{"exp", "1511903600"},
};
auto const assertion =
MakeJWTAssertionNoThrow(header.dump(), payload.dump(), kWellFormattedKey);
ASSERT_THAT(assertion, IsOk());

std::vector<std::string> components = absl::StrSplit(*assertion, '.');
EXPECT_EQ(components.size(), 3);
}

TEST(MakeJWTAssertionNoThrow, InvalidKey) {
auto header = nlohmann::json{
{"alg", "HS256"}, {"typ", "JWT"}, {"kid", "test-key-name"}};
auto payload = nlohmann::json{
{"iss", "[email protected]"},
{"sub", "[email protected]"},
{"aud", "https//not-a-service.googleapis.com"},
{"iat", "1511900000"},
{"exp", "1511903600"},
};
auto const assertion =
MakeJWTAssertionNoThrow(header.dump(), payload.dump(), "invalid-key");
ASSERT_THAT(assertion, Not(IsOk()));
}

} // namespace
} // namespace internal
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
} // namespace cloud
} // namespace google
Loading

0 comments on commit 5f0c674

Please sign in to comment.