Skip to content

Commit

Permalink
Add support of implicit and explicit flows for external account creds
Browse files Browse the repository at this point in the history
  • Loading branch information
Chuan Ren committed Jan 5, 2021
1 parent 393687c commit 06cc42e
Show file tree
Hide file tree
Showing 18 changed files with 559 additions and 261 deletions.
1 change: 1 addition & 0 deletions grpc.def
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ EXPORTS
grpc_google_compute_engine_credentials_create
grpc_max_auth_token_lifetime
grpc_service_account_jwt_access_credentials_create
grpc_external_account_credentials_create
grpc_google_refresh_token_credentials_create
grpc_access_token_credentials_create
grpc_google_iam_credentials_create
Expand Down
8 changes: 8 additions & 0 deletions include/grpc/grpc_security.h
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,14 @@ grpc_service_account_jwt_access_credentials_create(const char* json_key,
gpr_timespec token_lifetime,
void* reserved);

/** Builds External Account credentials.
- json_string is the JSON string containing the credentials options.
- scopes_string contains the scopes to be binded with the credentials.
This API is used for experimental purposes for now and may change in the
future. */
GRPCAPI grpc_call_credentials* grpc_external_account_credentials_create(
const char* json_string, const char* scopes_string);

/** Creates an Oauth2 Refresh Token credentials object for connecting to Google.
May return NULL if the input is invalid.
WARNING: Do NOT use this credentials to connect to a non-google service as
Expand Down
6 changes: 6 additions & 0 deletions include/grpcpp/security/credentials.h
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,12 @@ grpc::Status StsCredentialsOptionsFromEnv(StsCredentialsOptions* options);
std::shared_ptr<CallCredentials> StsCredentials(
const StsCredentialsOptions& options);

/// Builds External Account credentials.
/// json_string is the JSON string containing the credentials options.
/// scopes contains the scopes to be binded with the credentials.
std::shared_ptr<CallCredentials> ExternalAccountCredentials(
const grpc::string& json_string, const std::vector<grpc::string>& scopes);

std::shared_ptr<CallCredentials> MetadataCredentialsFromPlugin(
std::unique_ptr<MetadataCredentialsPlugin> plugin,
grpc_security_level min_security_level);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ std::string UrlEncode(const absl::string_view& s) {
} // namespace

RefCountedPtr<AwsExternalAccountCredentials>
AwsExternalAccountCredentials::Create(ExternalAccountCredentialsOptions options,
AwsExternalAccountCredentials::Create(Options options,
std::vector<std::string> scopes,
grpc_error** error) {
auto creds = MakeRefCounted<AwsExternalAccountCredentials>(
Expand All @@ -68,8 +68,7 @@ AwsExternalAccountCredentials::Create(ExternalAccountCredentialsOptions options,
}

AwsExternalAccountCredentials::AwsExternalAccountCredentials(
ExternalAccountCredentialsOptions options, std::vector<std::string> scopes,
grpc_error** error)
Options options, std::vector<std::string> scopes, grpc_error** error)
: ExternalAccountCredentials(options, std::move(scopes)) {
audience_ = options.audience;
auto it = options.credential_source.object_value().find("environment_id");
Expand Down Expand Up @@ -121,7 +120,7 @@ AwsExternalAccountCredentials::AwsExternalAccountCredentials(
}

void AwsExternalAccountCredentials::RetrieveSubjectToken(
HTTPRequestContext* ctx, const ExternalAccountCredentialsOptions& options,
HTTPRequestContext* ctx, const Options& options,
std::function<void(std::string, grpc_error*)> cb) {
if (ctx == nullptr) {
FinishRetrieveSubjectToken(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,15 @@ namespace grpc_core {
class AwsExternalAccountCredentials final : public ExternalAccountCredentials {
public:
static RefCountedPtr<AwsExternalAccountCredentials> Create(
ExternalAccountCredentialsOptions options,
std::vector<std::string> scopes, grpc_error** error);
Options options, std::vector<std::string> scopes, grpc_error** error);

AwsExternalAccountCredentials(ExternalAccountCredentialsOptions options,
AwsExternalAccountCredentials(Options options,
std::vector<std::string> scopes,
grpc_error** error);

private:
void RetrieveSubjectToken(
HTTPRequestContext* ctx, const ExternalAccountCredentialsOptions& options,
HTTPRequestContext* ctx, const Options& options,
std::function<void(std::string, grpc_error*)> cb) override;

void RetrieveRegion();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,18 @@

#include "absl/strings/str_format.h"
#include "absl/strings/str_join.h"
#include "absl/strings/str_split.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"

#include "src/core/lib/http/parser.h"
#include "src/core/lib/security/util/json_util.h"
#include "src/core/lib/slice/b64.h"

#include "src/core/lib/security/credentials/external/aws_external_account_credentials.h"
#include "src/core/lib/security/credentials/external/file_external_account_credentials.h"
#include "src/core/lib/security/credentials/external/url_external_account_credentials.h"

#define EXTERNAL_ACCOUNT_CREDENTIALS_GRANT_TYPE \
"urn:ietf:params:oauth:grant-type:token-exchange"
#define EXTERNAL_ACCOUNT_CREDENTIALS_REQUESTED_TOKEN_TYPE \
Expand Down Expand Up @@ -57,8 +62,122 @@ std::string UrlEncode(const absl::string_view& s) {

} // namespace

RefCountedPtr<ExternalAccountCredentials> ExternalAccountCredentials::Create(
const Json& json, std::vector<std::string> scopes, grpc_error** error) {
GPR_ASSERT(*error == GRPC_ERROR_NONE);
Options options;
options.type = GRPC_AUTH_JSON_TYPE_INVALID;
if (json.type() != Json::Type::OBJECT) {
*error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"Invalid json to construct credentials options.");
return nullptr;
}
auto it = json.object_value().find("type");
if (it == json.object_value().end()) {
*error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("type field not present.");
return nullptr;
}
if (it->second.type() != Json::Type::STRING) {
*error =
GRPC_ERROR_CREATE_FROM_STATIC_STRING("type field must be a string.");
return nullptr;
}
if (it->second.string_value() != GRPC_AUTH_JSON_TYPE_EXTERNAL_ACCOUNT) {
*error =
GRPC_ERROR_CREATE_FROM_STATIC_STRING("Invalid credentials json type.");
return nullptr;
}
options.type = GRPC_AUTH_JSON_TYPE_EXTERNAL_ACCOUNT;
it = json.object_value().find("audience");
if (it == json.object_value().end()) {
*error =
GRPC_ERROR_CREATE_FROM_STATIC_STRING("audience field not present.");
return nullptr;
}
if (it->second.type() != Json::Type::STRING) {
*error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"audience field must be a string.");
return nullptr;
}
options.audience = it->second.string_value();
it = json.object_value().find("subject_token_type");
if (it == json.object_value().end()) {
*error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"subject_token_type field not present.");
return nullptr;
}
if (it->second.type() != Json::Type::STRING) {
*error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"subject_token_type field must be a string.");
return nullptr;
}
options.subject_token_type = it->second.string_value();
it = json.object_value().find("service_account_impersonation_url");
if (it != json.object_value().end()) {
options.service_account_impersonation_url = it->second.string_value();
}
it = json.object_value().find("token_url");
if (it == json.object_value().end()) {
*error =
GRPC_ERROR_CREATE_FROM_STATIC_STRING("token_url field not present.");
return nullptr;
}
if (it->second.type() != Json::Type::STRING) {
*error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"token_url field must be a string.");
return nullptr;
}
options.token_url = it->second.string_value();
it = json.object_value().find("token_info_url");
if (it != json.object_value().end()) {
options.token_info_url = it->second.string_value();
}
it = json.object_value().find("credential_source");
if (it == json.object_value().end()) {
*error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"credential_source field not present.");
return nullptr;
}
options.credential_source = it->second;
it = json.object_value().find("quota_project_id");
if (it != json.object_value().end()) {
options.quota_project_id = it->second.string_value();
}
it = json.object_value().find("client_id");
if (it != json.object_value().end()) {
options.client_id = it->second.string_value();
}
it = json.object_value().find("client_secret");
if (it != json.object_value().end()) {
options.client_secret = it->second.string_value();
}
RefCountedPtr<ExternalAccountCredentials> creds;
if (options.credential_source.object_value().find("environment_id") !=
options.credential_source.object_value().end()) {
creds = MakeRefCounted<AwsExternalAccountCredentials>(
std::move(options), std::move(scopes), error);
} else if (options.credential_source.object_value().find("file") !=
options.credential_source.object_value().end()) {
creds = MakeRefCounted<FileExternalAccountCredentials>(
std::move(options), std::move(scopes), error);
} else if (options.credential_source.object_value().find("url") !=
options.credential_source.object_value().end()) {
creds = MakeRefCounted<UrlExternalAccountCredentials>(
std::move(options), std::move(scopes), error);
} else {
*error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"Invalid options credential source to create "
"ExternalAccountCredentials.");
}
if (*error == GRPC_ERROR_NONE) {
return creds;
} else {
return nullptr;
}
}

ExternalAccountCredentials::ExternalAccountCredentials(
ExternalAccountCredentialsOptions options, std::vector<std::string> scopes)
Options options, std::vector<std::string> scopes)
: options_(std::move(options)) {
if (scopes.empty()) {
scopes.push_back(GOOGLE_CLOUD_PLATFORM_DEFAULT_SCOPE);
Expand Down Expand Up @@ -351,3 +470,28 @@ void ExternalAccountCredentials::FinishTokenFetch(grpc_error* error) {
}

} // namespace grpc_core

grpc_call_credentials* grpc_external_account_credentials_create(
const char* json_string, const char* scopes_string) {
grpc_error* error = GRPC_ERROR_NONE;
grpc_core::Json json = grpc_core::Json::Parse(json_string, &error);
if (error != GRPC_ERROR_NONE) {
gpr_log(GPR_ERROR,
"External account credentials creation failed. Error: %s.",
grpc_error_string(error));
GRPC_ERROR_UNREF(error);
return nullptr;
}
std::vector<std::string> scopes = absl::StrSplit(scopes_string, ',');
auto creds = grpc_core::ExternalAccountCredentials::Create(
json, std::move(scopes), &error)
.release();
if (error != GRPC_ERROR_NONE) {
gpr_log(GPR_ERROR,
"External account credentials creation failed. Error: %s.",
grpc_error_string(error));
GRPC_ERROR_UNREF(error);
return nullptr;
}
return creds;
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class ExternalAccountCredentials
: public grpc_oauth2_token_fetcher_credentials {
public:
// External account credentials json interface.
struct ExternalAccountCredentialsOptions {
struct Options {
std::string type;
std::string audience;
std::string subject_token_type;
Expand All @@ -48,8 +48,10 @@ class ExternalAccountCredentials
std::string client_secret;
};

ExternalAccountCredentials(ExternalAccountCredentialsOptions options,
std::vector<std::string> scopes);
static RefCountedPtr<ExternalAccountCredentials> Create(
const Json& json, std::vector<std::string> scopes, grpc_error** error);

ExternalAccountCredentials(Options options, std::vector<std::string> scopes);
~ExternalAccountCredentials() override;
std::string debug_string() override;

Expand Down Expand Up @@ -81,7 +83,7 @@ class ExternalAccountCredentials
// the callback function (cb) to pass the subject token (or error)
// back.
virtual void RetrieveSubjectToken(
HTTPRequestContext* ctx, const ExternalAccountCredentialsOptions& options,
HTTPRequestContext* ctx, const Options& options,
std::function<void(std::string, grpc_error*)> cb) = 0;

private:
Expand All @@ -105,7 +107,7 @@ class ExternalAccountCredentials

void FinishTokenFetch(grpc_error* error);

ExternalAccountCredentialsOptions options_;
Options options_;
std::vector<std::string> scopes_;

HTTPRequestContext* ctx_ = nullptr;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@
namespace grpc_core {

RefCountedPtr<FileExternalAccountCredentials>
FileExternalAccountCredentials::Create(
ExternalAccountCredentialsOptions options, std::vector<std::string> scopes,
grpc_error** error) {
FileExternalAccountCredentials::Create(Options options,
std::vector<std::string> scopes,
grpc_error** error) {
auto creds = MakeRefCounted<FileExternalAccountCredentials>(
std::move(options), std::move(scopes), error);
if (*error == GRPC_ERROR_NONE) {
Expand All @@ -39,8 +39,7 @@ FileExternalAccountCredentials::Create(
}

FileExternalAccountCredentials::FileExternalAccountCredentials(
ExternalAccountCredentialsOptions options, std::vector<std::string> scopes,
grpc_error** error)
Options options, std::vector<std::string> scopes, grpc_error** error)
: ExternalAccountCredentials(options, std::move(scopes)) {
auto it = options.credential_source.object_value().find("file");
if (it == options.credential_source.object_value().end()) {
Expand Down Expand Up @@ -92,7 +91,7 @@ FileExternalAccountCredentials::FileExternalAccountCredentials(
}

void FileExternalAccountCredentials::RetrieveSubjectToken(
HTTPRequestContext* ctx, const ExternalAccountCredentialsOptions& options,
HTTPRequestContext* ctx, const Options& options,
std::function<void(std::string, grpc_error*)> cb) {
struct SliceWrapper {
~SliceWrapper() { grpc_slice_unref_internal(slice); }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,15 @@ namespace grpc_core {
class FileExternalAccountCredentials final : public ExternalAccountCredentials {
public:
static RefCountedPtr<FileExternalAccountCredentials> Create(
ExternalAccountCredentialsOptions options,
std::vector<std::string> scopes, grpc_error** error);
Options options, std::vector<std::string> scopes, grpc_error** error);

FileExternalAccountCredentials(ExternalAccountCredentialsOptions options,
FileExternalAccountCredentials(Options options,
std::vector<std::string> scopes,
grpc_error** error);

private:
void RetrieveSubjectToken(
HTTPRequestContext* ctx, const ExternalAccountCredentialsOptions& options,
HTTPRequestContext* ctx, const Options& options,
std::function<void(std::string, grpc_error*)> cb) override;

// Fields of credential source
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
namespace grpc_core {

RefCountedPtr<UrlExternalAccountCredentials>
UrlExternalAccountCredentials::Create(ExternalAccountCredentialsOptions options,
UrlExternalAccountCredentials::Create(Options options,
std::vector<std::string> scopes,
grpc_error** error) {
auto creds = MakeRefCounted<UrlExternalAccountCredentials>(
Expand All @@ -37,8 +37,7 @@ UrlExternalAccountCredentials::Create(ExternalAccountCredentialsOptions options,
}

UrlExternalAccountCredentials::UrlExternalAccountCredentials(
ExternalAccountCredentialsOptions options, std::vector<std::string> scopes,
grpc_error** error)
Options options, std::vector<std::string> scopes, grpc_error** error)
: ExternalAccountCredentials(options, std::move(scopes)) {
auto it = options.credential_source.object_value().find("url");
if (it == options.credential_source.object_value().end()) {
Expand Down Expand Up @@ -113,7 +112,7 @@ UrlExternalAccountCredentials::UrlExternalAccountCredentials(
}

void UrlExternalAccountCredentials::RetrieveSubjectToken(
HTTPRequestContext* ctx, const ExternalAccountCredentialsOptions& options,
HTTPRequestContext* ctx, const Options& options,
std::function<void(std::string, grpc_error*)> cb) {
if (ctx == nullptr) {
FinishRetrieveSubjectToken(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,15 @@ namespace grpc_core {
class UrlExternalAccountCredentials final : public ExternalAccountCredentials {
public:
static RefCountedPtr<UrlExternalAccountCredentials> Create(
ExternalAccountCredentialsOptions options,
std::vector<std::string> scopes, grpc_error** error);
Options options, std::vector<std::string> scopes, grpc_error** error);

UrlExternalAccountCredentials(ExternalAccountCredentialsOptions options,
UrlExternalAccountCredentials(Options options,
std::vector<std::string> scopes,
grpc_error** error);

private:
void RetrieveSubjectToken(
HTTPRequestContext* ctx, const ExternalAccountCredentialsOptions& options,
HTTPRequestContext* ctx, const Options& options,
std::function<void(std::string, grpc_error*)> cb) override;

static void OnRetrieveSubjectToken(void* arg, grpc_error* error);
Expand Down
Loading

0 comments on commit 06cc42e

Please sign in to comment.