From 6df967966b660fba8c802fcf5bf2adfd7fb3f393 Mon Sep 17 00:00:00 2001 From: yihuaz Date: Thu, 1 Jul 2021 10:26:33 -0700 Subject: [PATCH] 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 --- include/grpc/grpc_security.h | 23 ++- include/grpcpp/security/credentials.h | 18 +- src/core/ext/xds/xds_bootstrap.cc | 2 +- .../google_default_credentials.cc | 18 +- .../security/credentials/jwt/json_token.cc | 15 +- .../lib/security/credentials/jwt/json_token.h | 7 +- .../credentials/jwt/jwt_credentials.cc | 39 ++-- .../credentials/jwt/jwt_credentials.h | 13 +- src/cpp/client/secure_credentials.cc | 14 +- src/php/ext/grpc/channel_credentials.c | 2 +- .../grpc/_cython/_cygrpc/credentials.pyx.pxi | 2 +- .../grpcio/grpc/_cython/_cygrpc/grpc.pxi | 4 +- src/ruby/ext/grpc/rb_grpc_imports.generated.h | 4 +- test/core/security/create_jwt.cc | 2 +- test/core/security/credentials_test.cc | 179 ++++++++++++++++-- test/core/security/json_token_test.cc | 37 +++- test/core/security/jwt_verifier_test.cc | 12 +- 17 files changed, 318 insertions(+), 73 deletions(-) diff --git a/include/grpc/grpc_security.h b/include/grpc/grpc_security.h index 9edd70f39d76b..c116a7a585468 100644 --- a/include/grpc/grpc_security.h +++ b/include/grpc/grpc_security.h @@ -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. diff --git a/include/grpcpp/security/credentials.h b/include/grpcpp/security/credentials.h index 1b1f994c1a9d5..8516d62567d58 100644 --- a/include/grpcpp/security/credentials.h +++ b/include/grpcpp/security/credentials.h @@ -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 GoogleDefaultCredentials(); +std::shared_ptr GoogleDefaultCredentials( + const grpc::string& user_provided_scope = ""); /// Builds SSL Credentials given SSL specific options std::shared_ptr 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 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 diff --git a/src/core/ext/xds/xds_bootstrap.cc b/src/core/ext/xds/xds_bootstrap.cc index 33cf276d2c1f6..acbe6d74f91bc 100644 --- a/src/core/ext/xds/xds_bootstrap.cc +++ b/src/core/ext/xds/xds_bootstrap.cc @@ -59,7 +59,7 @@ RefCountedPtr 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") { diff --git a/src/core/lib/security/credentials/google_default/google_default_credentials.cc b/src/core/lib/security/credentials/google_default/google_default_credentials.cc index 93fdd5323e137..483a68cb74a99 100644 --- a/src/core/lib/security/credentials/google_default/google_default_credentials.cc +++ b/src/core/lib/security/credentials/google_default/google_default_credentials.cc @@ -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* 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 make_default_call_creds( - grpc_error_handle* error) { + const char* user_provided_scope, grpc_error_handle* error) { grpc_core::RefCountedPtr 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 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 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 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) { diff --git a/src/core/lib/security/credentials/jwt/json_token.cc b/src/core/lib/security/credentials/jwt/json_token.cc index 5e317c490a5e8..5ec5876f56f20 100644 --- a/src/core/lib/security/credentials/jwt/json_token.cc +++ b/src/core/lib/security/credentials/jwt/json_token.cc @@ -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 @@ char* compute_and_encode_signature(const grpc_auth_json_key* json_key, 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); diff --git a/src/core/lib/security/credentials/jwt/json_token.h b/src/core/lib/security/credentials/jwt/json_token.h index b9a41c6b2b628..7e312d3621e3a 100644 --- a/src/core/lib/security/credentials/jwt/json_token.h +++ b/src/core/lib/security/credentials/jwt/json_token.h @@ -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)( diff --git a/src/core/lib/security/credentials/jwt/jwt_credentials.cc b/src/core/lib/security/credentials/jwt/jwt_credentials.cc index 27590f4baf5b8..94b3fd6c951d4 100644 --- a/src/core/lib/security/credentials/jwt/jwt_credentials.cc +++ b/src/core/lib/security/credentials/jwt/jwt_credentials.cc @@ -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_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( - 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(token_lifetime.clock_type), reserved); + static_cast(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(); } diff --git a/src/core/lib/security/credentials/jwt/jwt_credentials.h b/src/core/lib/security/credentials/jwt/jwt_credentials.h index 5ae4c1f41fd0e..0d3e378e9630e 100644 --- a/src/core/lib/security/credentials/jwt/jwt_credentials.h +++ b/src/core/lib/security/credentials/jwt/jwt_credentials.h @@ -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_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 */ diff --git a/src/cpp/client/secure_credentials.cc b/src/cpp/client/secure_credentials.cc index e743165c51854..58e8c6cf449a1 100644 --- a/src/cpp/client/secure_credentials.cc +++ b/src/cpp/client/secure_credentials.cc @@ -103,10 +103,13 @@ std::shared_ptr WrapCallCredentials( } } // namespace -std::shared_ptr GoogleDefaultCredentials() { +std::shared_ptr 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 ExternalAccountCredentials( @@ -320,7 +323,8 @@ std::shared_ptr GoogleComputeEngineCredentials() { // Builds JWT credentials. std::shared_ptr 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 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. diff --git a/src/php/ext/grpc/channel_credentials.c b/src/php/ext/grpc/channel_credentials.c index 34b4c826085e9..ddf0881d0f220 100644 --- a/src/php/ext/grpc/channel_credentials.c +++ b/src/php/ext/grpc/channel_credentials.c @@ -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); diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi index 23de3a0b188c3..3d3804f985c0e 100644 --- a/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi +++ b/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi @@ -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 diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi index 6698dd6538f81..39c51f2f624c0 100644 --- a/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi +++ b/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi @@ -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( diff --git a/src/ruby/ext/grpc/rb_grpc_imports.generated.h b/src/ruby/ext/grpc/rb_grpc_imports.generated.h index 33c636de86b6f..dbaa40e7f185a 100644 --- a/src/ruby/ext/grpc/rb_grpc_imports.generated.h +++ b/src/ruby/ext/grpc/rb_grpc_imports.generated.h @@ -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); diff --git a/test/core/security/create_jwt.cc b/test/core/security/create_jwt.cc index 2ea640b605ea2..f0c808563b2b1 100644 --- a/test/core/security/create_jwt.cc +++ b/test/core/security/create_jwt.cc @@ -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"); diff --git a/test/core/security/credentials_test.cc b/test/core/security/credentials_test.cc index 0533247a18218..e9900e7e8b9eb 100644 --- a/test/core/security/credentials_test.cc +++ b/test/core/security/credentials_test.cc @@ -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_google_default_credentials_create(nullptr)); + grpc_google_default_credentials_create(nullptr, nullptr)); auto* default_creds = reinterpret_cast( 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_google_default_credentials_create(nullptr, test_scope)); + auto* default_creds = + reinterpret_cast( + 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(creds->call_creds()), auth_md_ctx, + state); + grpc_core::ExecCtx::Get()->Flush(); + auto* jwt = + reinterpret_cast( + 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_google_default_credentials_create(nullptr)); + grpc_google_default_credentials_create(nullptr, nullptr)); auto* default_creds = reinterpret_cast( 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_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_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_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_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(); diff --git a/test/core/security/json_token_test.cc b/test/core/security/json_token_test.cc index 1ded259b7f490..1235f10dbd94a 100644 --- a/test/core/security/json_token_test.cc +++ b/test/core/security/json_token_test.cc @@ -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(); diff --git a/test/core/security/jwt_verifier_test.cc b/test/core/security/jwt_verifier_test.cc index 21ed63fcfd144..0aff29b2795f1 100644 --- a/test/core/security/jwt_verifier_test.cc +++ b/test/core/security/jwt_verifier_test.cc @@ -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);