Imported Upstream version 1.33.1
[platform/upstream/grpc.git] / src / core / lib / security / credentials / oauth2 / oauth2_credentials.cc
1 /*
2  *
3  * Copyright 2015 gRPC authors.
4  *
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
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
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.
16  *
17  */
18
19 #include <grpc/support/port_platform.h>
20
21 #include "src/core/lib/json/json.h"
22 #include "src/core/lib/security/credentials/oauth2/oauth2_credentials.h"
23
24 #include <string.h>
25
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"
30
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>
37
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"
46
47 using grpc_core::Json;
48
49 //
50 // Auth Refresh Token.
51 //
52
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);
57 }
58
59 grpc_auth_refresh_token grpc_auth_refresh_token_create_from_json(
60     const Json& json) {
61   grpc_auth_refresh_token result;
62   const char* prop_value;
63   int success = 0;
64   grpc_error* error = GRPC_ERROR_NONE;
65
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.");
70     goto end;
71   }
72
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)) {
77     goto end;
78   }
79   result.type = GRPC_AUTH_JSON_TYPE_AUTHORIZED_USER;
80
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)) {
86     goto end;
87   }
88   success = 1;
89
90 end:
91   if (!success) grpc_auth_refresh_token_destruct(&result);
92   return result;
93 }
94
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);
102   }
103   return grpc_auth_refresh_token_create_from_json(json);
104 }
105
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;
112   }
113   if (refresh_token->client_secret != nullptr) {
114     gpr_free(refresh_token->client_secret);
115     refresh_token->client_secret = nullptr;
116   }
117   if (refresh_token->refresh_token != nullptr) {
118     gpr_free(refresh_token->refresh_token);
119     refresh_token->refresh_token = nullptr;
120   }
121 }
122
123 //
124 // Oauth2 Token Fetcher credentials.
125 //
126
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_);
133 }
134
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;
141   Json json;
142
143   if (response == nullptr) {
144     gpr_log(GPR_ERROR, "Received NULL response.");
145     status = GRPC_CREDENTIALS_ERROR;
146     goto end;
147   }
148
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);
154   }
155
156   if (response->status != 200) {
157     gpr_log(GPR_ERROR, "Call to http server ended with error %d [%s].",
158             response->status,
159             null_terminated_body != nullptr ? null_terminated_body : "");
160     status = GRPC_CREDENTIALS_ERROR;
161     goto end;
162   } else {
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;
174       goto end;
175     }
176     if (json.type() != Json::Type::OBJECT) {
177       gpr_log(GPR_ERROR, "Response should be a JSON object");
178       status = GRPC_CREDENTIALS_ERROR;
179       goto end;
180     }
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;
186       goto end;
187     }
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;
194       goto end;
195     }
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;
202       goto end;
203     }
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;
212   }
213
214 end:
215   if (status != GRPC_CREDENTIALS_OK && !GRPC_MDISNULL(*token_md)) {
216     GRPC_MDELEM_UNREF(*token_md);
217     *token_md = GRPC_MDNULL;
218   }
219   gpr_free(null_terminated_body);
220   return status;
221 }
222
223 static void on_oauth2_token_fetcher_http_response(void* user_data,
224                                                   grpc_error* error) {
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);
231 }
232
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.
243   gpr_mu_lock(&mu_);
244   token_fetch_pending_ = false;
245   access_token_md_ = GRPC_MDELEM_REF(access_token_md);
246   token_expiration_ =
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;
253   gpr_mu_unlock(&mu_);
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,
259                                         access_token_md);
260     } else {
261       new_error = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
262           "Error occurred when fetching oauth2 token.", &error, 1);
263     }
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;
270     gpr_free(prev);
271   }
272   GRPC_MDELEM_UNREF(access_token_md);
273   Unref();
274   grpc_credentials_metadata_request_destroy(r);
275 }
276
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;
285   gpr_mu_lock(&mu_);
286   if (!GRPC_MDISNULL(access_token_md_) &&
287       gpr_time_cmp(
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_);
292   }
293   if (!GRPC_MDISNULL(cached_access_token_md)) {
294     gpr_mu_unlock(&mu_);
295     grpc_credentials_mdelem_array_add(md_array, cached_access_token_md);
296     GRPC_MDELEM_UNREF(cached_access_token_md);
297     return true;
298   }
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;
314     start_fetch = true;
315   }
316   gpr_mu_unlock(&mu_);
317   if (start_fetch) {
318     Ref().release();
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);
323   }
324   return false;
325 }
326
327 void grpc_oauth2_token_fetcher_credentials::cancel_get_request_metadata(
328     grpc_credentials_mdelem_array* md_array, grpc_error* error) {
329   gpr_mu_lock(&mu_);
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;
337       } else {
338         pending_requests_ = pending_request->next;
339       }
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);
345       break;
346     }
347     prev = pending_request;
348     pending_request = pending_request->next;
349   }
350   gpr_mu_unlock(&mu_);
351   GRPC_ERROR_UNREF(error);
352 }
353
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())) {
359   gpr_mu_init(&mu_);
360   grpc_httpcli_context_init(&httpcli_context_);
361 }
362
363 std::string grpc_oauth2_token_fetcher_credentials::debug_string() {
364   return "OAuth2TokenFetcherCredentials";
365 }
366
367 //
368 //  Google Compute Engine credentials.
369 //
370
371 namespace {
372
373 class grpc_compute_engine_token_fetcher_credentials
374     : public grpc_oauth2_token_fetcher_credentials {
375  public:
376   grpc_compute_engine_token_fetcher_credentials() = default;
377   ~grpc_compute_engine_token_fetcher_credentials() override = default;
378
379  protected:
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);
403   }
404
405   std::string debug_string() override {
406     return absl::StrFormat(
407         "GoogleComputeEngineTokenFetcherCredentials{%s}",
408         grpc_oauth2_token_fetcher_credentials::debug_string());
409   }
410
411  private:
412   grpc_closure http_get_cb_closure_;
413 };
414
415 }  // namespace
416
417 grpc_call_credentials* grpc_google_compute_engine_credentials_create(
418     void* reserved) {
419   GRPC_API_TRACE("grpc_compute_engine_credentials_create(reserved=%p)", 1,
420                  (reserved));
421   GPR_ASSERT(reserved == nullptr);
422   return grpc_core::MakeRefCounted<
423              grpc_compute_engine_token_fetcher_credentials>()
424       .release();
425 }
426
427 //
428 // Google Refresh Token credentials.
429 //
430
431 grpc_google_refresh_token_credentials::
432     ~grpc_google_refresh_token_credentials() {
433   grpc_auth_refresh_token_destruct(&refresh_token_);
434 }
435
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);
464 }
465
466 grpc_google_refresh_token_credentials::grpc_google_refresh_token_credentials(
467     grpc_auth_refresh_token refresh_token)
468     : refresh_token_(refresh_token) {}
469
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");
475     return nullptr;
476   }
477   return grpc_core::MakeRefCounted<grpc_google_refresh_token_credentials>(
478       refresh_token);
479 }
480
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());
485 }
486
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>";
491   }
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);
496 }
497
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)) {
503     gpr_log(GPR_INFO,
504             "grpc_refresh_token_credentials_create(json_refresh_token=%s, "
505             "reserved=%p)",
506             create_loggable_refresh_token(&token).c_str(), reserved);
507   }
508   GPR_ASSERT(reserved == nullptr);
509   return grpc_refresh_token_credentials_create_from_auth_refresh_token(token)
510       .release();
511 }
512
513 //
514 // STS credentials.
515 //
516
517 namespace grpc_core {
518
519 namespace {
520
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));
525 }
526
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.");
533   }
534   return err;
535 }
536
537 class StsTokenFetcherCredentials
538     : public grpc_oauth2_token_fetcher_credentials {
539  public:
540   StsTokenFetcherCredentials(grpc_uri* sts_url,  // Ownership transferred.
541                              const grpc_sts_credentials_options* options)
542       : sts_url_(sts_url),
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)) {}
551
552   ~StsTokenFetcherCredentials() override { grpc_uri_destroy(sts_url_); }
553
554   std::string debug_string() override {
555     return absl::StrFormat(
556         "StsTokenFetcherCredentials{Path:%s,Authority:%s,%s}", sts_url_->path,
557         sts_url_->authority,
558         grpc_oauth2_token_fetcher_credentials::debug_string());
559   }
560
561  private:
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);
573       return;
574     }
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)
585                              ? &grpc_httpcli_ssl
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");
592     grpc_httpcli_post(
593         http_context, pollent, resource_quota, &request, body, body_length,
594         deadline,
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);
599     gpr_free(body);
600   }
601
602   grpc_error* FillBody(char** body, size_t* body_length) {
603     *body = nullptr;
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;
608
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();
615       }
616       grpc_slice_unref_internal(subject_token);
617       grpc_slice_unref_internal(actor_token);
618       return err;
619     };
620
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(),
631                    &body_parts);
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();
635       MaybeAddToBody(
636           "actor_token",
637           reinterpret_cast<const char*>(GRPC_SLICE_START_PTR(actor_token)),
638           &body_parts);
639       MaybeAddToBody("actor_token_type", actor_token_type_.get(), &body_parts);
640     }
641     return cleanup();
642   }
643
644   grpc_uri* sts_url_;
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_;
654 };
655
656 }  // namespace
657
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); }
662   };
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)
668           : nullptr);
669   if (sts_url == nullptr) {
670     error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
671         "Invalid or missing STS endpoint URL"));
672   } else {
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."));
677     }
678   }
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"));
683   }
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"));
688   }
689   if (error_list.empty()) {
690     *sts_url_out = sts_url.release();
691     return GRPC_ERROR_NONE;
692   } else {
693     return GRPC_ERROR_CREATE_FROM_VECTOR("Invalid STS Credentials Options",
694                                          &error_list);
695   }
696 }
697
698 }  // namespace grpc_core
699
700 grpc_call_credentials* grpc_sts_credentials_create(
701     const grpc_sts_credentials_options* options, void* reserved) {
702   GPR_ASSERT(reserved == nullptr);
703   grpc_uri* sts_url;
704   grpc_error* error =
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);
710     return nullptr;
711   }
712   return grpc_core::MakeRefCounted<grpc_core::StsTokenFetcherCredentials>(
713              sts_url, options)
714       .release();
715 }
716
717 //
718 // Oauth2 Access Token credentials.
719 //
720
721 grpc_access_token_credentials::~grpc_access_token_credentials() {
722   GRPC_MDELEM_UNREF(access_token_md_);
723 }
724
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_);
730   return true;
731 }
732
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);
736 }
737
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)));
745 }
746
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");
751 }
752
753 grpc_call_credentials* grpc_access_token_credentials_create(
754     const char* access_token, void* reserved) {
755   GRPC_API_TRACE(
756       "grpc_access_token_credentials_create(access_token=<redacted>, "
757       "reserved=%p)",
758       1, (reserved));
759   GPR_ASSERT(reserved == nullptr);
760   return grpc_core::MakeRefCounted<grpc_access_token_credentials>(access_token)
761       .release();
762 }