Support user provided "scope" in JWT and GDC (#26577)

* support scope overriding in jwt and gdc

* fix formatting

* fix bazel build issue

* fix clang tidy
pull/26593/head
yihuaz 4 years ago committed by GitHub
parent f835f3f97c
commit 6df967966b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 23
      include/grpc/grpc_security.h
  2. 18
      include/grpcpp/security/credentials.h
  3. 2
      src/core/ext/xds/xds_bootstrap.cc
  4. 18
      src/core/lib/security/credentials/google_default/google_default_credentials.cc
  5. 15
      src/core/lib/security/credentials/jwt/json_token.cc
  6. 7
      src/core/lib/security/credentials/jwt/json_token.h
  7. 39
      src/core/lib/security/credentials/jwt/jwt_credentials.cc
  8. 13
      src/core/lib/security/credentials/jwt/jwt_credentials.h
  9. 14
      src/cpp/client/secure_credentials.cc
  10. 2
      src/php/ext/grpc/channel_credentials.c
  11. 2
      src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi
  12. 4
      src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi
  13. 4
      src/ruby/ext/grpc/rb_grpc_imports.generated.h
  14. 2
      test/core/security/create_jwt.cc
  15. 179
      test/core/security/credentials_test.cc
  16. 37
      test/core/security/json_token_test.cc
  17. 12
      test/core/security/jwt_verifier_test.cc

@ -159,9 +159,17 @@ GRPCAPI void grpc_channel_credentials_release(grpc_channel_credentials* creds);
If nullptr is supplied, the returned channel credentials object will use a
call credentials object based on the Application Default Credentials
mechanism.
user_provided_scope is an optional field for user to use in the JWT token to
represent the scope field. It will only be used if a service account JWT
access credential is created by the application default credentials
mechanism. If user_provided_scope is not nullptr, the audience (service URL)
field in the JWT token will be cleared, which is dictated by
https://google.aip.dev/auth/4111. Also note that user_provided_scope will be
ignored if the call_credentials is not nullptr.
*/
GRPCAPI grpc_channel_credentials* grpc_google_default_credentials_create(
grpc_call_credentials* call_credentials);
grpc_call_credentials* call_credentials, const char* user_provided_scope);
/** Callback for getting the SSL roots override from the application.
In case of success, *pem_roots_certs must be set to a NULL terminated string
@ -324,11 +332,16 @@ GRPCAPI gpr_timespec grpc_max_auth_token_lifetime(void);
- json_key is the JSON key string containing the client's private key.
- token_lifetime is the lifetime of each Json Web Token (JWT) created with
this credentials. It should not exceed grpc_max_auth_token_lifetime or
will be cropped to this value. */
will be cropped to this value.
- user_provided_scope is an optional field for user to use in the JWT token
to represent the scope field.
- clear_audience dictating the clearance of audience field when it
is set to a non-zero value AND user_provided_scope is not nullptr.
*/
GRPCAPI grpc_call_credentials*
grpc_service_account_jwt_access_credentials_create(const char* json_key,
gpr_timespec token_lifetime,
void* reserved);
grpc_service_account_jwt_access_credentials_create(
const char* json_key, gpr_timespec token_lifetime,
const char* user_provided_scope, int clear_audience);
/** Builds External Account credentials.
- json_string is the JSON string containing the credentials options.

@ -172,11 +172,19 @@ struct SslCredentialsOptions {
/// Builds credentials with reasonable defaults.
///
/// user_provided_scope is an optional field for user to use to represent the
/// scope field in the JWT token. It will only be used if a service account JWT
/// access credential is created by the application default credentials
/// mechanism. If user_provided_scope is not empty, the audience (service URL)
/// field in the JWT token will be cleared, which is dictated by
/// https://google.aip.dev/auth/4111.
///
/// \warning Only use these credentials when connecting to a Google endpoint.
/// Using these credentials to connect to any other service may result in this
/// service being able to impersonate your client for requests to Google
/// services.
std::shared_ptr<ChannelCredentials> GoogleDefaultCredentials();
std::shared_ptr<ChannelCredentials> GoogleDefaultCredentials(
const grpc::string& user_provided_scope = "");
/// Builds SSL Credentials given SSL specific options
std::shared_ptr<ChannelCredentials> SslCredentials(
@ -197,9 +205,15 @@ constexpr long kMaxAuthTokenLifetimeSecs = 3600;
/// token_lifetime_seconds is the lifetime in seconds of each Json Web Token
/// (JWT) created with this credentials. It should not exceed
/// \a kMaxAuthTokenLifetimeSecs or will be cropped to this value.
/// user_provided_scope is an optional field for user to use to represent the
/// scope field in the JWT token.
/// clear_audience is a boolean field that dictates clearing audience
/// field in the JWT token when it is set to true AND user_provided_scope is
/// not empty. */
std::shared_ptr<CallCredentials> ServiceAccountJWTAccessCredentials(
const grpc::string& json_key,
long token_lifetime_seconds = kMaxAuthTokenLifetimeSecs);
long token_lifetime_seconds = kMaxAuthTokenLifetimeSecs,
const grpc::string& user_provided_scope = "", bool clear_audience = false);
/// Builds refresh token credentials.
/// json_refresh_token is the JSON string containing the refresh token along

@ -59,7 +59,7 @@ RefCountedPtr<grpc_channel_credentials>
XdsChannelCredsRegistry::MakeChannelCreds(const std::string& creds_type,
const Json& /*config*/) {
if (creds_type == "google_default") {
return grpc_google_default_credentials_create(nullptr);
return grpc_google_default_credentials_create(nullptr, nullptr);
} else if (creds_type == "insecure") {
return grpc_insecure_credentials_create();
} else if (creds_type == "fake") {

@ -224,7 +224,7 @@ static int is_metadata_server_reachable() {
/* Takes ownership of creds_path if not NULL. */
static grpc_error_handle create_default_creds_from_path(
const std::string& creds_path,
const std::string& creds_path, const char* user_provided_scope,
grpc_core::RefCountedPtr<grpc_call_credentials>* creds) {
grpc_auth_json_key key;
grpc_auth_refresh_token token;
@ -250,9 +250,11 @@ static grpc_error_handle create_default_creds_from_path(
/* First, try an auth json key. */
key = grpc_auth_json_key_create_from_json(json);
if (grpc_auth_json_key_is_valid(&key)) {
if (user_provided_scope == nullptr) user_provided_scope = "";
result =
grpc_service_account_jwt_access_credentials_create_from_auth_json_key(
key, grpc_max_auth_token_lifetime());
key, grpc_max_auth_token_lifetime(), user_provided_scope,
true /* clear_audience */);
if (result == nullptr) {
error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"grpc_service_account_jwt_access_credentials_create_from_auth_json_"
@ -306,14 +308,15 @@ static bool metadata_server_available() {
}
static grpc_core::RefCountedPtr<grpc_call_credentials> make_default_call_creds(
grpc_error_handle* error) {
const char* user_provided_scope, grpc_error_handle* error) {
grpc_core::RefCountedPtr<grpc_call_credentials> call_creds;
grpc_error_handle err;
/* First, try the environment variable. */
char* path_from_env = gpr_getenv(GRPC_GOOGLE_CREDENTIALS_ENV_VAR);
if (path_from_env != nullptr) {
err = create_default_creds_from_path(path_from_env, &call_creds);
err = create_default_creds_from_path(path_from_env, user_provided_scope,
&call_creds);
gpr_free(path_from_env);
if (err == GRPC_ERROR_NONE) return call_creds;
*error = grpc_error_add_child(*error, err);
@ -321,7 +324,8 @@ static grpc_core::RefCountedPtr<grpc_call_credentials> make_default_call_creds(
/* Then the well-known file. */
err = create_default_creds_from_path(
grpc_get_well_known_google_credentials_file_path(), &call_creds);
grpc_get_well_known_google_credentials_file_path(), user_provided_scope,
&call_creds);
if (err == GRPC_ERROR_NONE) return call_creds;
*error = grpc_error_add_child(*error, err);
@ -343,7 +347,7 @@ static grpc_core::RefCountedPtr<grpc_call_credentials> make_default_call_creds(
}
grpc_channel_credentials* grpc_google_default_credentials_create(
grpc_call_credentials* call_credentials) {
grpc_call_credentials* call_credentials, const char* user_provided_scope) {
grpc_channel_credentials* result = nullptr;
grpc_core::RefCountedPtr<grpc_call_credentials> call_creds(call_credentials);
grpc_error_handle error = GRPC_ERROR_NONE;
@ -353,7 +357,7 @@ grpc_channel_credentials* grpc_google_default_credentials_create(
(call_credentials));
if (call_creds == nullptr) {
call_creds = make_default_call_creds(&error);
call_creds = make_default_call_creds(user_provided_scope, &error);
}
if (call_creds != nullptr) {

@ -165,7 +165,8 @@ static char* encoded_jwt_header(const char* key_id, const char* algorithm) {
static char* encoded_jwt_claim(const grpc_auth_json_key* json_key,
const char* audience,
gpr_timespec token_lifetime, const char* scope) {
gpr_timespec token_lifetime, const char* scope,
bool clear_audience) {
gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME);
gpr_timespec expiration = gpr_time_add(now, token_lifetime);
if (gpr_time_cmp(token_lifetime, grpc_max_auth_token_lifetime()) > 0) {
@ -175,15 +176,19 @@ static char* encoded_jwt_claim(const grpc_auth_json_key* json_key,
Json::Object object = {
{"iss", json_key->client_email},
{"aud", audience},
{"iat", now.tv_sec},
{"exp", expiration.tv_sec},
};
if (scope != nullptr) {
object["scope"] = scope;
if (!clear_audience) {
object["aud"] = audience;
}
} else {
/* Unscoped JWTs need a sub field. */
object["sub"] = json_key->client_email;
object["aud"] = audience;
}
Json json(object);
@ -264,7 +269,8 @@ end:
char* grpc_jwt_encode_and_sign(const grpc_auth_json_key* json_key,
const char* audience,
gpr_timespec token_lifetime, const char* scope) {
gpr_timespec token_lifetime, const char* scope,
bool clear_audience) {
if (g_jwt_encode_and_sign_override != nullptr) {
return g_jwt_encode_and_sign_override(json_key, audience, token_lifetime,
scope);
@ -272,7 +278,8 @@ char* grpc_jwt_encode_and_sign(const grpc_auth_json_key* json_key,
const char* sig_algo = GRPC_JWT_RSA_SHA256_ALGORITHM;
char* to_sign = dot_concat_and_free_strings(
encoded_jwt_header(json_key->private_key_id, sig_algo),
encoded_jwt_claim(json_key, audience, token_lifetime, scope));
encoded_jwt_claim(json_key, audience, token_lifetime, scope,
clear_audience));
char* sig = compute_and_encode_signature(json_key, sig_algo, to_sign);
if (sig == nullptr) {
gpr_free(to_sign);

@ -58,10 +58,13 @@ void grpc_auth_json_key_destruct(grpc_auth_json_key* json_key);
/* --- json token encoding and signing. --- */
/* Caller is responsible for calling gpr_free on the returned value. May return
NULL on invalid input. The scope parameter may be NULL. */
NULL on invalid input. The scope parameter may be NULL. The
clear_audience parameter dictates the clearing of audience field when
it is set to a non-zero value AND scope is not nullptr. */
char* grpc_jwt_encode_and_sign(const grpc_auth_json_key* json_key,
const char* audience,
gpr_timespec token_lifetime, const char* scope);
gpr_timespec token_lifetime, const char* scope,
bool clear_audience);
/* Override encode_and_sign function for testing. */
typedef char* (*grpc_jwt_encode_and_sign_override)(

@ -62,13 +62,17 @@ bool grpc_service_account_jwt_access_credentials::get_request_metadata(
grpc_closure* /*on_request_metadata*/, grpc_error_handle* error) {
gpr_timespec refresh_threshold = gpr_time_from_seconds(
GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS, GPR_TIMESPAN);
absl::string_view scope = user_provided_scope_;
/* See if we can return a cached jwt. */
grpc_mdelem jwt_md = GRPC_MDNULL;
{
gpr_mu_lock(&cache_mu_);
// We use the cached token if
// 1) cached_.service_url is equal to context.service_url OR
// 2) scope is not emtpy AND clear_audience_ is a non-zero value.
if (cached_.service_url != nullptr &&
strcmp(cached_.service_url, context.service_url) == 0 &&
(strcmp(cached_.service_url, context.service_url) == 0 ||
(!scope.empty() && clear_audience_)) &&
!GRPC_MDISNULL(cached_.jwt_md) &&
(gpr_time_cmp(
gpr_time_sub(cached_.jwt_expiration, gpr_now(GPR_CLOCK_REALTIME)),
@ -84,7 +88,8 @@ bool grpc_service_account_jwt_access_credentials::get_request_metadata(
gpr_mu_lock(&cache_mu_);
reset_cache();
jwt = grpc_jwt_encode_and_sign(&key_, context.service_url, jwt_lifetime_,
nullptr);
scope.empty() ? nullptr : scope.data(),
clear_audience_);
if (jwt != nullptr) {
std::string md_value = absl::StrCat("Bearer ", jwt);
gpr_free(jwt);
@ -115,8 +120,13 @@ void grpc_service_account_jwt_access_credentials::cancel_get_request_metadata(
grpc_service_account_jwt_access_credentials::
grpc_service_account_jwt_access_credentials(grpc_auth_json_key key,
gpr_timespec token_lifetime)
: grpc_call_credentials(GRPC_CALL_CREDENTIALS_TYPE_JWT), key_(key) {
gpr_timespec token_lifetime,
std::string user_provided_scope,
bool clear_audience)
: grpc_call_credentials(GRPC_CALL_CREDENTIALS_TYPE_JWT),
key_(key),
user_provided_scope_(std::move(user_provided_scope)),
clear_audience_(clear_audience) {
gpr_timespec max_token_lifetime = grpc_max_auth_token_lifetime();
if (gpr_time_cmp(token_lifetime, max_token_lifetime) > 0) {
gpr_log(GPR_INFO,
@ -131,13 +141,14 @@ grpc_service_account_jwt_access_credentials::
grpc_core::RefCountedPtr<grpc_call_credentials>
grpc_service_account_jwt_access_credentials_create_from_auth_json_key(
grpc_auth_json_key key, gpr_timespec token_lifetime) {
grpc_auth_json_key key, gpr_timespec token_lifetime,
std::string user_provided_scope, bool clear_audience) {
if (!grpc_auth_json_key_is_valid(&key)) {
gpr_log(GPR_ERROR, "Invalid input for jwt credentials creation");
return nullptr;
}
return grpc_core::MakeRefCounted<grpc_service_account_jwt_access_credentials>(
key, token_lifetime);
key, token_lifetime, std::move(user_provided_scope), clear_audience);
}
static char* redact_private_key(const char* json_key) {
@ -152,7 +163,8 @@ static char* redact_private_key(const char* json_key) {
}
grpc_call_credentials* grpc_service_account_jwt_access_credentials_create(
const char* json_key, gpr_timespec token_lifetime, void* reserved) {
const char* json_key, gpr_timespec token_lifetime,
const char* user_provided_scope, int clear_audience) {
if (GRPC_TRACE_FLAG_ENABLED(grpc_api_trace)) {
char* clean_json = redact_private_key(json_key);
gpr_log(GPR_INFO,
@ -161,15 +173,18 @@ grpc_call_credentials* grpc_service_account_jwt_access_credentials_create(
"token_lifetime="
"gpr_timespec { tv_sec: %" PRId64
", tv_nsec: %d, clock_type: %d }, "
"reserved=%p)",
"user_provided_scope=%s, "
"clear_audience=%d)",
clean_json, token_lifetime.tv_sec, token_lifetime.tv_nsec,
static_cast<int>(token_lifetime.clock_type), reserved);
static_cast<int>(token_lifetime.clock_type), user_provided_scope,
clear_audience);
gpr_free(clean_json);
}
GPR_ASSERT(reserved == nullptr);
grpc_core::ApplicationCallbackExecCtx callback_exec_ctx;
grpc_core::ExecCtx exec_ctx;
if (user_provided_scope == nullptr) user_provided_scope = "";
return grpc_service_account_jwt_access_credentials_create_from_auth_json_key(
grpc_auth_json_key_create_from_string(json_key), token_lifetime)
grpc_auth_json_key_create_from_string(json_key), token_lifetime,
user_provided_scope, clear_audience)
.release();
}

@ -34,7 +34,9 @@ class grpc_service_account_jwt_access_credentials
: public grpc_call_credentials {
public:
grpc_service_account_jwt_access_credentials(grpc_auth_json_key key,
gpr_timespec token_lifetime);
gpr_timespec token_lifetime,
std::string user_provided_scope,
bool clear_audience);
~grpc_service_account_jwt_access_credentials() override;
bool get_request_metadata(grpc_polling_entity* pollent,
@ -48,7 +50,9 @@ class grpc_service_account_jwt_access_credentials
const gpr_timespec& jwt_lifetime() const { return jwt_lifetime_; }
const grpc_auth_json_key& key() const { return key_; }
const std::string& user_provided_scope() const {
return user_provided_scope_;
}
std::string debug_string() override {
return absl::StrFormat(
"JWTAccessCredentials{ExpirationTime:%s}",
@ -70,12 +74,15 @@ class grpc_service_account_jwt_access_credentials
grpc_auth_json_key key_;
gpr_timespec jwt_lifetime_;
std::string user_provided_scope_;
bool clear_audience_;
};
// Private constructor for jwt credentials from an already parsed json key.
// Takes ownership of the key.
grpc_core::RefCountedPtr<grpc_call_credentials>
grpc_service_account_jwt_access_credentials_create_from_auth_json_key(
grpc_auth_json_key key, gpr_timespec token_lifetime);
grpc_auth_json_key key, gpr_timespec token_lifetime,
std::string user_provided_scope, bool clear_audience);
#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JWT_CREDENTIALS_H */

@ -103,10 +103,13 @@ std::shared_ptr<CallCredentials> WrapCallCredentials(
}
} // namespace
std::shared_ptr<ChannelCredentials> GoogleDefaultCredentials() {
std::shared_ptr<ChannelCredentials> GoogleDefaultCredentials(
const grpc::string& user_provided_scope) {
grpc::GrpcLibraryCodegen init; // To call grpc_init().
return internal::WrapChannelCredentials(
grpc_google_default_credentials_create(nullptr));
grpc_google_default_credentials_create(
nullptr,
user_provided_scope.empty() ? nullptr : user_provided_scope.c_str()));
}
std::shared_ptr<CallCredentials> ExternalAccountCredentials(
@ -320,7 +323,8 @@ std::shared_ptr<CallCredentials> GoogleComputeEngineCredentials() {
// Builds JWT credentials.
std::shared_ptr<CallCredentials> ServiceAccountJWTAccessCredentials(
const std::string& json_key, long token_lifetime_seconds) {
const std::string& json_key, long token_lifetime_seconds,
const grpc::string& user_provided_scope, bool clear_audience) {
grpc::GrpcLibraryCodegen init; // To call grpc_init().
if (token_lifetime_seconds <= 0) {
gpr_log(GPR_ERROR,
@ -330,7 +334,9 @@ std::shared_ptr<CallCredentials> ServiceAccountJWTAccessCredentials(
gpr_timespec lifetime =
gpr_time_from_seconds(token_lifetime_seconds, GPR_TIMESPAN);
return WrapCallCredentials(grpc_service_account_jwt_access_credentials_create(
json_key.c_str(), lifetime, nullptr));
json_key.c_str(), lifetime,
user_provided_scope.empty() ? nullptr : user_provided_scope.c_str(),
clear_audience));
}
// Builds refresh token credentials.

@ -131,7 +131,7 @@ PHP_METHOD(ChannelCredentials, invalidateDefaultRootsPem) {
* @return ChannelCredentials The new default channel credentials object
*/
PHP_METHOD(ChannelCredentials, createDefault) {
grpc_channel_credentials *creds = grpc_google_default_credentials_create(NULL);
grpc_channel_credentials *creds = grpc_google_default_credentials_create(NULL, NULL);
zval *creds_object = grpc_php_wrap_channel_credentials(creds, NULL, false
TSRMLS_CC);
RETURN_DESTROY_ZVAL(creds_object);

@ -434,7 +434,7 @@ cdef class ComputeEngineChannelCredentials(ChannelCredentials):
raise ValueError("Call credentials may not be NULL.")
cdef grpc_channel_credentials *c(self) except *:
self._c_creds = grpc_google_default_credentials_create(self._call_creds)
self._c_creds = grpc_google_default_credentials_create(self._call_creds, NULL)
return self._c_creds

@ -525,7 +525,7 @@ cdef extern from "grpc/grpc_security.h":
void grpc_set_ssl_roots_override_callback(
grpc_ssl_roots_override_callback cb) nogil
grpc_channel_credentials *grpc_google_default_credentials_create(grpc_call_credentials* call_credentials) nogil
grpc_channel_credentials *grpc_google_default_credentials_create(grpc_call_credentials* call_credentials, const char* user_provided_scope) nogil
grpc_channel_credentials *grpc_ssl_credentials_create(
const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pair,
verify_peer_options *verify_options, void *reserved) nogil
@ -551,7 +551,7 @@ cdef extern from "grpc/grpc_security.h":
void *reserved) nogil
grpc_call_credentials *grpc_service_account_jwt_access_credentials_create(
const char *json_key,
gpr_timespec token_lifetime, void *reserved) nogil
gpr_timespec token_lifetime, const char* user_provided_scope, int clear_audience) nogil
grpc_call_credentials *grpc_google_refresh_token_credentials_create(
const char *json_refresh_token, void *reserved) nogil
grpc_call_credentials *grpc_google_iam_credentials_create(

@ -353,7 +353,7 @@ extern grpc_call_credentials_release_type grpc_call_credentials_release_import;
typedef void(*grpc_channel_credentials_release_type)(grpc_channel_credentials* creds);
extern grpc_channel_credentials_release_type grpc_channel_credentials_release_import;
#define grpc_channel_credentials_release grpc_channel_credentials_release_import
typedef grpc_channel_credentials*(*grpc_google_default_credentials_create_type)(grpc_call_credentials* call_credentials);
typedef grpc_channel_credentials*(*grpc_google_default_credentials_create_type)(grpc_call_credentials* call_credentials, const char* user_provided_scope);
extern grpc_google_default_credentials_create_type grpc_google_default_credentials_create_import;
#define grpc_google_default_credentials_create grpc_google_default_credentials_create_import
typedef void(*grpc_set_ssl_roots_override_callback_type)(grpc_ssl_roots_override_callback cb);
@ -377,7 +377,7 @@ extern grpc_google_compute_engine_credentials_create_type grpc_google_compute_en
typedef gpr_timespec(*grpc_max_auth_token_lifetime_type)(void);
extern grpc_max_auth_token_lifetime_type grpc_max_auth_token_lifetime_import;
#define grpc_max_auth_token_lifetime grpc_max_auth_token_lifetime_import
typedef grpc_call_credentials*(*grpc_service_account_jwt_access_credentials_create_type)(const char* json_key, gpr_timespec token_lifetime, void* reserved);
typedef grpc_call_credentials*(*grpc_service_account_jwt_access_credentials_create_type)(const char* json_key, gpr_timespec token_lifetime, const char* user_provided_scope, int clear_audience);
extern grpc_service_account_jwt_access_credentials_create_type grpc_service_account_jwt_access_credentials_create_import;
#define grpc_service_account_jwt_access_credentials_create grpc_service_account_jwt_access_credentials_create_import
typedef grpc_call_credentials*(*grpc_external_account_credentials_create_type)(const char* json_string, const char* scopes_string);

@ -45,7 +45,7 @@ void create_jwt(const char* json_key_file_path, const char* service_url,
}
jwt = grpc_jwt_encode_and_sign(
&key, service_url == nullptr ? GRPC_JWT_OAUTH2_AUDIENCE : service_url,
grpc_max_auth_token_lifetime(), scope);
grpc_max_auth_token_lifetime(), scope, false);
grpc_auth_json_key_destruct(&key);
if (jwt == nullptr) {
fprintf(stderr, "Could not create JWT.\n");

@ -129,6 +129,9 @@ static const char test_signed_jwt_token_type[] =
static const char test_signed_jwt2[] =
"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImY0OTRkN2M1YWU2MGRmOTcyNmM5YW"
"U2MDcyZTViYTdnZDkwODg5YzcifQ";
static const char test_signed_jwt3[] =
"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImY0OTRkN2M1YWU2MGRmOTcyNmM6YW"
"U3MDcyZTViYTdnZDkwODg5YzcifR";
static const char test_signed_jwt_token_type2[] =
"urn:ietf:params:oauth:token-type:jwt";
static const char test_signed_jwt_path_prefix[] = "test_sign_jwt";
@ -1296,6 +1299,16 @@ static char* encode_and_sign_jwt_success(const grpc_auth_json_key* json_key,
return gpr_strdup(test_signed_jwt);
}
static char* encode_and_sign_jwt_with_scope_success(
const grpc_auth_json_key* json_key, const char* /*audience*/,
gpr_timespec token_lifetime, const char* scope) {
if (strcmp(scope, test_scope) == 0) {
validate_jwt_encode_and_sign_params(json_key, scope, token_lifetime);
return gpr_strdup(test_signed_jwt3);
}
return nullptr;
}
static char* encode_and_sign_jwt_failure(const grpc_auth_json_key* json_key,
const char* /*audience*/,
gpr_timespec token_lifetime,
@ -1325,7 +1338,7 @@ static void test_jwt_creds_lifetime(void) {
// Max lifetime.
grpc_call_credentials* jwt_creds =
grpc_service_account_jwt_access_credentials_create(
json_key_string, grpc_max_auth_token_lifetime(), nullptr);
json_key_string, grpc_max_auth_token_lifetime(), nullptr, 0);
GPR_ASSERT(gpr_time_cmp(creds_as_jwt(jwt_creds)->jwt_lifetime(),
grpc_max_auth_token_lifetime()) == 0);
/* Check security level. */
@ -1339,7 +1352,7 @@ static void test_jwt_creds_lifetime(void) {
gpr_timespec token_lifetime = {10, 0, GPR_TIMESPAN};
GPR_ASSERT(gpr_time_cmp(grpc_max_auth_token_lifetime(), token_lifetime) > 0);
jwt_creds = grpc_service_account_jwt_access_credentials_create(
json_key_string, token_lifetime, nullptr);
json_key_string, token_lifetime, nullptr, 0);
GPR_ASSERT(gpr_time_cmp(creds_as_jwt(jwt_creds)->jwt_lifetime(),
token_lifetime) == 0);
GPR_ASSERT(strncmp(expected_creds_debug_string_prefix,
@ -1351,7 +1364,7 @@ static void test_jwt_creds_lifetime(void) {
gpr_timespec add_to_max = {10, 0, GPR_TIMESPAN};
token_lifetime = gpr_time_add(grpc_max_auth_token_lifetime(), add_to_max);
jwt_creds = grpc_service_account_jwt_access_credentials_create(
json_key_string, token_lifetime, nullptr);
json_key_string, token_lifetime, nullptr, 0);
GPR_ASSERT(gpr_time_cmp(creds_as_jwt(jwt_creds)->jwt_lifetime(),
grpc_max_auth_token_lifetime()) == 0);
GPR_ASSERT(strncmp(expected_creds_debug_string_prefix,
@ -1374,7 +1387,7 @@ static void test_jwt_creds_success(void) {
expected_md emd[] = {{"authorization", expected_md_value.c_str()}};
grpc_call_credentials* creds =
grpc_service_account_jwt_access_credentials_create(
json_key_string, grpc_max_auth_token_lifetime(), nullptr);
json_key_string, grpc_max_auth_token_lifetime(), nullptr, 0);
/* First request: jwt_encode_and_sign should be called. */
request_metadata_state* state =
@ -1408,6 +1421,99 @@ static void test_jwt_creds_success(void) {
grpc_jwt_encode_and_sign_set_override(nullptr);
}
static void test_jwt_creds_with_scope_success(void) {
const char expected_creds_debug_string_prefix[] =
"JWTAccessCredentials{ExpirationTime:";
char* json_key_string = test_json_key_str();
grpc_core::ExecCtx exec_ctx;
grpc_auth_metadata_context auth_md_ctx = {test_service_url, test_method,
nullptr, nullptr};
std::string expected_md_value = absl::StrCat("Bearer ", test_signed_jwt3);
expected_md emd[] = {{"authorization", expected_md_value.c_str()}};
grpc_call_credentials* creds =
grpc_service_account_jwt_access_credentials_create(
json_key_string, grpc_max_auth_token_lifetime(), test_scope,
0 /* should_clear_audience*/);
/* First request: jwt_encode_and_sign should be called. */
request_metadata_state* state =
make_request_metadata_state(GRPC_ERROR_NONE, emd, GPR_ARRAY_SIZE(emd));
grpc_jwt_encode_and_sign_set_override(encode_and_sign_jwt_with_scope_success);
run_request_metadata_test(creds, auth_md_ctx, state);
grpc_core::ExecCtx::Get()->Flush();
/* Second request: the cached token should be served directly. */
state =
make_request_metadata_state(GRPC_ERROR_NONE, emd, GPR_ARRAY_SIZE(emd));
grpc_jwt_encode_and_sign_set_override(
encode_and_sign_jwt_should_not_be_called);
run_request_metadata_test(creds, auth_md_ctx, state);
grpc_core::ExecCtx::Get()->Flush();
/* Third request: Different service url so jwt_encode_and_sign should be
called again (no caching). */
state =
make_request_metadata_state(GRPC_ERROR_NONE, emd, GPR_ARRAY_SIZE(emd));
auth_md_ctx.service_url = other_test_service_url;
grpc_jwt_encode_and_sign_set_override(encode_and_sign_jwt_with_scope_success);
run_request_metadata_test(creds, auth_md_ctx, state);
grpc_core::ExecCtx::Get()->Flush();
GPR_ASSERT(strncmp(expected_creds_debug_string_prefix,
creds->debug_string().c_str(),
strlen(expected_creds_debug_string_prefix)) == 0);
creds->Unref();
gpr_free(json_key_string);
grpc_jwt_encode_and_sign_set_override(nullptr);
}
static void test_jwt_creds_no_audience_success(void) {
const char expected_creds_debug_string_prefix[] =
"JWTAccessCredentials{ExpirationTime:";
char* json_key_string = test_json_key_str();
grpc_core::ExecCtx exec_ctx;
grpc_auth_metadata_context auth_md_ctx = {test_service_url, test_method,
nullptr, nullptr};
std::string expected_md_value = absl::StrCat("Bearer ", test_signed_jwt3);
expected_md emd[] = {{"authorization", expected_md_value.c_str()}};
grpc_call_credentials* creds =
grpc_service_account_jwt_access_credentials_create(
json_key_string, grpc_max_auth_token_lifetime(), test_scope,
1 /* should_clear_audience*/);
/* First request: jwt_encode_and_sign should be called. */
request_metadata_state* state =
make_request_metadata_state(GRPC_ERROR_NONE, emd, GPR_ARRAY_SIZE(emd));
grpc_jwt_encode_and_sign_set_override(encode_and_sign_jwt_with_scope_success);
run_request_metadata_test(creds, auth_md_ctx, state);
grpc_core::ExecCtx::Get()->Flush();
/* Second request: the cached token should be served directly. */
state =
make_request_metadata_state(GRPC_ERROR_NONE, emd, GPR_ARRAY_SIZE(emd));
grpc_jwt_encode_and_sign_set_override(
encode_and_sign_jwt_should_not_be_called);
run_request_metadata_test(creds, auth_md_ctx, state);
grpc_core::ExecCtx::Get()->Flush();
/* Third request: Different service url, but we still serve
the cached token because we only use scope and ignore audience. */
state =
make_request_metadata_state(GRPC_ERROR_NONE, emd, GPR_ARRAY_SIZE(emd));
auth_md_ctx.service_url = other_test_service_url;
grpc_jwt_encode_and_sign_set_override(
encode_and_sign_jwt_should_not_be_called);
run_request_metadata_test(creds, auth_md_ctx, state);
grpc_core::ExecCtx::Get()->Flush();
GPR_ASSERT(strncmp(expected_creds_debug_string_prefix,
creds->debug_string().c_str(),
strlen(expected_creds_debug_string_prefix)) == 0);
creds->Unref();
gpr_free(json_key_string);
grpc_jwt_encode_and_sign_set_override(nullptr);
}
static void test_jwt_creds_signing_failure(void) {
const char expected_creds_debug_string_prefix[] =
"JWTAccessCredentials{ExpirationTime:";
@ -1420,7 +1526,7 @@ static void test_jwt_creds_signing_failure(void) {
0);
grpc_call_credentials* creds =
grpc_service_account_jwt_access_credentials_create(
json_key_string, grpc_max_auth_token_lifetime(), nullptr);
json_key_string, grpc_max_auth_token_lifetime(), nullptr, 0);
grpc_jwt_encode_and_sign_set_override(encode_and_sign_jwt_failure);
run_request_metadata_test(creds, auth_md_ctx, state);
@ -1464,7 +1570,7 @@ static void test_google_default_creds_auth_key(void) {
"json_key_google_default_creds", json_key);
gpr_free(json_key);
creds = reinterpret_cast<grpc_composite_channel_credentials*>(
grpc_google_default_credentials_create(nullptr));
grpc_google_default_credentials_create(nullptr, nullptr));
auto* default_creds =
reinterpret_cast<const grpc_google_default_channel_credentials*>(
creds->inner_creds());
@ -1481,6 +1587,47 @@ static void test_google_default_creds_auth_key(void) {
gpr_setenv(GRPC_GOOGLE_CREDENTIALS_ENV_VAR, ""); /* Reset. */
}
static void test_google_default_creds_user_provided_scope(void) {
grpc_core::ExecCtx exec_ctx;
grpc_composite_channel_credentials* creds;
char* json_key = test_json_key_str();
grpc_flush_cached_google_default_credentials();
set_gce_tenancy_checker_for_testing(test_gce_tenancy_checker);
g_test_gce_tenancy_checker_called = false;
g_test_is_on_gce = true;
set_google_default_creds_env_var_with_file_contents(
"json_key_google_default_creds", json_key);
gpr_free(json_key);
creds = reinterpret_cast<grpc_composite_channel_credentials*>(
grpc_google_default_credentials_create(nullptr, test_scope));
auto* default_creds =
reinterpret_cast<const grpc_google_default_channel_credentials*>(
creds->inner_creds());
GPR_ASSERT(default_creds->ssl_creds() != nullptr);
grpc_auth_metadata_context auth_md_ctx = {test_service_url, test_method,
nullptr, nullptr};
std::string expected_md_value = absl::StrCat("Bearer ", test_signed_jwt3);
expected_md emd[] = {{"authorization", expected_md_value.c_str()}};
request_metadata_state* state =
make_request_metadata_state(GRPC_ERROR_NONE, emd, GPR_ARRAY_SIZE(emd));
grpc_jwt_encode_and_sign_set_override(encode_and_sign_jwt_with_scope_success);
run_request_metadata_test(
const_cast<grpc_call_credentials*>(creds->call_creds()), auth_md_ctx,
state);
grpc_core::ExecCtx::Get()->Flush();
auto* jwt =
reinterpret_cast<const grpc_service_account_jwt_access_credentials*>(
creds->call_creds());
GPR_ASSERT(
strcmp(jwt->key().client_id,
"777-abaslkan11hlb6nmim3bpspl31ud.apps.googleusercontent.com") ==
0);
GPR_ASSERT(g_test_gce_tenancy_checker_called == false);
creds->Unref();
gpr_setenv(GRPC_GOOGLE_CREDENTIALS_ENV_VAR, ""); /* Reset. */
}
static void test_google_default_creds_refresh_token(void) {
grpc_core::ExecCtx exec_ctx;
grpc_composite_channel_credentials* creds;
@ -1488,7 +1635,7 @@ static void test_google_default_creds_refresh_token(void) {
set_google_default_creds_env_var_with_file_contents(
"refresh_token_google_default_creds", test_refresh_token_str);
creds = reinterpret_cast<grpc_composite_channel_credentials*>(
grpc_google_default_credentials_create(nullptr));
grpc_google_default_credentials_create(nullptr, nullptr));
auto* default_creds =
reinterpret_cast<const grpc_google_default_channel_credentials*>(
creds->inner_creds());
@ -1539,7 +1686,7 @@ static void test_google_default_creds_gce(void) {
/* Simulate a successful detection of GCE. */
grpc_composite_channel_credentials* creds =
reinterpret_cast<grpc_composite_channel_credentials*>(
grpc_google_default_credentials_create(nullptr));
grpc_google_default_credentials_create(nullptr, nullptr));
/* Verify that the default creds actually embeds a GCE creds. */
GPR_ASSERT(creds != nullptr);
@ -1578,7 +1725,7 @@ static void test_google_default_creds_non_gce(void) {
httpcli_post_should_not_be_called);
grpc_composite_channel_credentials* creds =
reinterpret_cast<grpc_composite_channel_credentials*>(
grpc_google_default_credentials_create(nullptr));
grpc_google_default_credentials_create(nullptr, nullptr));
/* Verify that the default creds actually embeds a GCE creds. */
GPR_ASSERT(creds != nullptr);
GPR_ASSERT(creds->call_creds() != nullptr);
@ -1616,10 +1763,12 @@ static void test_no_google_default_creds(void) {
default_creds_gce_detection_httpcli_get_failure_override,
httpcli_post_should_not_be_called);
/* Simulate a successful detection of GCE. */
GPR_ASSERT(grpc_google_default_credentials_create(nullptr) == nullptr);
GPR_ASSERT(grpc_google_default_credentials_create(nullptr, nullptr) ==
nullptr);
/* Try a second one. GCE detection should occur again. */
g_test_gce_tenancy_checker_called = false;
GPR_ASSERT(grpc_google_default_credentials_create(nullptr) == nullptr);
GPR_ASSERT(grpc_google_default_credentials_create(nullptr, nullptr) ==
nullptr);
GPR_ASSERT(g_test_gce_tenancy_checker_called == true);
/* Cleanup. */
grpc_override_well_known_credentials_path_getter(nullptr);
@ -1645,7 +1794,7 @@ static void test_google_default_creds_call_creds_specified(void) {
httpcli_post_should_not_be_called);
grpc_composite_channel_credentials* channel_creds =
reinterpret_cast<grpc_composite_channel_credentials*>(
grpc_google_default_credentials_create(call_creds));
grpc_google_default_credentials_create(call_creds, nullptr));
GPR_ASSERT(g_test_gce_tenancy_checker_called == false);
GPR_ASSERT(channel_creds != nullptr);
GPR_ASSERT(channel_creds->call_creds() != nullptr);
@ -1704,7 +1853,8 @@ static void test_google_default_creds_not_default(void) {
httpcli_post_should_not_be_called);
grpc_composite_channel_credentials* channel_creds =
reinterpret_cast<grpc_composite_channel_credentials*>(
grpc_google_default_credentials_create(call_creds.release()));
grpc_google_default_credentials_create(call_creds.release(),
nullptr));
GPR_ASSERT(g_test_gce_tenancy_checker_called == false);
GPR_ASSERT(channel_creds != nullptr);
GPR_ASSERT(channel_creds->call_creds() != nullptr);
@ -3390,6 +3540,8 @@ int main(int argc, char** argv) {
test_sts_creds_token_file_not_found();
test_jwt_creds_lifetime();
test_jwt_creds_success();
test_jwt_creds_with_scope_success();
test_jwt_creds_no_audience_success();
test_jwt_creds_signing_failure();
test_google_default_creds_auth_key();
test_google_default_creds_refresh_token();
@ -3398,6 +3550,7 @@ int main(int argc, char** argv) {
test_no_google_default_creds();
test_google_default_creds_call_creds_specified();
test_google_default_creds_not_default();
test_google_default_creds_user_provided_scope();
test_metadata_plugin_success();
test_metadata_plugin_failure();
test_get_well_known_google_credentials_file_path();

@ -249,7 +249,6 @@ static void check_jwt_claim(const Json& claim, const char* expected_audience,
GPR_ASSERT(value.type() == Json::Type::STRING);
GPR_ASSERT(value.string_value() ==
"777-abaslkan11hlb6nmim3bpspl31ud@developer.gserviceaccount.com");
if (expected_scope != nullptr) {
GPR_ASSERT(object.find("sub") == object.end());
value = object["scope"];
@ -263,9 +262,13 @@ static void check_jwt_claim(const Json& claim, const char* expected_audience,
GPR_ASSERT(value.string_value() == object["iss"].string_value());
}
value = object["aud"];
GPR_ASSERT(value.type() == Json::Type::STRING);
GPR_ASSERT(value.string_value() == expected_audience);
if (expected_audience != nullptr) {
value = object["aud"];
GPR_ASSERT(value.type() == Json::Type::STRING);
GPR_ASSERT(value.string_value() == expected_audience);
} else {
GPR_ASSERT(object.find("aud") == object.end());
}
gpr_timespec expiration = gpr_time_0(GPR_CLOCK_REALTIME);
value = object["exp"];
@ -312,18 +315,31 @@ static void check_jwt_signature(const char* b64_signature, RSA* rsa_key,
static char* service_account_creds_jwt_encode_and_sign(
const grpc_auth_json_key* key) {
return grpc_jwt_encode_and_sign(key, GRPC_JWT_OAUTH2_AUDIENCE,
grpc_max_auth_token_lifetime(), test_scope);
grpc_max_auth_token_lifetime(), test_scope,
false);
}
static char* service_account_creds_no_audience_jwt_encode_and_sign(
const grpc_auth_json_key* key) {
return grpc_jwt_encode_and_sign(key, GRPC_JWT_OAUTH2_AUDIENCE,
grpc_max_auth_token_lifetime(), test_scope,
true);
}
static char* jwt_creds_jwt_encode_and_sign(const grpc_auth_json_key* key) {
return grpc_jwt_encode_and_sign(key, test_service_url,
grpc_max_auth_token_lifetime(), nullptr);
return grpc_jwt_encode_and_sign(
key, test_service_url, grpc_max_auth_token_lifetime(), nullptr, false);
}
static void service_account_creds_check_jwt_claim(const Json& claim) {
check_jwt_claim(claim, GRPC_JWT_OAUTH2_AUDIENCE, test_scope);
}
static void service_account_creds_no_audience_check_jwt_claim(
const Json& claim) {
check_jwt_claim(claim, nullptr, test_scope);
}
static void jwt_creds_check_jwt_claim(const Json& claim) {
check_jwt_claim(claim, test_service_url, nullptr);
}
@ -368,6 +384,12 @@ static void test_service_account_creds_jwt_encode_and_sign(void) {
service_account_creds_check_jwt_claim);
}
static void test_service_account_creds_no_audience_jwt_encode_and_sign(void) {
test_jwt_encode_and_sign(
service_account_creds_no_audience_jwt_encode_and_sign,
service_account_creds_no_audience_check_jwt_claim);
}
static void test_jwt_creds_jwt_encode_and_sign(void) {
test_jwt_encode_and_sign(jwt_creds_jwt_encode_and_sign,
jwt_creds_check_jwt_claim);
@ -442,6 +464,7 @@ int main(int argc, char** argv) {
test_parse_json_key_failure_no_private_key_id();
test_parse_json_key_failure_no_private_key();
test_service_account_creds_jwt_encode_and_sign();
test_service_account_creds_no_audience_jwt_encode_and_sign();
test_jwt_creds_jwt_encode_and_sign();
test_parse_refresh_token_success();
test_parse_refresh_token_failure_no_type();

@ -383,7 +383,7 @@ static void test_jwt_verifier_google_email_issuer_success(void) {
grpc_httpcli_set_override(httpcli_get_google_keys_for_email,
httpcli_post_should_not_be_called);
jwt = grpc_jwt_encode_and_sign(&key, expected_audience, expected_lifetime,
nullptr);
nullptr, false);
grpc_auth_json_key_destruct(&key);
GPR_ASSERT(jwt != nullptr);
grpc_jwt_verifier_verify(verifier, nullptr, jwt, expected_audience,
@ -417,7 +417,7 @@ static void test_jwt_verifier_custom_email_issuer_success(void) {
grpc_httpcli_set_override(httpcli_get_custom_keys_for_email,
httpcli_post_should_not_be_called);
jwt = grpc_jwt_encode_and_sign(&key, expected_audience, expected_lifetime,
nullptr);
nullptr, false);
grpc_auth_json_key_destruct(&key);
GPR_ASSERT(jwt != nullptr);
grpc_jwt_verifier_verify(verifier, nullptr, jwt, expected_audience,
@ -465,7 +465,7 @@ static void test_jwt_verifier_url_issuer_success(void) {
grpc_httpcli_set_override(httpcli_get_openid_config,
httpcli_post_should_not_be_called);
jwt = grpc_jwt_encode_and_sign(&key, expected_audience, expected_lifetime,
nullptr);
nullptr, false);
grpc_auth_json_key_destruct(&key);
GPR_ASSERT(jwt != nullptr);
grpc_jwt_verifier_verify(verifier, nullptr, jwt, expected_audience,
@ -505,7 +505,7 @@ static void test_jwt_verifier_url_issuer_bad_config(void) {
grpc_httpcli_set_override(httpcli_get_bad_json,
httpcli_post_should_not_be_called);
jwt = grpc_jwt_encode_and_sign(&key, expected_audience, expected_lifetime,
nullptr);
nullptr, false);
grpc_auth_json_key_destruct(&key);
GPR_ASSERT(jwt != nullptr);
grpc_jwt_verifier_verify(verifier, nullptr, jwt, expected_audience,
@ -528,7 +528,7 @@ static void test_jwt_verifier_bad_json_key(void) {
grpc_httpcli_set_override(httpcli_get_bad_json,
httpcli_post_should_not_be_called);
jwt = grpc_jwt_encode_and_sign(&key, expected_audience, expected_lifetime,
nullptr);
nullptr, false);
grpc_auth_json_key_destruct(&key);
GPR_ASSERT(jwt != nullptr);
grpc_jwt_verifier_verify(verifier, nullptr, jwt, expected_audience,
@ -579,7 +579,7 @@ static void test_jwt_verifier_bad_signature(void) {
grpc_httpcli_set_override(httpcli_get_openid_config,
httpcli_post_should_not_be_called);
jwt = grpc_jwt_encode_and_sign(&key, expected_audience, expected_lifetime,
nullptr);
nullptr, false);
grpc_auth_json_key_destruct(&key);
corrupt_jwt_sig(jwt);
GPR_ASSERT(jwt != nullptr);

Loading…
Cancel
Save