3 * Copyright 2015 gRPC authors.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
19 #include <grpc/support/port_platform.h>
21 #include "src/core/lib/json/json.h"
22 #include "src/core/lib/security/credentials/oauth2/oauth2_credentials.h"
26 #include "absl/container/inlined_vector.h"
27 #include "absl/strings/str_cat.h"
28 #include "absl/strings/str_format.h"
29 #include "absl/strings/str_join.h"
31 #include <grpc/grpc_security.h>
32 #include <grpc/impl/codegen/slice.h>
33 #include <grpc/slice.h>
34 #include <grpc/support/alloc.h>
35 #include <grpc/support/log.h>
36 #include <grpc/support/string_util.h>
38 #include "src/core/lib/gpr/string.h"
39 #include "src/core/lib/gprpp/ref_counted_ptr.h"
40 #include "src/core/lib/iomgr/error.h"
41 #include "src/core/lib/iomgr/load_file.h"
42 #include "src/core/lib/security/util/json_util.h"
43 #include "src/core/lib/slice/slice_internal.h"
44 #include "src/core/lib/surface/api_trace.h"
45 #include "src/core/lib/uri/uri_parser.h"
47 using grpc_core::Json;
50 // Auth Refresh Token.
53 int grpc_auth_refresh_token_is_valid(
54 const grpc_auth_refresh_token* refresh_token) {
55 return (refresh_token != nullptr) &&
56 strcmp(refresh_token->type, GRPC_AUTH_JSON_TYPE_INVALID);
59 grpc_auth_refresh_token grpc_auth_refresh_token_create_from_json(
61 grpc_auth_refresh_token result;
62 const char* prop_value;
64 grpc_error* error = GRPC_ERROR_NONE;
66 memset(&result, 0, sizeof(grpc_auth_refresh_token));
67 result.type = GRPC_AUTH_JSON_TYPE_INVALID;
68 if (json.type() != Json::Type::OBJECT) {
69 gpr_log(GPR_ERROR, "Invalid json.");
73 prop_value = grpc_json_get_string_property(json, "type", &error);
74 GRPC_LOG_IF_ERROR("Parsing refresh token", error);
75 if (prop_value == nullptr ||
76 strcmp(prop_value, GRPC_AUTH_JSON_TYPE_AUTHORIZED_USER)) {
79 result.type = GRPC_AUTH_JSON_TYPE_AUTHORIZED_USER;
81 if (!grpc_copy_json_string_property(json, "client_secret",
82 &result.client_secret) ||
83 !grpc_copy_json_string_property(json, "client_id", &result.client_id) ||
84 !grpc_copy_json_string_property(json, "refresh_token",
85 &result.refresh_token)) {
91 if (!success) grpc_auth_refresh_token_destruct(&result);
95 grpc_auth_refresh_token grpc_auth_refresh_token_create_from_string(
96 const char* json_string) {
97 grpc_error* error = GRPC_ERROR_NONE;
98 Json json = Json::Parse(json_string, &error);
99 if (error != GRPC_ERROR_NONE) {
100 gpr_log(GPR_ERROR, "JSON parsing failed: %s", grpc_error_string(error));
101 GRPC_ERROR_UNREF(error);
103 return grpc_auth_refresh_token_create_from_json(json);
106 void grpc_auth_refresh_token_destruct(grpc_auth_refresh_token* refresh_token) {
107 if (refresh_token == nullptr) return;
108 refresh_token->type = GRPC_AUTH_JSON_TYPE_INVALID;
109 if (refresh_token->client_id != nullptr) {
110 gpr_free(refresh_token->client_id);
111 refresh_token->client_id = nullptr;
113 if (refresh_token->client_secret != nullptr) {
114 gpr_free(refresh_token->client_secret);
115 refresh_token->client_secret = nullptr;
117 if (refresh_token->refresh_token != nullptr) {
118 gpr_free(refresh_token->refresh_token);
119 refresh_token->refresh_token = nullptr;
124 // Oauth2 Token Fetcher credentials.
127 grpc_oauth2_token_fetcher_credentials::
128 ~grpc_oauth2_token_fetcher_credentials() {
129 GRPC_MDELEM_UNREF(access_token_md_);
130 gpr_mu_destroy(&mu_);
131 grpc_pollset_set_destroy(grpc_polling_entity_pollset_set(&pollent_));
132 grpc_httpcli_context_destroy(&httpcli_context_);
135 grpc_credentials_status
136 grpc_oauth2_token_fetcher_credentials_parse_server_response(
137 const grpc_http_response* response, grpc_mdelem* token_md,
138 grpc_millis* token_lifetime) {
139 char* null_terminated_body = nullptr;
140 grpc_credentials_status status = GRPC_CREDENTIALS_OK;
143 if (response == nullptr) {
144 gpr_log(GPR_ERROR, "Received NULL response.");
145 status = GRPC_CREDENTIALS_ERROR;
149 if (response->body_length > 0) {
150 null_terminated_body =
151 static_cast<char*>(gpr_malloc(response->body_length + 1));
152 null_terminated_body[response->body_length] = '\0';
153 memcpy(null_terminated_body, response->body, response->body_length);
156 if (response->status != 200) {
157 gpr_log(GPR_ERROR, "Call to http server ended with error %d [%s].",
159 null_terminated_body != nullptr ? null_terminated_body : "");
160 status = GRPC_CREDENTIALS_ERROR;
163 const char* access_token = nullptr;
164 const char* token_type = nullptr;
165 const char* expires_in = nullptr;
166 Json::Object::const_iterator it;
167 grpc_error* error = GRPC_ERROR_NONE;
168 json = Json::Parse(null_terminated_body, &error);
169 if (error != GRPC_ERROR_NONE) {
170 gpr_log(GPR_ERROR, "Could not parse JSON from %s: %s",
171 null_terminated_body, grpc_error_string(error));
172 GRPC_ERROR_UNREF(error);
173 status = GRPC_CREDENTIALS_ERROR;
176 if (json.type() != Json::Type::OBJECT) {
177 gpr_log(GPR_ERROR, "Response should be a JSON object");
178 status = GRPC_CREDENTIALS_ERROR;
181 it = json.object_value().find("access_token");
182 if (it == json.object_value().end() ||
183 it->second.type() != Json::Type::STRING) {
184 gpr_log(GPR_ERROR, "Missing or invalid access_token in JSON.");
185 status = GRPC_CREDENTIALS_ERROR;
188 access_token = it->second.string_value().c_str();
189 it = json.object_value().find("token_type");
190 if (it == json.object_value().end() ||
191 it->second.type() != Json::Type::STRING) {
192 gpr_log(GPR_ERROR, "Missing or invalid token_type in JSON.");
193 status = GRPC_CREDENTIALS_ERROR;
196 token_type = it->second.string_value().c_str();
197 it = json.object_value().find("expires_in");
198 if (it == json.object_value().end() ||
199 it->second.type() != Json::Type::NUMBER) {
200 gpr_log(GPR_ERROR, "Missing or invalid expires_in in JSON.");
201 status = GRPC_CREDENTIALS_ERROR;
204 expires_in = it->second.string_value().c_str();
205 *token_lifetime = strtol(expires_in, nullptr, 10) * GPR_MS_PER_SEC;
206 if (!GRPC_MDISNULL(*token_md)) GRPC_MDELEM_UNREF(*token_md);
207 *token_md = grpc_mdelem_from_slices(
208 grpc_core::ExternallyManagedSlice(GRPC_AUTHORIZATION_METADATA_KEY),
209 grpc_slice_from_cpp_string(
210 absl::StrCat(token_type, " ", access_token)));
211 status = GRPC_CREDENTIALS_OK;
215 if (status != GRPC_CREDENTIALS_OK && !GRPC_MDISNULL(*token_md)) {
216 GRPC_MDELEM_UNREF(*token_md);
217 *token_md = GRPC_MDNULL;
219 gpr_free(null_terminated_body);
223 static void on_oauth2_token_fetcher_http_response(void* user_data,
225 GRPC_LOG_IF_ERROR("oauth_fetch", GRPC_ERROR_REF(error));
226 grpc_credentials_metadata_request* r =
227 static_cast<grpc_credentials_metadata_request*>(user_data);
228 grpc_oauth2_token_fetcher_credentials* c =
229 reinterpret_cast<grpc_oauth2_token_fetcher_credentials*>(r->creds.get());
230 c->on_http_response(r, error);
233 void grpc_oauth2_token_fetcher_credentials::on_http_response(
234 grpc_credentials_metadata_request* r, grpc_error* error) {
235 grpc_mdelem access_token_md = GRPC_MDNULL;
236 grpc_millis token_lifetime = 0;
237 grpc_credentials_status status =
238 error == GRPC_ERROR_NONE
239 ? grpc_oauth2_token_fetcher_credentials_parse_server_response(
240 &r->response, &access_token_md, &token_lifetime)
241 : GRPC_CREDENTIALS_ERROR;
242 // Update cache and grab list of pending requests.
244 token_fetch_pending_ = false;
245 access_token_md_ = GRPC_MDELEM_REF(access_token_md);
247 status == GRPC_CREDENTIALS_OK
248 ? gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
249 gpr_time_from_millis(token_lifetime, GPR_TIMESPAN))
250 : gpr_inf_past(GPR_CLOCK_MONOTONIC);
251 grpc_oauth2_pending_get_request_metadata* pending_request = pending_requests_;
252 pending_requests_ = nullptr;
254 // Invoke callbacks for all pending requests.
255 while (pending_request != nullptr) {
256 grpc_error* new_error = GRPC_ERROR_NONE;
257 if (status == GRPC_CREDENTIALS_OK) {
258 grpc_credentials_mdelem_array_add(pending_request->md_array,
261 new_error = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
262 "Error occurred when fetching oauth2 token.", &error, 1);
264 grpc_core::ExecCtx::Run(DEBUG_LOCATION,
265 pending_request->on_request_metadata, new_error);
266 grpc_polling_entity_del_from_pollset_set(
267 pending_request->pollent, grpc_polling_entity_pollset_set(&pollent_));
268 grpc_oauth2_pending_get_request_metadata* prev = pending_request;
269 pending_request = pending_request->next;
272 GRPC_MDELEM_UNREF(access_token_md);
274 grpc_credentials_metadata_request_destroy(r);
277 bool grpc_oauth2_token_fetcher_credentials::get_request_metadata(
278 grpc_polling_entity* pollent, grpc_auth_metadata_context /*context*/,
279 grpc_credentials_mdelem_array* md_array, grpc_closure* on_request_metadata,
280 grpc_error** /*error*/) {
281 // Check if we can use the cached token.
282 grpc_millis refresh_threshold =
283 GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS * GPR_MS_PER_SEC;
284 grpc_mdelem cached_access_token_md = GRPC_MDNULL;
286 if (!GRPC_MDISNULL(access_token_md_) &&
288 gpr_time_sub(token_expiration_, gpr_now(GPR_CLOCK_MONOTONIC)),
289 gpr_time_from_seconds(GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS,
290 GPR_TIMESPAN)) > 0) {
291 cached_access_token_md = GRPC_MDELEM_REF(access_token_md_);
293 if (!GRPC_MDISNULL(cached_access_token_md)) {
295 grpc_credentials_mdelem_array_add(md_array, cached_access_token_md);
296 GRPC_MDELEM_UNREF(cached_access_token_md);
299 // Couldn't get the token from the cache.
300 // Add request to pending_requests_ and start a new fetch if needed.
301 grpc_oauth2_pending_get_request_metadata* pending_request =
302 static_cast<grpc_oauth2_pending_get_request_metadata*>(
303 gpr_malloc(sizeof(*pending_request)));
304 pending_request->md_array = md_array;
305 pending_request->on_request_metadata = on_request_metadata;
306 pending_request->pollent = pollent;
307 grpc_polling_entity_add_to_pollset_set(
308 pollent, grpc_polling_entity_pollset_set(&pollent_));
309 pending_request->next = pending_requests_;
310 pending_requests_ = pending_request;
311 bool start_fetch = false;
312 if (!token_fetch_pending_) {
313 token_fetch_pending_ = true;
319 fetch_oauth2(grpc_credentials_metadata_request_create(this->Ref()),
320 &httpcli_context_, &pollent_,
321 on_oauth2_token_fetcher_http_response,
322 grpc_core::ExecCtx::Get()->Now() + refresh_threshold);
327 void grpc_oauth2_token_fetcher_credentials::cancel_get_request_metadata(
328 grpc_credentials_mdelem_array* md_array, grpc_error* error) {
330 grpc_oauth2_pending_get_request_metadata* prev = nullptr;
331 grpc_oauth2_pending_get_request_metadata* pending_request = pending_requests_;
332 while (pending_request != nullptr) {
333 if (pending_request->md_array == md_array) {
334 // Remove matching pending request from the list.
335 if (prev != nullptr) {
336 prev->next = pending_request->next;
338 pending_requests_ = pending_request->next;
340 // Invoke the callback immediately with an error.
341 grpc_core::ExecCtx::Run(DEBUG_LOCATION,
342 pending_request->on_request_metadata,
343 GRPC_ERROR_REF(error));
344 gpr_free(pending_request);
347 prev = pending_request;
348 pending_request = pending_request->next;
351 GRPC_ERROR_UNREF(error);
354 grpc_oauth2_token_fetcher_credentials::grpc_oauth2_token_fetcher_credentials()
355 : grpc_call_credentials(GRPC_CALL_CREDENTIALS_TYPE_OAUTH2),
356 token_expiration_(gpr_inf_past(GPR_CLOCK_MONOTONIC)),
357 pollent_(grpc_polling_entity_create_from_pollset_set(
358 grpc_pollset_set_create())) {
360 grpc_httpcli_context_init(&httpcli_context_);
363 std::string grpc_oauth2_token_fetcher_credentials::debug_string() {
364 return "OAuth2TokenFetcherCredentials";
368 // Google Compute Engine credentials.
373 class grpc_compute_engine_token_fetcher_credentials
374 : public grpc_oauth2_token_fetcher_credentials {
376 grpc_compute_engine_token_fetcher_credentials() = default;
377 ~grpc_compute_engine_token_fetcher_credentials() override = default;
380 void fetch_oauth2(grpc_credentials_metadata_request* metadata_req,
381 grpc_httpcli_context* http_context,
382 grpc_polling_entity* pollent,
383 grpc_iomgr_cb_func response_cb,
384 grpc_millis deadline) override {
385 grpc_http_header header = {const_cast<char*>("Metadata-Flavor"),
386 const_cast<char*>("Google")};
387 grpc_httpcli_request request;
388 memset(&request, 0, sizeof(grpc_httpcli_request));
389 request.host = (char*)GRPC_COMPUTE_ENGINE_METADATA_HOST;
390 request.http.path = (char*)GRPC_COMPUTE_ENGINE_METADATA_TOKEN_PATH;
391 request.http.hdr_count = 1;
392 request.http.hdrs = &header;
393 /* TODO(ctiller): Carry the resource_quota in ctx and share it with the host
394 channel. This would allow us to cancel an authentication query when under
395 extreme memory pressure. */
396 grpc_resource_quota* resource_quota =
397 grpc_resource_quota_create("oauth2_credentials");
398 grpc_httpcli_get(http_context, pollent, resource_quota, &request, deadline,
399 GRPC_CLOSURE_INIT(&http_get_cb_closure_, response_cb,
400 metadata_req, grpc_schedule_on_exec_ctx),
401 &metadata_req->response);
402 grpc_resource_quota_unref_internal(resource_quota);
405 std::string debug_string() override {
406 return absl::StrFormat(
407 "GoogleComputeEngineTokenFetcherCredentials{%s}",
408 grpc_oauth2_token_fetcher_credentials::debug_string());
412 grpc_closure http_get_cb_closure_;
417 grpc_call_credentials* grpc_google_compute_engine_credentials_create(
419 GRPC_API_TRACE("grpc_compute_engine_credentials_create(reserved=%p)", 1,
421 GPR_ASSERT(reserved == nullptr);
422 return grpc_core::MakeRefCounted<
423 grpc_compute_engine_token_fetcher_credentials>()
428 // Google Refresh Token credentials.
431 grpc_google_refresh_token_credentials::
432 ~grpc_google_refresh_token_credentials() {
433 grpc_auth_refresh_token_destruct(&refresh_token_);
436 void grpc_google_refresh_token_credentials::fetch_oauth2(
437 grpc_credentials_metadata_request* metadata_req,
438 grpc_httpcli_context* httpcli_context, grpc_polling_entity* pollent,
439 grpc_iomgr_cb_func response_cb, grpc_millis deadline) {
440 grpc_http_header header = {
441 const_cast<char*>("Content-Type"),
442 const_cast<char*>("application/x-www-form-urlencoded")};
443 grpc_httpcli_request request;
444 std::string body = absl::StrFormat(
445 GRPC_REFRESH_TOKEN_POST_BODY_FORMAT_STRING, refresh_token_.client_id,
446 refresh_token_.client_secret, refresh_token_.refresh_token);
447 memset(&request, 0, sizeof(grpc_httpcli_request));
448 request.host = (char*)GRPC_GOOGLE_OAUTH2_SERVICE_HOST;
449 request.http.path = (char*)GRPC_GOOGLE_OAUTH2_SERVICE_TOKEN_PATH;
450 request.http.hdr_count = 1;
451 request.http.hdrs = &header;
452 request.handshaker = &grpc_httpcli_ssl;
453 /* TODO(ctiller): Carry the resource_quota in ctx and share it with the host
454 channel. This would allow us to cancel an authentication query when under
455 extreme memory pressure. */
456 grpc_resource_quota* resource_quota =
457 grpc_resource_quota_create("oauth2_credentials_refresh");
458 grpc_httpcli_post(httpcli_context, pollent, resource_quota, &request,
459 body.c_str(), body.size(), deadline,
460 GRPC_CLOSURE_INIT(&http_post_cb_closure_, response_cb,
461 metadata_req, grpc_schedule_on_exec_ctx),
462 &metadata_req->response);
463 grpc_resource_quota_unref_internal(resource_quota);
466 grpc_google_refresh_token_credentials::grpc_google_refresh_token_credentials(
467 grpc_auth_refresh_token refresh_token)
468 : refresh_token_(refresh_token) {}
470 grpc_core::RefCountedPtr<grpc_call_credentials>
471 grpc_refresh_token_credentials_create_from_auth_refresh_token(
472 grpc_auth_refresh_token refresh_token) {
473 if (!grpc_auth_refresh_token_is_valid(&refresh_token)) {
474 gpr_log(GPR_ERROR, "Invalid input for refresh token credentials creation");
477 return grpc_core::MakeRefCounted<grpc_google_refresh_token_credentials>(
481 std::string grpc_google_refresh_token_credentials::debug_string() {
482 return absl::StrFormat("GoogleRefreshToken{ClientID:%s,%s}",
483 refresh_token_.client_id,
484 grpc_oauth2_token_fetcher_credentials::debug_string());
487 static std::string create_loggable_refresh_token(
488 grpc_auth_refresh_token* token) {
489 if (strcmp(token->type, GRPC_AUTH_JSON_TYPE_INVALID) == 0) {
490 return "<Invalid json token>";
492 return absl::StrFormat(
493 "{\n type: %s\n client_id: %s\n client_secret: "
494 "<redacted>\n refresh_token: <redacted>\n}",
495 token->type, token->client_id);
498 grpc_call_credentials* grpc_google_refresh_token_credentials_create(
499 const char* json_refresh_token, void* reserved) {
500 grpc_auth_refresh_token token =
501 grpc_auth_refresh_token_create_from_string(json_refresh_token);
502 if (GRPC_TRACE_FLAG_ENABLED(grpc_api_trace)) {
504 "grpc_refresh_token_credentials_create(json_refresh_token=%s, "
506 create_loggable_refresh_token(&token).c_str(), reserved);
508 GPR_ASSERT(reserved == nullptr);
509 return grpc_refresh_token_credentials_create_from_auth_refresh_token(token)
517 namespace grpc_core {
521 void MaybeAddToBody(const char* field_name, const char* field,
522 std::vector<std::string>* body) {
523 if (field == nullptr || strlen(field) == 0) return;
524 body->push_back(absl::StrFormat("&%s=%s", field_name, field));
527 grpc_error* LoadTokenFile(const char* path, gpr_slice* token) {
528 grpc_error* err = grpc_load_file(path, 1, token);
529 if (err != GRPC_ERROR_NONE) return err;
530 if (GRPC_SLICE_LENGTH(*token) == 0) {
531 gpr_log(GPR_ERROR, "Token file %s is empty", path);
532 err = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Token file is empty.");
537 class StsTokenFetcherCredentials
538 : public grpc_oauth2_token_fetcher_credentials {
540 StsTokenFetcherCredentials(grpc_uri* sts_url, // Ownership transferred.
541 const grpc_sts_credentials_options* options)
543 resource_(gpr_strdup(options->resource)),
544 audience_(gpr_strdup(options->audience)),
545 scope_(gpr_strdup(options->scope)),
546 requested_token_type_(gpr_strdup(options->requested_token_type)),
547 subject_token_path_(gpr_strdup(options->subject_token_path)),
548 subject_token_type_(gpr_strdup(options->subject_token_type)),
549 actor_token_path_(gpr_strdup(options->actor_token_path)),
550 actor_token_type_(gpr_strdup(options->actor_token_type)) {}
552 ~StsTokenFetcherCredentials() override { grpc_uri_destroy(sts_url_); }
554 std::string debug_string() override {
555 return absl::StrFormat(
556 "StsTokenFetcherCredentials{Path:%s,Authority:%s,%s}", sts_url_->path,
558 grpc_oauth2_token_fetcher_credentials::debug_string());
562 void fetch_oauth2(grpc_credentials_metadata_request* metadata_req,
563 grpc_httpcli_context* http_context,
564 grpc_polling_entity* pollent,
565 grpc_iomgr_cb_func response_cb,
566 grpc_millis deadline) override {
567 char* body = nullptr;
568 size_t body_length = 0;
569 grpc_error* err = FillBody(&body, &body_length);
570 if (err != GRPC_ERROR_NONE) {
571 response_cb(metadata_req, err);
572 GRPC_ERROR_UNREF(err);
575 grpc_http_header header = {
576 const_cast<char*>("Content-Type"),
577 const_cast<char*>("application/x-www-form-urlencoded")};
578 grpc_httpcli_request request;
579 memset(&request, 0, sizeof(grpc_httpcli_request));
580 request.host = (char*)sts_url_->authority;
581 request.http.path = (char*)sts_url_->path;
582 request.http.hdr_count = 1;
583 request.http.hdrs = &header;
584 request.handshaker = (strcmp(sts_url_->scheme, "https") == 0)
586 : &grpc_httpcli_plaintext;
587 /* TODO(ctiller): Carry the resource_quota in ctx and share it with the host
588 channel. This would allow us to cancel an authentication query when under
589 extreme memory pressure. */
590 grpc_resource_quota* resource_quota =
591 grpc_resource_quota_create("oauth2_credentials_refresh");
593 http_context, pollent, resource_quota, &request, body, body_length,
595 GRPC_CLOSURE_INIT(&http_post_cb_closure_, response_cb, metadata_req,
596 grpc_schedule_on_exec_ctx),
597 &metadata_req->response);
598 grpc_resource_quota_unref_internal(resource_quota);
602 grpc_error* FillBody(char** body, size_t* body_length) {
604 std::vector<std::string> body_parts;
605 grpc_slice subject_token = grpc_empty_slice();
606 grpc_slice actor_token = grpc_empty_slice();
607 grpc_error* err = GRPC_ERROR_NONE;
609 auto cleanup = [&body, &body_length, &body_parts, &subject_token,
610 &actor_token, &err]() {
611 if (err == GRPC_ERROR_NONE) {
612 std::string body_str = absl::StrJoin(body_parts, "");
613 *body = gpr_strdup(body_str.c_str());
614 *body_length = body_str.size();
616 grpc_slice_unref_internal(subject_token);
617 grpc_slice_unref_internal(actor_token);
621 err = LoadTokenFile(subject_token_path_.get(), &subject_token);
622 if (err != GRPC_ERROR_NONE) return cleanup();
623 body_parts.push_back(absl::StrFormat(
624 GRPC_STS_POST_MINIMAL_BODY_FORMAT_STRING,
625 reinterpret_cast<const char*>(GRPC_SLICE_START_PTR(subject_token)),
626 subject_token_type_.get()));
627 MaybeAddToBody("resource", resource_.get(), &body_parts);
628 MaybeAddToBody("audience", audience_.get(), &body_parts);
629 MaybeAddToBody("scope", scope_.get(), &body_parts);
630 MaybeAddToBody("requested_token_type", requested_token_type_.get(),
632 if ((actor_token_path_ != nullptr) && *actor_token_path_ != '\0') {
633 err = LoadTokenFile(actor_token_path_.get(), &actor_token);
634 if (err != GRPC_ERROR_NONE) return cleanup();
637 reinterpret_cast<const char*>(GRPC_SLICE_START_PTR(actor_token)),
639 MaybeAddToBody("actor_token_type", actor_token_type_.get(), &body_parts);
645 grpc_closure http_post_cb_closure_;
646 grpc_core::UniquePtr<char> resource_;
647 grpc_core::UniquePtr<char> audience_;
648 grpc_core::UniquePtr<char> scope_;
649 grpc_core::UniquePtr<char> requested_token_type_;
650 grpc_core::UniquePtr<char> subject_token_path_;
651 grpc_core::UniquePtr<char> subject_token_type_;
652 grpc_core::UniquePtr<char> actor_token_path_;
653 grpc_core::UniquePtr<char> actor_token_type_;
658 grpc_error* ValidateStsCredentialsOptions(
659 const grpc_sts_credentials_options* options, grpc_uri** sts_url_out) {
660 struct GrpcUriDeleter {
661 void operator()(grpc_uri* uri) { grpc_uri_destroy(uri); }
663 *sts_url_out = nullptr;
664 absl::InlinedVector<grpc_error*, 3> error_list;
665 std::unique_ptr<grpc_uri, GrpcUriDeleter> sts_url(
666 options->token_exchange_service_uri != nullptr
667 ? grpc_uri_parse(options->token_exchange_service_uri, false)
669 if (sts_url == nullptr) {
670 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
671 "Invalid or missing STS endpoint URL"));
673 if (strcmp(sts_url->scheme, "https") != 0 &&
674 strcmp(sts_url->scheme, "http") != 0) {
675 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
676 "Invalid URI scheme, must be https to http."));
679 if (options->subject_token_path == nullptr ||
680 strlen(options->subject_token_path) == 0) {
681 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
682 "subject_token needs to be specified"));
684 if (options->subject_token_type == nullptr ||
685 strlen(options->subject_token_type) == 0) {
686 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
687 "subject_token_type needs to be specified"));
689 if (error_list.empty()) {
690 *sts_url_out = sts_url.release();
691 return GRPC_ERROR_NONE;
693 return GRPC_ERROR_CREATE_FROM_VECTOR("Invalid STS Credentials Options",
698 } // namespace grpc_core
700 grpc_call_credentials* grpc_sts_credentials_create(
701 const grpc_sts_credentials_options* options, void* reserved) {
702 GPR_ASSERT(reserved == nullptr);
705 grpc_core::ValidateStsCredentialsOptions(options, &sts_url);
706 if (error != GRPC_ERROR_NONE) {
707 gpr_log(GPR_ERROR, "STS Credentials creation failed. Error: %s.",
708 grpc_error_string(error));
709 GRPC_ERROR_UNREF(error);
712 return grpc_core::MakeRefCounted<grpc_core::StsTokenFetcherCredentials>(
718 // Oauth2 Access Token credentials.
721 grpc_access_token_credentials::~grpc_access_token_credentials() {
722 GRPC_MDELEM_UNREF(access_token_md_);
725 bool grpc_access_token_credentials::get_request_metadata(
726 grpc_polling_entity* /*pollent*/, grpc_auth_metadata_context /*context*/,
727 grpc_credentials_mdelem_array* md_array,
728 grpc_closure* /*on_request_metadata*/, grpc_error** /*error*/) {
729 grpc_credentials_mdelem_array_add(md_array, access_token_md_);
733 void grpc_access_token_credentials::cancel_get_request_metadata(
734 grpc_credentials_mdelem_array* /*md_array*/, grpc_error* error) {
735 GRPC_ERROR_UNREF(error);
738 grpc_access_token_credentials::grpc_access_token_credentials(
739 const char* access_token)
740 : grpc_call_credentials(GRPC_CALL_CREDENTIALS_TYPE_OAUTH2) {
741 grpc_core::ExecCtx exec_ctx;
742 access_token_md_ = grpc_mdelem_from_slices(
743 grpc_core::ExternallyManagedSlice(GRPC_AUTHORIZATION_METADATA_KEY),
744 grpc_slice_from_cpp_string(absl::StrCat("Bearer ", access_token)));
747 std::string grpc_access_token_credentials::debug_string() {
748 bool access_token_present = !GRPC_MDISNULL(access_token_md_);
749 return absl::StrFormat("AccessTokenCredentials{Token:%s}",
750 access_token_present ? "present" : "absent");
753 grpc_call_credentials* grpc_access_token_credentials_create(
754 const char* access_token, void* reserved) {
756 "grpc_access_token_credentials_create(access_token=<redacted>, "
759 GPR_ASSERT(reserved == nullptr);
760 return grpc_core::MakeRefCounted<grpc_access_token_credentials>(access_token)