• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "src/core/lib/security/credentials/oauth2/oauth2_credentials.h"
20 
21 #include <grpc/credentials.h>
22 #include <grpc/grpc.h>
23 #include <grpc/grpc_security.h>
24 #include <grpc/slice.h>
25 #include <grpc/support/alloc.h>
26 #include <grpc/support/json.h>
27 #include <grpc/support/port_platform.h>
28 #include <grpc/support/string_util.h>
29 #include <grpc/support/time.h>
30 #include <string.h>
31 
32 #include <algorithm>
33 #include <atomic>
34 #include <map>
35 #include <memory>
36 #include <vector>
37 
38 #include "absl/log/check.h"
39 #include "absl/log/log.h"
40 #include "absl/status/status.h"
41 #include "absl/strings/numbers.h"
42 #include "absl/strings/str_cat.h"
43 #include "absl/strings/str_format.h"
44 #include "absl/strings/str_join.h"
45 #include "absl/strings/string_view.h"
46 #include "src/core/lib/debug/trace.h"
47 #include "src/core/lib/iomgr/error.h"
48 #include "src/core/lib/iomgr/pollset_set.h"
49 #include "src/core/lib/promise/context.h"
50 #include "src/core/lib/promise/poll.h"
51 #include "src/core/lib/promise/promise.h"
52 #include "src/core/lib/security/util/json_util.h"
53 #include "src/core/lib/transport/error_utils.h"
54 #include "src/core/lib/transport/metadata_batch.h"
55 #include "src/core/util/http_client/httpcli_ssl_credentials.h"
56 #include "src/core/util/json/json.h"
57 #include "src/core/util/json/json_reader.h"
58 #include "src/core/util/load_file.h"
59 #include "src/core/util/memory.h"
60 #include "src/core/util/ref_counted_ptr.h"
61 #include "src/core/util/status_helper.h"
62 #include "src/core/util/uri.h"
63 
64 using grpc_core::Json;
65 
66 //
67 // Auth Refresh Token.
68 //
69 
grpc_auth_refresh_token_is_valid(const grpc_auth_refresh_token * refresh_token)70 int grpc_auth_refresh_token_is_valid(
71     const grpc_auth_refresh_token* refresh_token) {
72   return (refresh_token != nullptr) &&
73          strcmp(refresh_token->type, GRPC_AUTH_JSON_TYPE_INVALID) != 0;
74 }
75 
grpc_auth_refresh_token_create_from_json(const Json & json)76 grpc_auth_refresh_token grpc_auth_refresh_token_create_from_json(
77     const Json& json) {
78   grpc_auth_refresh_token result;
79   const char* prop_value;
80   int success = 0;
81   grpc_error_handle error;
82 
83   memset(&result, 0, sizeof(grpc_auth_refresh_token));
84   result.type = GRPC_AUTH_JSON_TYPE_INVALID;
85   if (json.type() != Json::Type::kObject) {
86     LOG(ERROR) << "Invalid json.";
87     goto end;
88   }
89 
90   prop_value = grpc_json_get_string_property(json, "type", &error);
91   GRPC_LOG_IF_ERROR("Parsing refresh token", error);
92   if (prop_value == nullptr ||
93       strcmp(prop_value, GRPC_AUTH_JSON_TYPE_AUTHORIZED_USER) != 0) {
94     goto end;
95   }
96   result.type = GRPC_AUTH_JSON_TYPE_AUTHORIZED_USER;
97 
98   if (!grpc_copy_json_string_property(json, "client_secret",
99                                       &result.client_secret) ||
100       !grpc_copy_json_string_property(json, "client_id", &result.client_id) ||
101       !grpc_copy_json_string_property(json, "refresh_token",
102                                       &result.refresh_token)) {
103     goto end;
104   }
105   success = 1;
106 
107 end:
108   if (!success) grpc_auth_refresh_token_destruct(&result);
109   return result;
110 }
111 
grpc_auth_refresh_token_create_from_string(const char * json_string)112 grpc_auth_refresh_token grpc_auth_refresh_token_create_from_string(
113     const char* json_string) {
114   Json json;
115   auto json_or = grpc_core::JsonParse(json_string);
116   if (!json_or.ok()) {
117     LOG(ERROR) << "JSON parsing failed: " << json_or.status();
118   } else {
119     json = std::move(*json_or);
120   }
121   return grpc_auth_refresh_token_create_from_json(json);
122 }
123 
grpc_auth_refresh_token_destruct(grpc_auth_refresh_token * refresh_token)124 void grpc_auth_refresh_token_destruct(grpc_auth_refresh_token* refresh_token) {
125   if (refresh_token == nullptr) return;
126   refresh_token->type = GRPC_AUTH_JSON_TYPE_INVALID;
127   if (refresh_token->client_id != nullptr) {
128     gpr_free(refresh_token->client_id);
129     refresh_token->client_id = nullptr;
130   }
131   if (refresh_token->client_secret != nullptr) {
132     gpr_free(refresh_token->client_secret);
133     refresh_token->client_secret = nullptr;
134   }
135   if (refresh_token->refresh_token != nullptr) {
136     gpr_free(refresh_token->refresh_token);
137     refresh_token->refresh_token = nullptr;
138   }
139 }
140 
141 //
142 // Oauth2 Token parsing.
143 //
144 
145 grpc_credentials_status
grpc_oauth2_token_fetcher_credentials_parse_server_response_body(absl::string_view body,absl::optional<grpc_core::Slice> * token_value,grpc_core::Duration * token_lifetime)146 grpc_oauth2_token_fetcher_credentials_parse_server_response_body(
147     absl::string_view body, absl::optional<grpc_core::Slice>* token_value,
148     grpc_core::Duration* token_lifetime) {
149   auto json = grpc_core::JsonParse(body);
150   if (!json.ok()) {
151     LOG(ERROR) << "Could not parse JSON from " << body << ": " << json.status();
152     return GRPC_CREDENTIALS_ERROR;
153   }
154   if (json->type() != Json::Type::kObject) {
155     LOG(ERROR) << "Response should be a JSON object";
156     return GRPC_CREDENTIALS_ERROR;
157   }
158   auto it = json->object().find("access_token");
159   if (it == json->object().end() || it->second.type() != Json::Type::kString) {
160     LOG(ERROR) << "Missing or invalid access_token in JSON.";
161     return GRPC_CREDENTIALS_ERROR;
162   }
163   absl::string_view access_token = it->second.string();
164   it = json->object().find("token_type");
165   if (it == json->object().end() || it->second.type() != Json::Type::kString) {
166     LOG(ERROR) << "Missing or invalid token_type in JSON.";
167     return GRPC_CREDENTIALS_ERROR;
168   }
169   absl::string_view token_type = it->second.string();
170   it = json->object().find("expires_in");
171   if (it == json->object().end() || it->second.type() != Json::Type::kNumber) {
172     LOG(ERROR) << "Missing or invalid expires_in in JSON.";
173     return GRPC_CREDENTIALS_ERROR;
174   }
175   absl::string_view expires_in = it->second.string();
176   long seconds;
177   if (!absl::SimpleAtoi(expires_in, &seconds)) {
178     LOG(ERROR) << "Invalid expires_in in JSON.";
179     return GRPC_CREDENTIALS_ERROR;
180   }
181   *token_lifetime = grpc_core::Duration::Seconds(seconds);
182   *token_value = grpc_core::Slice::FromCopiedString(
183       absl::StrCat(token_type, " ", access_token));
184   return GRPC_CREDENTIALS_OK;
185 }
186 
187 grpc_credentials_status
grpc_oauth2_token_fetcher_credentials_parse_server_response(const grpc_http_response * response,absl::optional<grpc_core::Slice> * token_value,grpc_core::Duration * token_lifetime)188 grpc_oauth2_token_fetcher_credentials_parse_server_response(
189     const grpc_http_response* response,
190     absl::optional<grpc_core::Slice>* token_value,
191     grpc_core::Duration* token_lifetime) {
192   *token_value = absl::nullopt;
193   if (response == nullptr) {
194     LOG(ERROR) << "Received NULL response.";
195     return GRPC_CREDENTIALS_ERROR;
196   }
197   absl::string_view body(response->body, response->body_length);
198   if (response->status != 200) {
199     LOG(ERROR) << "Call to http server ended with error " << response->status
200                << " [" << body << "]";
201     return GRPC_CREDENTIALS_ERROR;
202   }
203   return grpc_oauth2_token_fetcher_credentials_parse_server_response_body(
204       body, token_value, token_lifetime);
205 }
206 
207 //
208 // Oauth2TokenFetcherCredentials
209 //
210 
211 namespace grpc_core {
212 
213 // State held for a pending HTTP request.
214 class Oauth2TokenFetcherCredentials::HttpFetchRequest final
215     : public TokenFetcherCredentials::FetchRequest {
216  public:
HttpFetchRequest(Oauth2TokenFetcherCredentials * creds,Timestamp deadline,absl::AnyInvocable<void (absl::StatusOr<RefCountedPtr<TokenFetcherCredentials::Token>>)> on_done)217   HttpFetchRequest(
218       Oauth2TokenFetcherCredentials* creds, Timestamp deadline,
219       absl::AnyInvocable<
220           void(absl::StatusOr<RefCountedPtr<TokenFetcherCredentials::Token>>)>
221           on_done)
222       : on_done_(std::move(on_done)) {
223     GRPC_CLOSURE_INIT(&on_http_response_, OnHttpResponse, this, nullptr);
224     Ref().release();  // Ref held by HTTP request callback.
225     http_request_ = creds->StartHttpRequest(creds->pollent(), deadline,
226                                             &response_, &on_http_response_);
227   }
228 
~HttpFetchRequest()229   ~HttpFetchRequest() override { grpc_http_response_destroy(&response_); }
230 
Orphan()231   void Orphan() override {
232     http_request_.reset();
233     Unref();
234   }
235 
236  private:
OnHttpResponse(void * arg,grpc_error_handle error)237   static void OnHttpResponse(void* arg, grpc_error_handle error) {
238     RefCountedPtr<HttpFetchRequest> self(static_cast<HttpFetchRequest*>(arg));
239     if (!error.ok()) {
240       self->on_done_(std::move(error));
241       return;
242     }
243     // Parse oauth2 token.
244     absl::optional<Slice> access_token_value;
245     Duration token_lifetime;
246     grpc_credentials_status status =
247         grpc_oauth2_token_fetcher_credentials_parse_server_response(
248             &self->response_, &access_token_value, &token_lifetime);
249     if (status != GRPC_CREDENTIALS_OK) {
250       self->on_done_(absl::UnavailableError("error parsing oauth2 token"));
251       return;
252     }
253     self->on_done_(MakeRefCounted<Token>(std::move(*access_token_value),
254                                          Timestamp::Now() + token_lifetime));
255   }
256 
257   OrphanablePtr<HttpRequest> http_request_;
258   grpc_closure on_http_response_;
259   grpc_http_response response_;
260   absl::AnyInvocable<void(
261       absl::StatusOr<RefCountedPtr<TokenFetcherCredentials::Token>>)>
262       on_done_;
263 };
264 
debug_string()265 std::string Oauth2TokenFetcherCredentials::debug_string() {
266   return "OAuth2TokenFetcherCredentials";
267 }
268 
type() const269 UniqueTypeName Oauth2TokenFetcherCredentials::type() const {
270   static UniqueTypeName::Factory kFactory("Oauth2");
271   return kFactory.Create();
272 }
273 
274 OrphanablePtr<TokenFetcherCredentials::FetchRequest>
FetchToken(Timestamp deadline,absl::AnyInvocable<void (absl::StatusOr<RefCountedPtr<TokenFetcherCredentials::Token>>)> on_done)275 Oauth2TokenFetcherCredentials::FetchToken(
276     Timestamp deadline,
277     absl::AnyInvocable<
278         void(absl::StatusOr<RefCountedPtr<TokenFetcherCredentials::Token>>)>
279         on_done) {
280   return MakeOrphanable<HttpFetchRequest>(this, deadline, std::move(on_done));
281 }
282 
283 }  // namespace grpc_core
284 
285 //
286 //  Google Compute Engine credentials.
287 //
288 
289 namespace {
290 
291 class grpc_compute_engine_token_fetcher_credentials
292     : public grpc_core::Oauth2TokenFetcherCredentials {
293  public:
294   grpc_compute_engine_token_fetcher_credentials() = default;
295   ~grpc_compute_engine_token_fetcher_credentials() override = default;
296 
debug_string()297   std::string debug_string() override {
298     return absl::StrFormat(
299         "GoogleComputeEngineTokenFetcherCredentials{%s}",
300         grpc_core::Oauth2TokenFetcherCredentials::debug_string());
301   }
302 
303  private:
StartHttpRequest(grpc_polling_entity * pollent,grpc_core::Timestamp deadline,grpc_http_response * response,grpc_closure * on_complete)304   grpc_core::OrphanablePtr<grpc_core::HttpRequest> StartHttpRequest(
305       grpc_polling_entity* pollent, grpc_core::Timestamp deadline,
306       grpc_http_response* response, grpc_closure* on_complete) override {
307     grpc_http_header header = {const_cast<char*>("Metadata-Flavor"),
308                                const_cast<char*>("Google")};
309     grpc_http_request request;
310     memset(&request, 0, sizeof(grpc_http_request));
311     request.hdr_count = 1;
312     request.hdrs = &header;
313     // TODO(ctiller): Carry the memory quota in ctx and share it with the host
314     // channel. This would allow us to cancel an authentication query when under
315     // extreme memory pressure.
316     auto uri = grpc_core::URI::Create("http", GRPC_COMPUTE_ENGINE_METADATA_HOST,
317                                       GRPC_COMPUTE_ENGINE_METADATA_TOKEN_PATH,
318                                       {} /* query params */, "" /* fragment */);
319     CHECK(uri.ok());  // params are hardcoded
320     auto http_request = grpc_core::HttpRequest::Get(
321         std::move(*uri), /*args=*/nullptr, pollent, &request, deadline,
322         on_complete, response,
323         grpc_core::RefCountedPtr<grpc_channel_credentials>(
324             grpc_insecure_credentials_create()));
325     http_request->Start();
326     return http_request;
327   }
328 };
329 
330 }  // namespace
331 
grpc_google_compute_engine_credentials_create(void * reserved)332 grpc_call_credentials* grpc_google_compute_engine_credentials_create(
333     void* reserved) {
334   GRPC_TRACE_LOG(api, INFO)
335       << "grpc_compute_engine_credentials_create(reserved=" << reserved << ")";
336   CHECK_EQ(reserved, nullptr);
337   return grpc_core::MakeRefCounted<
338              grpc_compute_engine_token_fetcher_credentials>()
339       .release();
340 }
341 
342 //
343 // Google Refresh Token credentials.
344 //
345 
grpc_google_refresh_token_credentials(grpc_auth_refresh_token refresh_token)346 grpc_google_refresh_token_credentials::grpc_google_refresh_token_credentials(
347     grpc_auth_refresh_token refresh_token)
348     : refresh_token_(refresh_token) {}
349 
350 grpc_google_refresh_token_credentials::
~grpc_google_refresh_token_credentials()351     ~grpc_google_refresh_token_credentials() {
352   grpc_auth_refresh_token_destruct(&refresh_token_);
353 }
354 
355 grpc_core::OrphanablePtr<grpc_core::HttpRequest>
StartHttpRequest(grpc_polling_entity * pollent,grpc_core::Timestamp deadline,grpc_http_response * response,grpc_closure * on_complete)356 grpc_google_refresh_token_credentials::StartHttpRequest(
357     grpc_polling_entity* pollent, grpc_core::Timestamp deadline,
358     grpc_http_response* response, grpc_closure* on_complete) {
359   grpc_http_header header = {
360       const_cast<char*>("Content-Type"),
361       const_cast<char*>("application/x-www-form-urlencoded")};
362   std::string body = absl::StrFormat(
363       GRPC_REFRESH_TOKEN_POST_BODY_FORMAT_STRING, refresh_token_.client_id,
364       refresh_token_.client_secret, refresh_token_.refresh_token);
365   grpc_http_request request;
366   memset(&request, 0, sizeof(grpc_http_request));
367   request.hdr_count = 1;
368   request.hdrs = &header;
369   request.body = const_cast<char*>(body.c_str());
370   request.body_length = body.size();
371   // TODO(ctiller): Carry the memory quota in ctx and share it with the host
372   // channel. This would allow us to cancel an authentication query when under
373   // extreme memory pressure.
374   auto uri = grpc_core::URI::Create("https", GRPC_GOOGLE_OAUTH2_SERVICE_HOST,
375                                     GRPC_GOOGLE_OAUTH2_SERVICE_TOKEN_PATH,
376                                     {} /* query params */, "" /* fragment */);
377   CHECK(uri.ok());  // params are hardcoded
378   auto http_request = grpc_core::HttpRequest::Post(
379       std::move(*uri), /*args=*/nullptr, pollent, &request, deadline,
380       on_complete, response, grpc_core::CreateHttpRequestSSLCredentials());
381   http_request->Start();
382   return http_request;
383 }
384 
385 grpc_core::RefCountedPtr<grpc_call_credentials>
grpc_refresh_token_credentials_create_from_auth_refresh_token(grpc_auth_refresh_token refresh_token)386 grpc_refresh_token_credentials_create_from_auth_refresh_token(
387     grpc_auth_refresh_token refresh_token) {
388   if (!grpc_auth_refresh_token_is_valid(&refresh_token)) {
389     LOG(ERROR) << "Invalid input for refresh token credentials creation";
390     return nullptr;
391   }
392   return grpc_core::MakeRefCounted<grpc_google_refresh_token_credentials>(
393       refresh_token);
394 }
395 
debug_string()396 std::string grpc_google_refresh_token_credentials::debug_string() {
397   return absl::StrFormat(
398       "GoogleRefreshToken{ClientID:%s,%s}", refresh_token_.client_id,
399       grpc_core::Oauth2TokenFetcherCredentials::debug_string());
400 }
401 
type() const402 grpc_core::UniqueTypeName grpc_google_refresh_token_credentials::type() const {
403   static grpc_core::UniqueTypeName::Factory kFactory("GoogleRefreshToken");
404   return kFactory.Create();
405 }
406 
create_loggable_refresh_token(grpc_auth_refresh_token * token)407 static std::string create_loggable_refresh_token(
408     grpc_auth_refresh_token* token) {
409   if (strcmp(token->type, GRPC_AUTH_JSON_TYPE_INVALID) == 0) {
410     return "<Invalid json token>";
411   }
412   return absl::StrFormat(
413       "{\n type: %s\n client_id: %s\n client_secret: "
414       "<redacted>\n refresh_token: <redacted>\n}",
415       token->type, token->client_id);
416 }
417 
grpc_google_refresh_token_credentials_create(const char * json_refresh_token,void * reserved)418 grpc_call_credentials* grpc_google_refresh_token_credentials_create(
419     const char* json_refresh_token, void* reserved) {
420   grpc_auth_refresh_token token =
421       grpc_auth_refresh_token_create_from_string(json_refresh_token);
422   GRPC_TRACE_LOG(api, INFO)
423       << "grpc_refresh_token_credentials_create(json_refresh_token="
424       << create_loggable_refresh_token(&token) << ", reserved=" << reserved
425       << ")";
426   CHECK_EQ(reserved, nullptr);
427   return grpc_refresh_token_credentials_create_from_auth_refresh_token(token)
428       .release();
429 }
430 
431 //
432 // STS credentials.
433 //
434 
435 namespace grpc_core {
436 
437 namespace {
438 
MaybeAddToBody(const char * field_name,const char * field,std::vector<std::string> * body)439 void MaybeAddToBody(const char* field_name, const char* field,
440                     std::vector<std::string>* body) {
441   if (field == nullptr || strlen(field) == 0) return;
442   body->push_back(absl::StrFormat("&%s=%s", field_name, field));
443 }
444 
LoadTokenFile(const char * path,grpc_slice * token)445 grpc_error_handle LoadTokenFile(const char* path, grpc_slice* token) {
446   auto slice = LoadFile(path, /*add_null_terminator=*/true);
447   if (!slice.ok()) return slice.status();
448   if (slice->empty()) {
449     LOG(ERROR) << "Token file " << path << " is empty";
450     return GRPC_ERROR_CREATE("Token file is empty.");
451   }
452   *token = slice->TakeCSlice();
453   return absl::OkStatus();
454 }
455 
456 class StsTokenFetcherCredentials : public Oauth2TokenFetcherCredentials {
457  public:
StsTokenFetcherCredentials(URI sts_url,const grpc_sts_credentials_options * options)458   StsTokenFetcherCredentials(URI sts_url,
459                              const grpc_sts_credentials_options* options)
460       : sts_url_(std::move(sts_url)),
461         resource_(gpr_strdup(options->resource)),
462         audience_(gpr_strdup(options->audience)),
463         scope_(gpr_strdup(options->scope)),
464         requested_token_type_(gpr_strdup(options->requested_token_type)),
465         subject_token_path_(gpr_strdup(options->subject_token_path)),
466         subject_token_type_(gpr_strdup(options->subject_token_type)),
467         actor_token_path_(gpr_strdup(options->actor_token_path)),
468         actor_token_type_(gpr_strdup(options->actor_token_type)) {}
469 
debug_string()470   std::string debug_string() override {
471     return absl::StrFormat(
472         "StsTokenFetcherCredentials{Path:%s,Authority:%s,%s}", sts_url_.path(),
473         sts_url_.authority(), Oauth2TokenFetcherCredentials::debug_string());
474   }
475 
476  private:
StartHttpRequest(grpc_polling_entity * pollent,Timestamp deadline,grpc_http_response * response,grpc_closure * on_complete)477   OrphanablePtr<HttpRequest> StartHttpRequest(
478       grpc_polling_entity* pollent, Timestamp deadline,
479       grpc_http_response* response, grpc_closure* on_complete) override {
480     grpc_http_request request;
481     memset(&request, 0, sizeof(grpc_http_request));
482     grpc_error_handle err = FillBody(&request.body, &request.body_length);
483     if (!err.ok()) {
484       ExecCtx::Run(DEBUG_LOCATION, on_complete, std::move(err));
485       return nullptr;
486     }
487     grpc_http_header header = {
488         const_cast<char*>("Content-Type"),
489         const_cast<char*>("application/x-www-form-urlencoded")};
490     request.hdr_count = 1;
491     request.hdrs = &header;
492     // TODO(ctiller): Carry the memory quota in ctx and share it with the host
493     // channel. This would allow us to cancel an authentication query when under
494     // extreme memory pressure.
495     RefCountedPtr<grpc_channel_credentials> http_request_creds;
496     if (sts_url_.scheme() == "http") {
497       http_request_creds = RefCountedPtr<grpc_channel_credentials>(
498           grpc_insecure_credentials_create());
499     } else {
500       http_request_creds = CreateHttpRequestSSLCredentials();
501     }
502     auto http_request = HttpRequest::Post(
503         sts_url_, /*args=*/nullptr, pollent, &request, deadline, on_complete,
504         response, std::move(http_request_creds));
505     http_request->Start();
506     gpr_free(request.body);
507     return http_request;
508   }
509 
FillBody(char ** body,size_t * body_length)510   grpc_error_handle FillBody(char** body, size_t* body_length) {
511     *body = nullptr;
512     std::vector<std::string> body_parts;
513     grpc_slice subject_token = grpc_empty_slice();
514     grpc_slice actor_token = grpc_empty_slice();
515     grpc_error_handle err;
516 
517     auto cleanup = [&body, &body_length, &body_parts, &subject_token,
518                     &actor_token, &err]() {
519       if (err.ok()) {
520         std::string body_str = absl::StrJoin(body_parts, "");
521         *body = gpr_strdup(body_str.c_str());
522         *body_length = body_str.size();
523       }
524       CSliceUnref(subject_token);
525       CSliceUnref(actor_token);
526       return err;
527     };
528 
529     err = LoadTokenFile(subject_token_path_.get(), &subject_token);
530     if (!err.ok()) return cleanup();
531     body_parts.push_back(absl::StrFormat(
532         GRPC_STS_POST_MINIMAL_BODY_FORMAT_STRING,
533         reinterpret_cast<const char*>(GRPC_SLICE_START_PTR(subject_token)),
534         subject_token_type_.get()));
535     MaybeAddToBody("resource", resource_.get(), &body_parts);
536     MaybeAddToBody("audience", audience_.get(), &body_parts);
537     MaybeAddToBody("scope", scope_.get(), &body_parts);
538     MaybeAddToBody("requested_token_type", requested_token_type_.get(),
539                    &body_parts);
540     if ((actor_token_path_ != nullptr) && *actor_token_path_ != '\0') {
541       err = LoadTokenFile(actor_token_path_.get(), &actor_token);
542       if (!err.ok()) return cleanup();
543       MaybeAddToBody(
544           "actor_token",
545           reinterpret_cast<const char*>(GRPC_SLICE_START_PTR(actor_token)),
546           &body_parts);
547       MaybeAddToBody("actor_token_type", actor_token_type_.get(), &body_parts);
548     }
549     return cleanup();
550   }
551 
552   URI sts_url_;
553   UniquePtr<char> resource_;
554   UniquePtr<char> audience_;
555   UniquePtr<char> scope_;
556   UniquePtr<char> requested_token_type_;
557   UniquePtr<char> subject_token_path_;
558   UniquePtr<char> subject_token_type_;
559   UniquePtr<char> actor_token_path_;
560   UniquePtr<char> actor_token_type_;
561   OrphanablePtr<HttpRequest> http_request_;
562 };
563 
564 }  // namespace
565 
ValidateStsCredentialsOptions(const grpc_sts_credentials_options * options)566 absl::StatusOr<URI> ValidateStsCredentialsOptions(
567     const grpc_sts_credentials_options* options) {
568   std::vector<grpc_error_handle> error_list;
569   absl::StatusOr<URI> sts_url =
570       URI::Parse(options->token_exchange_service_uri == nullptr
571                      ? ""
572                      : options->token_exchange_service_uri);
573   if (!sts_url.ok()) {
574     error_list.push_back(GRPC_ERROR_CREATE(
575         absl::StrFormat("Invalid or missing STS endpoint URL. Error: %s",
576                         sts_url.status().ToString())));
577   } else if (sts_url->scheme() != "https" && sts_url->scheme() != "http") {
578     error_list.push_back(
579         GRPC_ERROR_CREATE("Invalid URI scheme, must be https to http."));
580   }
581   if (options->subject_token_path == nullptr ||
582       strlen(options->subject_token_path) == 0) {
583     error_list.push_back(
584         GRPC_ERROR_CREATE("subject_token needs to be specified"));
585   }
586   if (options->subject_token_type == nullptr ||
587       strlen(options->subject_token_type) == 0) {
588     error_list.push_back(
589         GRPC_ERROR_CREATE("subject_token_type needs to be specified"));
590   }
591   if (error_list.empty()) {
592     return sts_url;
593   }
594   auto grpc_error_vec = GRPC_ERROR_CREATE_FROM_VECTOR(
595       "Invalid STS Credentials Options", &error_list);
596   auto retval = absl::InvalidArgumentError(StatusToString(grpc_error_vec));
597   return retval;
598 }
599 
600 }  // namespace grpc_core
601 
grpc_sts_credentials_create(const grpc_sts_credentials_options * options,void * reserved)602 grpc_call_credentials* grpc_sts_credentials_create(
603     const grpc_sts_credentials_options* options, void* reserved) {
604   CHECK_EQ(reserved, nullptr);
605   absl::StatusOr<grpc_core::URI> sts_url =
606       grpc_core::ValidateStsCredentialsOptions(options);
607   if (!sts_url.ok()) {
608     LOG(ERROR) << "STS Credentials creation failed. Error: "
609                << sts_url.status();
610     return nullptr;
611   }
612   return grpc_core::MakeRefCounted<grpc_core::StsTokenFetcherCredentials>(
613              std::move(*sts_url), options)
614       .release();
615 }
616 
617 //
618 // Oauth2 Access Token credentials.
619 //
620 
621 grpc_core::ArenaPromise<absl::StatusOr<grpc_core::ClientMetadataHandle>>
GetRequestMetadata(grpc_core::ClientMetadataHandle initial_metadata,const grpc_call_credentials::GetRequestMetadataArgs *)622 grpc_access_token_credentials::GetRequestMetadata(
623     grpc_core::ClientMetadataHandle initial_metadata,
624     const grpc_call_credentials::GetRequestMetadataArgs*) {
625   initial_metadata->Append(
626       GRPC_AUTHORIZATION_METADATA_KEY, access_token_value_.Ref(),
627       [](absl::string_view, const grpc_core::Slice&) { abort(); });
628   return grpc_core::Immediate(std::move(initial_metadata));
629 }
630 
Type()631 grpc_core::UniqueTypeName grpc_access_token_credentials::Type() {
632   static grpc_core::UniqueTypeName::Factory kFactory("AccessToken");
633   return kFactory.Create();
634 }
635 
grpc_access_token_credentials(const char * access_token)636 grpc_access_token_credentials::grpc_access_token_credentials(
637     const char* access_token)
638     : access_token_value_(grpc_core::Slice::FromCopiedString(
639           absl::StrCat("Bearer ", access_token))) {}
640 
debug_string()641 std::string grpc_access_token_credentials::debug_string() {
642   return "AccessTokenCredentials{Token:present}";
643 }
644 
grpc_access_token_credentials_create(const char * access_token,void * reserved)645 grpc_call_credentials* grpc_access_token_credentials_create(
646     const char* access_token, void* reserved) {
647   GRPC_TRACE_LOG(api, INFO) << "grpc_access_token_credentials_create(access_"
648                                "token=<redacted>, reserved="
649                             << reserved << ")";
650   CHECK_EQ(reserved, nullptr);
651   return grpc_core::MakeRefCounted<grpc_access_token_credentials>(access_token)
652       .release();
653 }
654