/* * * Copyright 2015 gRPC authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ #include #include "src/core/lib/json/json.h" #include "src/core/lib/security/credentials/oauth2/oauth2_credentials.h" #include #include "absl/container/inlined_vector.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" #include "absl/strings/str_join.h" #include #include #include #include #include #include #include "src/core/lib/gpr/string.h" #include "src/core/lib/gprpp/ref_counted_ptr.h" #include "src/core/lib/iomgr/error.h" #include "src/core/lib/iomgr/load_file.h" #include "src/core/lib/security/util/json_util.h" #include "src/core/lib/slice/slice_internal.h" #include "src/core/lib/surface/api_trace.h" #include "src/core/lib/uri/uri_parser.h" using grpc_core::Json; // // Auth Refresh Token. // int grpc_auth_refresh_token_is_valid( const grpc_auth_refresh_token* refresh_token) { return (refresh_token != nullptr) && strcmp(refresh_token->type, GRPC_AUTH_JSON_TYPE_INVALID) != 0; } grpc_auth_refresh_token grpc_auth_refresh_token_create_from_json( const Json& json) { grpc_auth_refresh_token result; const char* prop_value; int success = 0; grpc_error* error = GRPC_ERROR_NONE; memset(&result, 0, sizeof(grpc_auth_refresh_token)); result.type = GRPC_AUTH_JSON_TYPE_INVALID; if (json.type() != Json::Type::OBJECT) { gpr_log(GPR_ERROR, "Invalid json."); goto end; } prop_value = grpc_json_get_string_property(json, "type", &error); GRPC_LOG_IF_ERROR("Parsing refresh token", error); if (prop_value == nullptr || strcmp(prop_value, GRPC_AUTH_JSON_TYPE_AUTHORIZED_USER) != 0) { goto end; } result.type = GRPC_AUTH_JSON_TYPE_AUTHORIZED_USER; if (!grpc_copy_json_string_property(json, "client_secret", &result.client_secret) || !grpc_copy_json_string_property(json, "client_id", &result.client_id) || !grpc_copy_json_string_property(json, "refresh_token", &result.refresh_token)) { goto end; } success = 1; end: if (!success) grpc_auth_refresh_token_destruct(&result); return result; } grpc_auth_refresh_token grpc_auth_refresh_token_create_from_string( const char* json_string) { grpc_error* error = GRPC_ERROR_NONE; Json json = Json::Parse(json_string, &error); if (error != GRPC_ERROR_NONE) { gpr_log(GPR_ERROR, "JSON parsing failed: %s", grpc_error_string(error)); GRPC_ERROR_UNREF(error); } return grpc_auth_refresh_token_create_from_json(json); } void grpc_auth_refresh_token_destruct(grpc_auth_refresh_token* refresh_token) { if (refresh_token == nullptr) return; refresh_token->type = GRPC_AUTH_JSON_TYPE_INVALID; if (refresh_token->client_id != nullptr) { gpr_free(refresh_token->client_id); refresh_token->client_id = nullptr; } if (refresh_token->client_secret != nullptr) { gpr_free(refresh_token->client_secret); refresh_token->client_secret = nullptr; } if (refresh_token->refresh_token != nullptr) { gpr_free(refresh_token->refresh_token); refresh_token->refresh_token = nullptr; } } // // Oauth2 Token Fetcher credentials. // grpc_oauth2_token_fetcher_credentials:: ~grpc_oauth2_token_fetcher_credentials() { GRPC_MDELEM_UNREF(access_token_md_); gpr_mu_destroy(&mu_); grpc_pollset_set_destroy(grpc_polling_entity_pollset_set(&pollent_)); grpc_httpcli_context_destroy(&httpcli_context_); } grpc_credentials_status grpc_oauth2_token_fetcher_credentials_parse_server_response( const grpc_http_response* response, grpc_mdelem* token_md, grpc_millis* token_lifetime) { char* null_terminated_body = nullptr; grpc_credentials_status status = GRPC_CREDENTIALS_OK; Json json; if (response == nullptr) { gpr_log(GPR_ERROR, "Received NULL response."); status = GRPC_CREDENTIALS_ERROR; goto end; } if (response->body_length > 0) { null_terminated_body = static_cast(gpr_malloc(response->body_length + 1)); null_terminated_body[response->body_length] = '\0'; memcpy(null_terminated_body, response->body, response->body_length); } if (response->status != 200) { gpr_log(GPR_ERROR, "Call to http server ended with error %d [%s].", response->status, null_terminated_body != nullptr ? null_terminated_body : ""); status = GRPC_CREDENTIALS_ERROR; goto end; } else { const char* access_token = nullptr; const char* token_type = nullptr; const char* expires_in = nullptr; Json::Object::const_iterator it; grpc_error* error = GRPC_ERROR_NONE; json = Json::Parse(null_terminated_body, &error); if (error != GRPC_ERROR_NONE) { gpr_log(GPR_ERROR, "Could not parse JSON from %s: %s", null_terminated_body, grpc_error_string(error)); GRPC_ERROR_UNREF(error); status = GRPC_CREDENTIALS_ERROR; goto end; } if (json.type() != Json::Type::OBJECT) { gpr_log(GPR_ERROR, "Response should be a JSON object"); status = GRPC_CREDENTIALS_ERROR; goto end; } it = json.object_value().find("access_token"); if (it == json.object_value().end() || it->second.type() != Json::Type::STRING) { gpr_log(GPR_ERROR, "Missing or invalid access_token in JSON."); status = GRPC_CREDENTIALS_ERROR; goto end; } access_token = it->second.string_value().c_str(); it = json.object_value().find("token_type"); if (it == json.object_value().end() || it->second.type() != Json::Type::STRING) { gpr_log(GPR_ERROR, "Missing or invalid token_type in JSON."); status = GRPC_CREDENTIALS_ERROR; goto end; } token_type = it->second.string_value().c_str(); it = json.object_value().find("expires_in"); if (it == json.object_value().end() || it->second.type() != Json::Type::NUMBER) { gpr_log(GPR_ERROR, "Missing or invalid expires_in in JSON."); status = GRPC_CREDENTIALS_ERROR; goto end; } expires_in = it->second.string_value().c_str(); *token_lifetime = strtol(expires_in, nullptr, 10) * GPR_MS_PER_SEC; if (!GRPC_MDISNULL(*token_md)) GRPC_MDELEM_UNREF(*token_md); *token_md = grpc_mdelem_from_slices( grpc_core::ExternallyManagedSlice(GRPC_AUTHORIZATION_METADATA_KEY), grpc_slice_from_cpp_string( absl::StrCat(token_type, " ", access_token))); status = GRPC_CREDENTIALS_OK; } end: if (status != GRPC_CREDENTIALS_OK && !GRPC_MDISNULL(*token_md)) { GRPC_MDELEM_UNREF(*token_md); *token_md = GRPC_MDNULL; } gpr_free(null_terminated_body); return status; } static void on_oauth2_token_fetcher_http_response(void* user_data, grpc_error* error) { GRPC_LOG_IF_ERROR("oauth_fetch", GRPC_ERROR_REF(error)); grpc_credentials_metadata_request* r = static_cast(user_data); grpc_oauth2_token_fetcher_credentials* c = reinterpret_cast(r->creds.get()); c->on_http_response(r, error); } void grpc_oauth2_token_fetcher_credentials::on_http_response( grpc_credentials_metadata_request* r, grpc_error* error) { grpc_mdelem access_token_md = GRPC_MDNULL; grpc_millis token_lifetime = 0; grpc_credentials_status status = error == GRPC_ERROR_NONE ? grpc_oauth2_token_fetcher_credentials_parse_server_response( &r->response, &access_token_md, &token_lifetime) : GRPC_CREDENTIALS_ERROR; // Update cache and grab list of pending requests. gpr_mu_lock(&mu_); token_fetch_pending_ = false; access_token_md_ = GRPC_MDELEM_REF(access_token_md); token_expiration_ = status == GRPC_CREDENTIALS_OK ? gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), gpr_time_from_millis(token_lifetime, GPR_TIMESPAN)) : gpr_inf_past(GPR_CLOCK_MONOTONIC); grpc_oauth2_pending_get_request_metadata* pending_request = pending_requests_; pending_requests_ = nullptr; gpr_mu_unlock(&mu_); // Invoke callbacks for all pending requests. while (pending_request != nullptr) { grpc_error* new_error = GRPC_ERROR_NONE; if (status == GRPC_CREDENTIALS_OK) { grpc_credentials_mdelem_array_add(pending_request->md_array, access_token_md); } else { new_error = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( "Error occurred when fetching oauth2 token.", &error, 1); } grpc_core::ExecCtx::Run(DEBUG_LOCATION, pending_request->on_request_metadata, new_error); grpc_polling_entity_del_from_pollset_set( pending_request->pollent, grpc_polling_entity_pollset_set(&pollent_)); grpc_oauth2_pending_get_request_metadata* prev = pending_request; pending_request = pending_request->next; gpr_free(prev); } GRPC_MDELEM_UNREF(access_token_md); Unref(); grpc_credentials_metadata_request_destroy(r); } bool grpc_oauth2_token_fetcher_credentials::get_request_metadata( grpc_polling_entity* pollent, grpc_auth_metadata_context /*context*/, grpc_credentials_mdelem_array* md_array, grpc_closure* on_request_metadata, grpc_error** /*error*/) { // Check if we can use the cached token. grpc_millis refresh_threshold = GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS * GPR_MS_PER_SEC; grpc_mdelem cached_access_token_md = GRPC_MDNULL; gpr_mu_lock(&mu_); if (!GRPC_MDISNULL(access_token_md_) && gpr_time_cmp( gpr_time_sub(token_expiration_, gpr_now(GPR_CLOCK_MONOTONIC)), gpr_time_from_seconds(GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS, GPR_TIMESPAN)) > 0) { cached_access_token_md = GRPC_MDELEM_REF(access_token_md_); } if (!GRPC_MDISNULL(cached_access_token_md)) { gpr_mu_unlock(&mu_); grpc_credentials_mdelem_array_add(md_array, cached_access_token_md); GRPC_MDELEM_UNREF(cached_access_token_md); return true; } // Couldn't get the token from the cache. // Add request to pending_requests_ and start a new fetch if needed. grpc_oauth2_pending_get_request_metadata* pending_request = static_cast( gpr_malloc(sizeof(*pending_request))); pending_request->md_array = md_array; pending_request->on_request_metadata = on_request_metadata; pending_request->pollent = pollent; grpc_polling_entity_add_to_pollset_set( pollent, grpc_polling_entity_pollset_set(&pollent_)); pending_request->next = pending_requests_; pending_requests_ = pending_request; bool start_fetch = false; if (!token_fetch_pending_) { token_fetch_pending_ = true; start_fetch = true; } gpr_mu_unlock(&mu_); if (start_fetch) { Ref().release(); fetch_oauth2(grpc_credentials_metadata_request_create(this->Ref()), &httpcli_context_, &pollent_, on_oauth2_token_fetcher_http_response, grpc_core::ExecCtx::Get()->Now() + refresh_threshold); } return false; } void grpc_oauth2_token_fetcher_credentials::cancel_get_request_metadata( grpc_credentials_mdelem_array* md_array, grpc_error* error) { gpr_mu_lock(&mu_); grpc_oauth2_pending_get_request_metadata* prev = nullptr; grpc_oauth2_pending_get_request_metadata* pending_request = pending_requests_; while (pending_request != nullptr) { if (pending_request->md_array == md_array) { // Remove matching pending request from the list. if (prev != nullptr) { prev->next = pending_request->next; } else { pending_requests_ = pending_request->next; } // Invoke the callback immediately with an error. grpc_core::ExecCtx::Run(DEBUG_LOCATION, pending_request->on_request_metadata, GRPC_ERROR_REF(error)); gpr_free(pending_request); break; } prev = pending_request; pending_request = pending_request->next; } gpr_mu_unlock(&mu_); GRPC_ERROR_UNREF(error); } grpc_oauth2_token_fetcher_credentials::grpc_oauth2_token_fetcher_credentials() : grpc_call_credentials(GRPC_CALL_CREDENTIALS_TYPE_OAUTH2), token_expiration_(gpr_inf_past(GPR_CLOCK_MONOTONIC)), pollent_(grpc_polling_entity_create_from_pollset_set( grpc_pollset_set_create())) { gpr_mu_init(&mu_); grpc_httpcli_context_init(&httpcli_context_); } std::string grpc_oauth2_token_fetcher_credentials::debug_string() { return "OAuth2TokenFetcherCredentials"; } // // Google Compute Engine credentials. // namespace { class grpc_compute_engine_token_fetcher_credentials : public grpc_oauth2_token_fetcher_credentials { public: grpc_compute_engine_token_fetcher_credentials() = default; ~grpc_compute_engine_token_fetcher_credentials() override = default; protected: void fetch_oauth2(grpc_credentials_metadata_request* metadata_req, grpc_httpcli_context* http_context, grpc_polling_entity* pollent, grpc_iomgr_cb_func response_cb, grpc_millis deadline) override { grpc_http_header header = {const_cast("Metadata-Flavor"), const_cast("Google")}; grpc_httpcli_request request; memset(&request, 0, sizeof(grpc_httpcli_request)); request.host = (char*)GRPC_COMPUTE_ENGINE_METADATA_HOST; request.http.path = (char*)GRPC_COMPUTE_ENGINE_METADATA_TOKEN_PATH; request.http.hdr_count = 1; request.http.hdrs = &header; /* TODO(ctiller): Carry the resource_quota in ctx and share it with the host channel. This would allow us to cancel an authentication query when under extreme memory pressure. */ grpc_resource_quota* resource_quota = grpc_resource_quota_create("oauth2_credentials"); grpc_httpcli_get(http_context, pollent, resource_quota, &request, deadline, GRPC_CLOSURE_INIT(&http_get_cb_closure_, response_cb, metadata_req, grpc_schedule_on_exec_ctx), &metadata_req->response); grpc_resource_quota_unref_internal(resource_quota); } std::string debug_string() override { return absl::StrFormat( "GoogleComputeEngineTokenFetcherCredentials{%s}", grpc_oauth2_token_fetcher_credentials::debug_string()); } private: grpc_closure http_get_cb_closure_; }; } // namespace grpc_call_credentials* grpc_google_compute_engine_credentials_create( void* reserved) { GRPC_API_TRACE("grpc_compute_engine_credentials_create(reserved=%p)", 1, (reserved)); GPR_ASSERT(reserved == nullptr); return grpc_core::MakeRefCounted< grpc_compute_engine_token_fetcher_credentials>() .release(); } // // Google Refresh Token credentials. // grpc_google_refresh_token_credentials:: ~grpc_google_refresh_token_credentials() { grpc_auth_refresh_token_destruct(&refresh_token_); } void grpc_google_refresh_token_credentials::fetch_oauth2( grpc_credentials_metadata_request* metadata_req, grpc_httpcli_context* httpcli_context, grpc_polling_entity* pollent, grpc_iomgr_cb_func response_cb, grpc_millis deadline) { grpc_http_header header = { const_cast("Content-Type"), const_cast("application/x-www-form-urlencoded")}; grpc_httpcli_request request; std::string body = absl::StrFormat( GRPC_REFRESH_TOKEN_POST_BODY_FORMAT_STRING, refresh_token_.client_id, refresh_token_.client_secret, refresh_token_.refresh_token); memset(&request, 0, sizeof(grpc_httpcli_request)); request.host = (char*)GRPC_GOOGLE_OAUTH2_SERVICE_HOST; request.http.path = (char*)GRPC_GOOGLE_OAUTH2_SERVICE_TOKEN_PATH; request.http.hdr_count = 1; request.http.hdrs = &header; request.handshaker = &grpc_httpcli_ssl; /* TODO(ctiller): Carry the resource_quota in ctx and share it with the host channel. This would allow us to cancel an authentication query when under extreme memory pressure. */ grpc_resource_quota* resource_quota = grpc_resource_quota_create("oauth2_credentials_refresh"); grpc_httpcli_post(httpcli_context, pollent, resource_quota, &request, body.c_str(), body.size(), deadline, GRPC_CLOSURE_INIT(&http_post_cb_closure_, response_cb, metadata_req, grpc_schedule_on_exec_ctx), &metadata_req->response); grpc_resource_quota_unref_internal(resource_quota); } grpc_google_refresh_token_credentials::grpc_google_refresh_token_credentials( grpc_auth_refresh_token refresh_token) : refresh_token_(refresh_token) {} grpc_core::RefCountedPtr grpc_refresh_token_credentials_create_from_auth_refresh_token( grpc_auth_refresh_token refresh_token) { if (!grpc_auth_refresh_token_is_valid(&refresh_token)) { gpr_log(GPR_ERROR, "Invalid input for refresh token credentials creation"); return nullptr; } return grpc_core::MakeRefCounted( refresh_token); } std::string grpc_google_refresh_token_credentials::debug_string() { return absl::StrFormat("GoogleRefreshToken{ClientID:%s,%s}", refresh_token_.client_id, grpc_oauth2_token_fetcher_credentials::debug_string()); } static std::string create_loggable_refresh_token( grpc_auth_refresh_token* token) { if (strcmp(token->type, GRPC_AUTH_JSON_TYPE_INVALID) == 0) { return ""; } return absl::StrFormat( "{\n type: %s\n client_id: %s\n client_secret: " "\n refresh_token: \n}", token->type, token->client_id); } grpc_call_credentials* grpc_google_refresh_token_credentials_create( const char* json_refresh_token, void* reserved) { grpc_auth_refresh_token token = grpc_auth_refresh_token_create_from_string(json_refresh_token); if (GRPC_TRACE_FLAG_ENABLED(grpc_api_trace)) { gpr_log(GPR_INFO, "grpc_refresh_token_credentials_create(json_refresh_token=%s, " "reserved=%p)", create_loggable_refresh_token(&token).c_str(), reserved); } GPR_ASSERT(reserved == nullptr); return grpc_refresh_token_credentials_create_from_auth_refresh_token(token) .release(); } // // STS credentials. // namespace grpc_core { namespace { void MaybeAddToBody(const char* field_name, const char* field, std::vector* body) { if (field == nullptr || strlen(field) == 0) return; body->push_back(absl::StrFormat("&%s=%s", field_name, field)); } grpc_error* LoadTokenFile(const char* path, gpr_slice* token) { grpc_error* err = grpc_load_file(path, 1, token); if (err != GRPC_ERROR_NONE) return err; if (GRPC_SLICE_LENGTH(*token) == 0) { gpr_log(GPR_ERROR, "Token file %s is empty", path); err = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Token file is empty."); } return err; } class StsTokenFetcherCredentials : public grpc_oauth2_token_fetcher_credentials { public: StsTokenFetcherCredentials(grpc_uri* sts_url, // Ownership transferred. const grpc_sts_credentials_options* options) : sts_url_(sts_url), resource_(gpr_strdup(options->resource)), audience_(gpr_strdup(options->audience)), scope_(gpr_strdup(options->scope)), requested_token_type_(gpr_strdup(options->requested_token_type)), subject_token_path_(gpr_strdup(options->subject_token_path)), subject_token_type_(gpr_strdup(options->subject_token_type)), actor_token_path_(gpr_strdup(options->actor_token_path)), actor_token_type_(gpr_strdup(options->actor_token_type)) {} ~StsTokenFetcherCredentials() override { grpc_uri_destroy(sts_url_); } std::string debug_string() override { return absl::StrFormat( "StsTokenFetcherCredentials{Path:%s,Authority:%s,%s}", sts_url_->path, sts_url_->authority, grpc_oauth2_token_fetcher_credentials::debug_string()); } private: void fetch_oauth2(grpc_credentials_metadata_request* metadata_req, grpc_httpcli_context* http_context, grpc_polling_entity* pollent, grpc_iomgr_cb_func response_cb, grpc_millis deadline) override { char* body = nullptr; size_t body_length = 0; grpc_error* err = FillBody(&body, &body_length); if (err != GRPC_ERROR_NONE) { response_cb(metadata_req, err); GRPC_ERROR_UNREF(err); return; } grpc_http_header header = { const_cast("Content-Type"), const_cast("application/x-www-form-urlencoded")}; grpc_httpcli_request request; memset(&request, 0, sizeof(grpc_httpcli_request)); request.host = (char*)sts_url_->authority; request.http.path = (char*)sts_url_->path; request.http.hdr_count = 1; request.http.hdrs = &header; request.handshaker = (strcmp(sts_url_->scheme, "https") == 0) ? &grpc_httpcli_ssl : &grpc_httpcli_plaintext; /* TODO(ctiller): Carry the resource_quota in ctx and share it with the host channel. This would allow us to cancel an authentication query when under extreme memory pressure. */ grpc_resource_quota* resource_quota = grpc_resource_quota_create("oauth2_credentials_refresh"); grpc_httpcli_post( http_context, pollent, resource_quota, &request, body, body_length, deadline, GRPC_CLOSURE_INIT(&http_post_cb_closure_, response_cb, metadata_req, grpc_schedule_on_exec_ctx), &metadata_req->response); grpc_resource_quota_unref_internal(resource_quota); gpr_free(body); } grpc_error* FillBody(char** body, size_t* body_length) { *body = nullptr; std::vector body_parts; grpc_slice subject_token = grpc_empty_slice(); grpc_slice actor_token = grpc_empty_slice(); grpc_error* err = GRPC_ERROR_NONE; auto cleanup = [&body, &body_length, &body_parts, &subject_token, &actor_token, &err]() { if (err == GRPC_ERROR_NONE) { std::string body_str = absl::StrJoin(body_parts, ""); *body = gpr_strdup(body_str.c_str()); *body_length = body_str.size(); } grpc_slice_unref_internal(subject_token); grpc_slice_unref_internal(actor_token); return err; }; err = LoadTokenFile(subject_token_path_.get(), &subject_token); if (err != GRPC_ERROR_NONE) return cleanup(); body_parts.push_back(absl::StrFormat( GRPC_STS_POST_MINIMAL_BODY_FORMAT_STRING, reinterpret_cast(GRPC_SLICE_START_PTR(subject_token)), subject_token_type_.get())); MaybeAddToBody("resource", resource_.get(), &body_parts); MaybeAddToBody("audience", audience_.get(), &body_parts); MaybeAddToBody("scope", scope_.get(), &body_parts); MaybeAddToBody("requested_token_type", requested_token_type_.get(), &body_parts); if ((actor_token_path_ != nullptr) && *actor_token_path_ != '\0') { err = LoadTokenFile(actor_token_path_.get(), &actor_token); if (err != GRPC_ERROR_NONE) return cleanup(); MaybeAddToBody( "actor_token", reinterpret_cast(GRPC_SLICE_START_PTR(actor_token)), &body_parts); MaybeAddToBody("actor_token_type", actor_token_type_.get(), &body_parts); } return cleanup(); } grpc_uri* sts_url_; grpc_closure http_post_cb_closure_; grpc_core::UniquePtr resource_; grpc_core::UniquePtr audience_; grpc_core::UniquePtr scope_; grpc_core::UniquePtr requested_token_type_; grpc_core::UniquePtr subject_token_path_; grpc_core::UniquePtr subject_token_type_; grpc_core::UniquePtr actor_token_path_; grpc_core::UniquePtr actor_token_type_; }; } // namespace grpc_error* ValidateStsCredentialsOptions( const grpc_sts_credentials_options* options, grpc_uri** sts_url_out) { struct GrpcUriDeleter { void operator()(grpc_uri* uri) { grpc_uri_destroy(uri); } }; *sts_url_out = nullptr; absl::InlinedVector error_list; std::unique_ptr sts_url( options->token_exchange_service_uri != nullptr ? grpc_uri_parse(options->token_exchange_service_uri, false) : nullptr); if (sts_url == nullptr) { error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( "Invalid or missing STS endpoint URL")); } else { if (strcmp(sts_url->scheme, "https") != 0 && strcmp(sts_url->scheme, "http") != 0) { error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( "Invalid URI scheme, must be https to http.")); } } if (options->subject_token_path == nullptr || strlen(options->subject_token_path) == 0) { error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( "subject_token needs to be specified")); } if (options->subject_token_type == nullptr || strlen(options->subject_token_type) == 0) { error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( "subject_token_type needs to be specified")); } if (error_list.empty()) { *sts_url_out = sts_url.release(); return GRPC_ERROR_NONE; } else { return GRPC_ERROR_CREATE_FROM_VECTOR("Invalid STS Credentials Options", &error_list); } } } // namespace grpc_core grpc_call_credentials* grpc_sts_credentials_create( const grpc_sts_credentials_options* options, void* reserved) { GPR_ASSERT(reserved == nullptr); grpc_uri* sts_url; grpc_error* error = grpc_core::ValidateStsCredentialsOptions(options, &sts_url); if (error != GRPC_ERROR_NONE) { gpr_log(GPR_ERROR, "STS Credentials creation failed. Error: %s.", grpc_error_string(error)); GRPC_ERROR_UNREF(error); return nullptr; } return grpc_core::MakeRefCounted( sts_url, options) .release(); } // // Oauth2 Access Token credentials. // grpc_access_token_credentials::~grpc_access_token_credentials() { GRPC_MDELEM_UNREF(access_token_md_); } bool grpc_access_token_credentials::get_request_metadata( grpc_polling_entity* /*pollent*/, grpc_auth_metadata_context /*context*/, grpc_credentials_mdelem_array* md_array, grpc_closure* /*on_request_metadata*/, grpc_error** /*error*/) { grpc_credentials_mdelem_array_add(md_array, access_token_md_); return true; } void grpc_access_token_credentials::cancel_get_request_metadata( grpc_credentials_mdelem_array* /*md_array*/, grpc_error* error) { GRPC_ERROR_UNREF(error); } grpc_access_token_credentials::grpc_access_token_credentials( const char* access_token) : grpc_call_credentials(GRPC_CALL_CREDENTIALS_TYPE_OAUTH2) { grpc_core::ExecCtx exec_ctx; access_token_md_ = grpc_mdelem_from_slices( grpc_core::ExternallyManagedSlice(GRPC_AUTHORIZATION_METADATA_KEY), grpc_slice_from_cpp_string(absl::StrCat("Bearer ", access_token))); } std::string grpc_access_token_credentials::debug_string() { bool access_token_present = !GRPC_MDISNULL(access_token_md_); return absl::StrFormat("AccessTokenCredentials{Token:%s}", access_token_present ? "present" : "absent"); } grpc_call_credentials* grpc_access_token_credentials_create( const char* access_token, void* reserved) { GRPC_API_TRACE( "grpc_access_token_credentials_create(access_token=, " "reserved=%p)", 1, (reserved)); GPR_ASSERT(reserved == nullptr); return grpc_core::MakeRefCounted(access_token) .release(); }