blob: 4c17745ebb8648db28804b1d2c31d2638d9b9310 [file] [log] [blame]
/*
*
* 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 <grpc/support/port_platform.h>
#include "src/core/lib/security/credentials/oauth2/oauth2_credentials.h"
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <atomic>
#include <map>
#include <memory>
#include <vector>
#include "absl/status/status.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/strings/str_join.h"
#include "absl/strings/string_view.h"
#include <grpc/grpc.h>
#include <grpc/grpc_security.h>
#include <grpc/impl/codegen/gpr_slice.h>
#include <grpc/slice.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/string_util.h>
#include <grpc/support/time.h>
#include "src/core/lib/debug/trace.h"
#include "src/core/lib/gprpp/memory.h"
#include "src/core/lib/gprpp/ref_counted_ptr.h"
#include "src/core/lib/http/httpcli_ssl_credentials.h"
#include "src/core/lib/iomgr/error.h"
#include "src/core/lib/iomgr/exec_ctx.h"
#include "src/core/lib/iomgr/load_file.h"
#include "src/core/lib/iomgr/pollset_set.h"
#include "src/core/lib/json/json.h"
#include "src/core/lib/promise/context.h"
#include "src/core/lib/promise/poll.h"
#include "src/core/lib/promise/promise.h"
#include "src/core/lib/security/util/json_util.h"
#include "src/core/lib/slice/slice_refcount.h"
#include "src/core/lib/surface/api_trace.h"
#include "src/core/lib/transport/error_utils.h"
#include "src/core/lib/transport/metadata_batch.h"
#include "src/core/lib/transport/transport.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_handle 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) {
Json json;
auto json_or = Json::Parse(json_string);
if (!json_or.ok()) {
gpr_log(GPR_ERROR, "JSON parsing failed: %s",
json_or.status().ToString().c_str());
} else {
json = std::move(*json_or);
}
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() {
gpr_mu_destroy(&mu_);
grpc_pollset_set_destroy(grpc_polling_entity_pollset_set(&pollent_));
}
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) {
char* null_terminated_body = nullptr;
grpc_credentials_status status = GRPC_CREDENTIALS_OK;
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<char*>(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;
auto json = Json::Parse(
null_terminated_body != nullptr ? null_terminated_body : "");
if (!json.ok()) {
gpr_log(GPR_ERROR, "Could not parse JSON from %s: %s",
null_terminated_body, json.status().ToString().c_str());
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 =
grpc_core::Duration::Seconds(strtol(expires_in, nullptr, 10));
*token_value = grpc_core::Slice::FromCopiedString(
absl::StrCat(token_type, " ", access_token));
status = GRPC_CREDENTIALS_OK;
}
end:
if (status != GRPC_CREDENTIALS_OK) *token_value = absl::nullopt;
gpr_free(null_terminated_body);
return status;
}
static void on_oauth2_token_fetcher_http_response(void* user_data,
grpc_error_handle error) {
GRPC_LOG_IF_ERROR("oauth_fetch", GRPC_ERROR_REF(error));
grpc_credentials_metadata_request* r =
static_cast<grpc_credentials_metadata_request*>(user_data);
grpc_oauth2_token_fetcher_credentials* c =
reinterpret_cast<grpc_oauth2_token_fetcher_credentials*>(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_handle error) {
absl::optional<grpc_core::Slice> access_token_value;
grpc_core::Duration token_lifetime;
grpc_credentials_status status =
GRPC_ERROR_IS_NONE(error)
? grpc_oauth2_token_fetcher_credentials_parse_server_response(
&r->response, &access_token_value, &token_lifetime)
: GRPC_CREDENTIALS_ERROR;
// Update cache and grab list of pending requests.
gpr_mu_lock(&mu_);
token_fetch_pending_ = false;
if (access_token_value.has_value()) {
access_token_value_ = access_token_value->Ref();
} else {
access_token_value_ = absl::nullopt;
}
token_expiration_ = status == GRPC_CREDENTIALS_OK
? gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
token_lifetime.as_timespec())
: 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) {
if (status == GRPC_CREDENTIALS_OK) {
pending_request->md->Append(
GRPC_AUTHORIZATION_METADATA_KEY, access_token_value->Ref(),
[](absl::string_view, const grpc_core::Slice&) { abort(); });
pending_request->result = std::move(pending_request->md);
} else {
auto err = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
"Error occurred when fetching oauth2 token.", &error, 1);
pending_request->result = grpc_error_to_absl_status(err);
GRPC_ERROR_UNREF(err);
}
pending_request->done.store(true, std::memory_order_release);
pending_request->waker.Wakeup();
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;
prev->Unref();
}
delete r;
}
grpc_core::ArenaPromise<absl::StatusOr<grpc_core::ClientMetadataHandle>>
grpc_oauth2_token_fetcher_credentials::GetRequestMetadata(
grpc_core::ClientMetadataHandle initial_metadata,
const grpc_call_credentials::GetRequestMetadataArgs*) {
// Check if we can use the cached token.
absl::optional<grpc_core::Slice> cached_access_token_value;
gpr_mu_lock(&mu_);
if (access_token_value_.has_value() &&
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_value = access_token_value_->Ref();
}
if (cached_access_token_value.has_value()) {
gpr_mu_unlock(&mu_);
initial_metadata->Append(
GRPC_AUTHORIZATION_METADATA_KEY, std::move(*cached_access_token_value),
[](absl::string_view, const grpc_core::Slice&) { abort(); });
return grpc_core::Immediate(std::move(initial_metadata));
}
// Couldn't get the token from the cache.
// Add request to pending_requests_ and start a new fetch if needed.
grpc_core::Duration refresh_threshold =
grpc_core::Duration::Seconds(GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS);
auto pending_request =
grpc_core::MakeRefCounted<grpc_oauth2_pending_get_request_metadata>();
pending_request->pollent = grpc_core::GetContext<grpc_polling_entity>();
pending_request->waker = grpc_core::Activity::current()->MakeNonOwningWaker();
grpc_polling_entity_add_to_pollset_set(
pending_request->pollent, grpc_polling_entity_pollset_set(&pollent_));
pending_request->next = pending_requests_;
pending_request->md = std::move(initial_metadata);
pending_requests_ = pending_request->Ref().release();
bool start_fetch = false;
if (!token_fetch_pending_) {
token_fetch_pending_ = true;
start_fetch = true;
}
gpr_mu_unlock(&mu_);
if (start_fetch) {
fetch_oauth2(new grpc_credentials_metadata_request(Ref()), &pollent_,
on_oauth2_token_fetcher_http_response,
grpc_core::ExecCtx::Get()->Now() + refresh_threshold);
}
return
[pending_request]()
-> grpc_core::Poll<absl::StatusOr<grpc_core::ClientMetadataHandle>> {
if (!pending_request->done.load(std::memory_order_acquire)) {
return grpc_core::Pending{};
}
return std::move(pending_request->result);
};
}
grpc_oauth2_token_fetcher_credentials::grpc_oauth2_token_fetcher_credentials()
: token_expiration_(gpr_inf_past(GPR_CLOCK_MONOTONIC)),
pollent_(grpc_polling_entity_create_from_pollset_set(
grpc_pollset_set_create())) {
gpr_mu_init(&mu_);
}
std::string grpc_oauth2_token_fetcher_credentials::debug_string() {
return "OAuth2TokenFetcherCredentials";
}
grpc_core::UniqueTypeName grpc_oauth2_token_fetcher_credentials::type() const {
static grpc_core::UniqueTypeName::Factory kFactory("Oauth2");
return kFactory.Create();
}
//
// 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_polling_entity* pollent,
grpc_iomgr_cb_func response_cb,
grpc_core::Timestamp deadline) override {
grpc_http_header header = {const_cast<char*>("Metadata-Flavor"),
const_cast<char*>("Google")};
grpc_http_request request;
memset(&request, 0, sizeof(grpc_http_request));
request.hdr_count = 1;
request.hdrs = &header;
/* TODO(ctiller): Carry the memory quota in ctx and share it with the host
channel. This would allow us to cancel an authentication query when under
extreme memory pressure. */
auto uri = grpc_core::URI::Create("http", GRPC_COMPUTE_ENGINE_METADATA_HOST,
GRPC_COMPUTE_ENGINE_METADATA_TOKEN_PATH,
{} /* query params */, "" /* fragment */);
GPR_ASSERT(uri.ok()); // params are hardcoded
http_request_ = grpc_core::HttpRequest::Get(
std::move(*uri), nullptr /* channel args */, pollent, &request,
deadline,
GRPC_CLOSURE_INIT(&http_get_cb_closure_, response_cb, metadata_req,
grpc_schedule_on_exec_ctx),
&metadata_req->response,
grpc_core::RefCountedPtr<grpc_channel_credentials>(
grpc_insecure_credentials_create()));
http_request_->Start();
}
std::string debug_string() override {
return absl::StrFormat(
"GoogleComputeEngineTokenFetcherCredentials{%s}",
grpc_oauth2_token_fetcher_credentials::debug_string());
}
private:
grpc_closure http_get_cb_closure_;
grpc_core::OrphanablePtr<grpc_core::HttpRequest> http_request_;
};
} // 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_polling_entity* pollent, grpc_iomgr_cb_func response_cb,
grpc_core::Timestamp deadline) {
grpc_http_header header = {
const_cast<char*>("Content-Type"),
const_cast<char*>("application/x-www-form-urlencoded")};
grpc_http_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_http_request));
request.hdr_count = 1;
request.hdrs = &header;
request.body = const_cast<char*>(body.c_str());
request.body_length = body.size();
/* TODO(ctiller): Carry the memory quota in ctx and share it with the host
channel. This would allow us to cancel an authentication query when under
extreme memory pressure. */
auto uri = grpc_core::URI::Create("https", GRPC_GOOGLE_OAUTH2_SERVICE_HOST,
GRPC_GOOGLE_OAUTH2_SERVICE_TOKEN_PATH,
{} /* query params */, "" /* fragment */);
GPR_ASSERT(uri.ok()); // params are hardcoded
http_request_ = grpc_core::HttpRequest::Post(
std::move(*uri), nullptr /* channel args */, pollent, &request, deadline,
GRPC_CLOSURE_INIT(&http_post_cb_closure_, response_cb, metadata_req,
grpc_schedule_on_exec_ctx),
&metadata_req->response, grpc_core::CreateHttpRequestSSLCredentials());
http_request_->Start();
}
grpc_google_refresh_token_credentials::grpc_google_refresh_token_credentials(
grpc_auth_refresh_token refresh_token)
: refresh_token_(refresh_token) {}
grpc_core::RefCountedPtr<grpc_call_credentials>
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<grpc_google_refresh_token_credentials>(
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());
}
grpc_core::UniqueTypeName grpc_google_refresh_token_credentials::type() const {
static grpc_core::UniqueTypeName::Factory kFactory("GoogleRefreshToken");
return kFactory.Create();
}
static std::string create_loggable_refresh_token(
grpc_auth_refresh_token* token) {
if (strcmp(token->type, GRPC_AUTH_JSON_TYPE_INVALID) == 0) {
return "<Invalid json token>";
}
return absl::StrFormat(
"{\n type: %s\n client_id: %s\n client_secret: "
"<redacted>\n refresh_token: <redacted>\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<std::string>* body) {
if (field == nullptr || strlen(field) == 0) return;
body->push_back(absl::StrFormat("&%s=%s", field_name, field));
}
grpc_error_handle LoadTokenFile(const char* path, gpr_slice* token) {
grpc_error_handle err = grpc_load_file(path, 1, token);
if (!GRPC_ERROR_IS_NONE(err)) 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(URI sts_url,
const grpc_sts_credentials_options* options)
: sts_url_(std::move(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)) {}
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_polling_entity* pollent,
grpc_iomgr_cb_func response_cb,
Timestamp deadline) override {
grpc_http_request request;
memset(&request, 0, sizeof(grpc_http_request));
grpc_error_handle err = FillBody(&request.body, &request.body_length);
if (!GRPC_ERROR_IS_NONE(err)) {
response_cb(metadata_req, err);
GRPC_ERROR_UNREF(err);
return;
}
grpc_http_header header = {
const_cast<char*>("Content-Type"),
const_cast<char*>("application/x-www-form-urlencoded")};
request.hdr_count = 1;
request.hdrs = &header;
/* TODO(ctiller): Carry the memory quota in ctx and share it with the host
channel. This would allow us to cancel an authentication query when under
extreme memory pressure. */
RefCountedPtr<grpc_channel_credentials> http_request_creds;
if (sts_url_.scheme() == "http") {
http_request_creds = RefCountedPtr<grpc_channel_credentials>(
grpc_insecure_credentials_create());
} else {
http_request_creds = CreateHttpRequestSSLCredentials();
}
http_request_ = HttpRequest::Post(
sts_url_, nullptr /* channel args */, pollent, &request, deadline,
GRPC_CLOSURE_INIT(&http_post_cb_closure_, response_cb, metadata_req,
grpc_schedule_on_exec_ctx),
&metadata_req->response, std::move(http_request_creds));
http_request_->Start();
gpr_free(request.body);
}
grpc_error_handle FillBody(char** body, size_t* body_length) {
*body = nullptr;
std::vector<std::string> body_parts;
grpc_slice subject_token = grpc_empty_slice();
grpc_slice actor_token = grpc_empty_slice();
grpc_error_handle err = GRPC_ERROR_NONE;
auto cleanup = [&body, &body_length, &body_parts, &subject_token,
&actor_token, &err]() {
if (GRPC_ERROR_IS_NONE(err)) {
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 (!GRPC_ERROR_IS_NONE(err)) return cleanup();
body_parts.push_back(absl::StrFormat(
GRPC_STS_POST_MINIMAL_BODY_FORMAT_STRING,
reinterpret_cast<const char*>(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 (!GRPC_ERROR_IS_NONE(err)) return cleanup();
MaybeAddToBody(
"actor_token",
reinterpret_cast<const char*>(GRPC_SLICE_START_PTR(actor_token)),
&body_parts);
MaybeAddToBody("actor_token_type", actor_token_type_.get(), &body_parts);
}
return cleanup();
}
URI sts_url_;
grpc_closure http_post_cb_closure_;
UniquePtr<char> resource_;
UniquePtr<char> audience_;
UniquePtr<char> scope_;
UniquePtr<char> requested_token_type_;
UniquePtr<char> subject_token_path_;
UniquePtr<char> subject_token_type_;
UniquePtr<char> actor_token_path_;
UniquePtr<char> actor_token_type_;
OrphanablePtr<HttpRequest> http_request_;
};
} // namespace
absl::StatusOr<URI> ValidateStsCredentialsOptions(
const grpc_sts_credentials_options* options) {
std::vector<grpc_error_handle> error_list;
absl::StatusOr<URI> sts_url =
URI::Parse(options->token_exchange_service_uri == nullptr
? ""
: options->token_exchange_service_uri);
if (!sts_url.ok()) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_CPP_STRING(
absl::StrFormat("Invalid or missing STS endpoint URL. Error: %s",
sts_url.status().ToString())));
} else if (sts_url->scheme() != "https" && sts_url->scheme() != "http") {
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()) {
return sts_url;
}
auto grpc_error_vec = GRPC_ERROR_CREATE_FROM_VECTOR(
"Invalid STS Credentials Options", &error_list);
auto retval =
absl::InvalidArgumentError(grpc_error_std_string(grpc_error_vec));
GRPC_ERROR_UNREF(grpc_error_vec);
return retval;
}
} // namespace grpc_core
grpc_call_credentials* grpc_sts_credentials_create(
const grpc_sts_credentials_options* options, void* reserved) {
GPR_ASSERT(reserved == nullptr);
absl::StatusOr<grpc_core::URI> sts_url =
grpc_core::ValidateStsCredentialsOptions(options);
if (!sts_url.ok()) {
gpr_log(GPR_ERROR, "STS Credentials creation failed. Error: %s.",
sts_url.status().ToString().c_str());
return nullptr;
}
return grpc_core::MakeRefCounted<grpc_core::StsTokenFetcherCredentials>(
std::move(*sts_url), options)
.release();
}
//
// Oauth2 Access Token credentials.
//
grpc_core::ArenaPromise<absl::StatusOr<grpc_core::ClientMetadataHandle>>
grpc_access_token_credentials::GetRequestMetadata(
grpc_core::ClientMetadataHandle initial_metadata,
const grpc_call_credentials::GetRequestMetadataArgs*) {
initial_metadata->Append(
GRPC_AUTHORIZATION_METADATA_KEY, access_token_value_.Ref(),
[](absl::string_view, const grpc_core::Slice&) { abort(); });
return grpc_core::Immediate(std::move(initial_metadata));
}
grpc_core::UniqueTypeName grpc_access_token_credentials::Type() {
static grpc_core::UniqueTypeName::Factory kFactory("AccessToken");
return kFactory.Create();
}
grpc_access_token_credentials::grpc_access_token_credentials(
const char* access_token)
: access_token_value_(grpc_core::Slice::FromCopiedString(
absl::StrCat("Bearer ", access_token))) {}
std::string grpc_access_token_credentials::debug_string() {
return absl::StrFormat("AccessTokenCredentials{Token:present}");
}
grpc_call_credentials* grpc_access_token_credentials_create(
const char* access_token, void* reserved) {
GRPC_API_TRACE(
"grpc_access_token_credentials_create(access_token=<redacted>, "
"reserved=%p)",
1, (reserved));
GPR_ASSERT(reserved == nullptr);
return grpc_core::MakeRefCounted<grpc_access_token_credentials>(access_token)
.release();
}