Skip to content

Commit 0a97e1a

Browse files
authored
docs: add samples for API key auth (#14740)
1 parent bc28dc9 commit 0a97e1a

File tree

3 files changed

+198
-1
lines changed

3 files changed

+198
-1
lines changed

examples/BUILD.bazel

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,19 @@ cc_test(
9898
"@com_github_curl_curl//:curl",
9999
],
100100
)
101+
102+
cc_test(
103+
name = "api_key",
104+
srcs = ["api_key.cc"],
105+
tags = [
106+
"integration-test",
107+
"integration-test-production",
108+
],
109+
deps = [
110+
"//:apikeys",
111+
"//:common",
112+
"//:language",
113+
"//google/cloud/testing_util:google_cloud_cpp_testing_grpc_private",
114+
"//google/cloud/testing_util:google_cloud_cpp_testing_private",
115+
],
116+
)

examples/CMakeLists.txt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,21 @@ if (batch IN_LIST GOOGLE_CLOUD_CPP_ENABLE AND logging IN_LIST
5757
google_cloud_cpp_add_common_options(batch_logging)
5858
endif ()
5959

60+
if (apikeys IN_LIST GOOGLE_CLOUD_CPP_ENABLE AND language IN_LIST
61+
GOOGLE_CLOUD_CPP_ENABLE)
62+
add_executable(api_key api_key.cc)
63+
target_link_libraries(
64+
api_key PRIVATE google_cloud_cpp_testing google-cloud-cpp::apikeys
65+
google-cloud-cpp::language)
66+
google_cloud_cpp_add_common_options(api_key)
67+
endif ()
68+
6069
# Label all the binaries as "tests", so they run in our CI builds.
6170
if (NOT BUILD_TESTING)
6271
return()
6372
endif ()
6473

65-
foreach (test batch_logging gcs2cbt grpc_credential_types)
74+
foreach (test api_key batch_logging gcs2cbt grpc_credential_types)
6675
if (NOT TARGET "${test}")
6776
continue()
6877
endif ()

examples/api_key.cc

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
// Copyright 2024 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// The #includes (with the extra blank line) are part of the code extracted into
16+
// the reference documentation. We want to highlight what includes are needed.
17+
// [START apikeys_create_api_key]
18+
#include "google/cloud/apikeys/v2/api_keys_client.h"
19+
#include "google/cloud/location.h"
20+
21+
// [END apikeys_create_api_key]
22+
// [START apikeys_authenticate_api_key]
23+
#include "google/cloud/language/v1/language_client.h"
24+
#include "google/cloud/common_options.h"
25+
#include "google/cloud/options.h"
26+
27+
// [END apikeys_authenticate_api_key]
28+
#include "google/cloud/internal/format_time_point.h"
29+
#include "google/cloud/internal/getenv.h"
30+
#include "google/cloud/testing_util/example_driver.h"
31+
#include "google/cloud/testing_util/scoped_environment.h"
32+
#include "absl/strings/match.h"
33+
#include "absl/strings/str_split.h"
34+
#include <chrono>
35+
#include <iostream>
36+
#include <string>
37+
#include <thread>
38+
#include <vector>
39+
40+
namespace {
41+
42+
// [START apikeys_create_api_key]
43+
google::api::apikeys::v2::Key CreateApiKey(
44+
google::cloud::apikeys_v2::ApiKeysClient client,
45+
google::cloud::Location location, std::string display_name) {
46+
google::api::apikeys::v2::CreateKeyRequest request;
47+
request.set_parent(location.FullName());
48+
request.mutable_key()->set_display_name(std::move(display_name));
49+
request.mutable_key()->mutable_restrictions()->add_api_targets()->set_service(
50+
"language.googleapis.com");
51+
52+
// Create the key, blocking on the result.
53+
auto key = client.CreateKey(request).get();
54+
if (!key) throw std::move(key.status());
55+
std::cout << "Successfully created an API key: " << key->name() << "\n";
56+
57+
// For authenticating with the API key, use the value in `key->key_string()`.
58+
59+
// The API key's resource name is the value in `key->name()`. Use this to
60+
// refer to the specific key in a `GetKey()` or `DeleteKey()` RPC.
61+
return *key;
62+
}
63+
// [END apikeys_create_api_key]
64+
65+
void CreateApiKeyCommand(std::vector<std::string> const& argv) {
66+
if (argv.size() != 2) {
67+
throw google::cloud::testing_util::Usage{
68+
"create-api-key <project-id> <display-name>"};
69+
}
70+
auto client = google::cloud::apikeys_v2::ApiKeysClient(
71+
google::cloud::apikeys_v2::MakeApiKeysConnection());
72+
73+
(void)CreateApiKey(client, google::cloud::Location(argv.at(0), "global"),
74+
argv.at(1));
75+
}
76+
77+
// [START apikeys_authenticate_api_key]
78+
void AuthenticateWithApiKey(std::vector<std::string> const& argv) {
79+
if (argv.size() != 2) {
80+
throw google::cloud::testing_util::Usage{
81+
"authenticate-with-api-key <project-id> <api-key>"};
82+
}
83+
namespace gc = ::google::cloud;
84+
auto options = gc::Options{}.set<gc::ApiKeyOption>(argv[1]);
85+
auto client = gc::language_v1::LanguageServiceClient(
86+
gc::language_v1::MakeLanguageServiceConnection(options));
87+
88+
auto constexpr kText = "Hello, world!";
89+
90+
google::cloud::language::v1::Document d;
91+
d.set_content(kText);
92+
d.set_type(google::cloud::language::v1::Document::PLAIN_TEXT);
93+
94+
auto response = client.AnalyzeSentiment(d, {});
95+
if (!response) throw std::move(response.status());
96+
auto const& sentiment = response->document_sentiment();
97+
std::cout << "Text: " << kText << "\n";
98+
std::cout << "Sentiment: " << sentiment.score() << ", "
99+
<< sentiment.magnitude() << "\n";
100+
std::cout << "Successfully authenticated using the API key\n";
101+
}
102+
// [END apikeys_authenticate_api_key]
103+
104+
void AutoRun(std::vector<std::string> const& argv) {
105+
namespace gc = ::google::cloud;
106+
namespace examples = ::google::cloud::testing_util;
107+
using ::google::cloud::internal::GetEnv;
108+
if (!argv.empty()) throw examples::Usage{"auto"};
109+
examples::CheckEnvironmentVariablesAreSet({
110+
"GOOGLE_CLOUD_PROJECT",
111+
});
112+
auto const project_id = GetEnv("GOOGLE_CLOUD_PROJECT").value();
113+
auto const location = gc::Location(project_id, "global");
114+
115+
auto constexpr kKeyPrefix = "examples/api_key.cc ";
116+
char delimiter = '@';
117+
auto options = gc::Options{}.set<gc::UserProjectOption>(project_id);
118+
auto client = gc::apikeys_v2::ApiKeysClient(
119+
gc::apikeys_v2::MakeApiKeysConnection(options));
120+
121+
std::cout << "Cleaning up stale keys\n";
122+
auto stale = gc::internal::FormatUtcDate(std::chrono::system_clock::now() -
123+
std::chrono::hours(48));
124+
for (auto key : client.ListKeys(location.FullName())) {
125+
if (!key) throw std::move(key.status());
126+
std::vector<std::string> parts =
127+
absl::StrSplit(key->display_name(), delimiter);
128+
if (parts.size() == 2 && parts[0] == kKeyPrefix && parts[1] <= stale) {
129+
std::cout << "Deleting stale API Key: " << key->display_name() << "\n";
130+
(void)client.DeleteKey(key->name()).get();
131+
}
132+
}
133+
134+
std::cout << "Running CreateApiKey\n";
135+
auto suffix = gc::internal::FormatUtcDate(std::chrono::system_clock::now());
136+
auto display_name = std::string(kKeyPrefix) + delimiter + suffix;
137+
auto key = CreateApiKey(client, location, std::move(display_name));
138+
139+
std::cout << "Running AuthenticateWithApiKey\n";
140+
for (auto backoff : {60, 60, 60, 0}) {
141+
// API keys are not always usable immediately after they are created. To
142+
// give the API key some time to propagate, we implement a retry loop around
143+
// the RPC that attempts to authenticate with the newly created API key.
144+
145+
// When we authenticate with an API key, we do not have (or need)
146+
// credentials. Using a quota project requires credentials, so disable it.
147+
google::cloud::testing_util::ScopedEnvironment overlay(
148+
"GOOGLE_CLOUD_CPP_USER_PROJECT", absl::nullopt);
149+
150+
try {
151+
AuthenticateWithApiKey({project_id, key.key_string()});
152+
} catch (gc::Status const& s) {
153+
if (backoff == 0) throw(s);
154+
std::cout << "Sleeping for " << backoff << " seconds\n";
155+
std::this_thread::sleep_for(std::chrono::seconds(backoff));
156+
}
157+
}
158+
159+
std::cout << "Deleting API Key\n";
160+
(void)client.DeleteKey(key.name()).get();
161+
}
162+
163+
} // namespace
164+
165+
int main(int argc, char* argv[]) { // NOLINT(bugprone-exception-escape)
166+
google::cloud::testing_util::Example example({
167+
{"create-api-key", CreateApiKeyCommand},
168+
{"authenticate-with-api-key", AuthenticateWithApiKey},
169+
{"auto", AutoRun},
170+
});
171+
return example.Run(argc, argv);
172+
}

0 commit comments

Comments
 (0)