1 /*
2 *
3 * Copyright 2015 gRPC authors.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 */
18
19 #include "src/core/lib/security/credentials/jwt/json_token.h"
20
21 #include <openssl/evp.h>
22 #include <string.h>
23
24 #include <grpc/grpc_security.h>
25 #include <grpc/slice.h>
26 #include <grpc/support/alloc.h>
27 #include <grpc/support/log.h>
28
29 #include "src/core/lib/json/json.h"
30 #include "src/core/lib/security/credentials/oauth2/oauth2_credentials.h"
31 #include "src/core/lib/slice/b64.h"
32 #include "src/core/lib/slice/slice_internal.h"
33 #include "test/core/util/test_config.h"
34
35 using grpc_core::Json;
36
37 /* This JSON key was generated with the GCE console and revoked immediately.
38 The identifiers have been changed as well.
39 Maximum size for a string literal is 509 chars in C89, yay! */
40 static const char test_json_key_str_part1[] =
41 "{ \"private_key\": \"-----BEGIN PRIVATE KEY-----"
42 "\\nMIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAOEvJsnoHnyHkXcp\\n7mJE"
43 "qg"
44 "WGjiw71NfXByguekSKho65FxaGbsnSM9SMQAqVk7Q2rG+I0OpsT0LrWQtZ\\nyjSeg/"
45 "rWBQvS4hle4LfijkP3J5BG+"
46 "IXDMP8RfziNRQsenAXDNPkY4kJCvKux2xdD\\nOnVF6N7dL3nTYZg+"
47 "uQrNsMTz9UxVAgMBAAECgYEAzbLewe1xe9vy+2GoSsfib+28\\nDZgSE6Bu/"
48 "zuFoPrRc6qL9p2SsnV7txrunTyJkkOnPLND9ABAXybRTlcVKP/sGgza\\n/"
49 "8HpCqFYM9V8f34SBWfD4fRFT+n/"
50 "73cfRUtGXdXpseva2lh8RilIQfPhNZAncenU\\ngqXjDvpkypEusgXAykECQQD+";
51 static const char test_json_key_str_part2[] =
52 "53XxNVnxBHsYb+AYEfklR96yVi8HywjVHP34+OQZ\\nCslxoHQM8s+"
53 "dBnjfScLu22JqkPv04xyxmt0QAKm9+vTdAkEA4ib7YvEAn2jXzcCI\\nEkoy2L/"
54 "XydR1GCHoacdfdAwiL2npOdnbvi4ZmdYRPY1LSTO058tQHKVXV7NLeCa3\\nAARh2QJBAMKeDA"
55 "G"
56 "W303SQv2cZTdbeaLKJbB5drz3eo3j7dDKjrTD9JupixFbzcGw\\n8FZi5c8idxiwC36kbAL6Hz"
57 "A"
58 "ZoX+ofI0CQE6KCzPJTtYNqyShgKAZdJ8hwOcvCZtf\\n6z8RJm0+"
59 "6YBd38lfh5j8mZd7aHFf6I17j5AQY7oPEc47TjJj/"
60 "5nZ68ECQQDvYuI3\\nLyK5fS8g0SYbmPOL9TlcHDOqwG0mrX9qpg5DC2fniXNSrrZ64GTDKdzZ"
61 "Y"
62 "Ap6LI9W\\nIqv4vr6y38N79TTC\\n-----END PRIVATE KEY-----\\n\", ";
63 static const char test_json_key_str_part3[] =
64 "\"private_key_id\": \"e6b5137873db8d2ef81e06a47289e6434ec8a165\", "
65 "\"client_email\": "
66 "\"777-abaslkan11hlb6nmim3bpspl31ud@developer.gserviceaccount."
67 "com\", \"client_id\": "
68 "\"777-abaslkan11hlb6nmim3bpspl31ud.apps.googleusercontent."
69 "com\", \"type\": \"service_account\" }";
70
71 /* Test refresh token. */
72 static const char test_refresh_token_str[] =
73 "{ \"client_id\": \"32555999999.apps.googleusercontent.com\","
74 " \"client_secret\": \"EmssLNjJy1332hD4KFsecret\","
75 " \"refresh_token\": \"1/Blahblasj424jladJDSGNf-u4Sua3HDA2ngjd42\","
76 " \"type\": \"authorized_user\"}";
77
78 static const char test_scope[] = "myperm1 myperm2";
79
80 static const char test_service_url[] = "https://foo.com/foo.v1";
81
test_json_key_str(const char * bad_part3)82 static char* test_json_key_str(const char* bad_part3) {
83 const char* part3 =
84 bad_part3 != nullptr ? bad_part3 : test_json_key_str_part3;
85 size_t result_len = strlen(test_json_key_str_part1) +
86 strlen(test_json_key_str_part2) + strlen(part3);
87 char* result = static_cast<char*>(gpr_malloc(result_len + 1));
88 char* current = result;
89 strcpy(result, test_json_key_str_part1);
90 current += strlen(test_json_key_str_part1);
91 strcpy(current, test_json_key_str_part2);
92 current += strlen(test_json_key_str_part2);
93 strcpy(current, part3);
94 return result;
95 }
96
test_parse_json_key_success(void)97 static void test_parse_json_key_success(void) {
98 char* json_string = test_json_key_str(nullptr);
99 grpc_auth_json_key json_key =
100 grpc_auth_json_key_create_from_string(json_string);
101 GPR_ASSERT(grpc_auth_json_key_is_valid(&json_key));
102 GPR_ASSERT(json_key.type != nullptr &&
103 strcmp(json_key.type, "service_account") == 0);
104 GPR_ASSERT(json_key.private_key_id != nullptr &&
105 strcmp(json_key.private_key_id,
106 "e6b5137873db8d2ef81e06a47289e6434ec8a165") == 0);
107 GPR_ASSERT(json_key.client_id != nullptr &&
108 strcmp(json_key.client_id,
109 "777-abaslkan11hlb6nmim3bpspl31ud.apps."
110 "googleusercontent.com") == 0);
111 GPR_ASSERT(json_key.client_email != nullptr &&
112 strcmp(json_key.client_email,
113 "777-abaslkan11hlb6nmim3bpspl31ud@developer."
114 "gserviceaccount.com") == 0);
115 GPR_ASSERT(json_key.private_key != nullptr);
116 gpr_free(json_string);
117 grpc_auth_json_key_destruct(&json_key);
118 }
119
test_parse_json_key_failure_bad_json(void)120 static void test_parse_json_key_failure_bad_json(void) {
121 const char non_closing_part3[] =
122 "\"private_key_id\": \"e6b5137873db8d2ef81e06a47289e6434ec8a165\", "
123 "\"client_email\": "
124 "\"777-abaslkan11hlb6nmim3bpspl31ud@developer.gserviceaccount."
125 "com\", \"client_id\": "
126 "\"777-abaslkan11hlb6nmim3bpspl31ud.apps.googleusercontent."
127 "com\", \"type\": \"service_account\" ";
128 char* json_string = test_json_key_str(non_closing_part3);
129 grpc_auth_json_key json_key =
130 grpc_auth_json_key_create_from_string(json_string);
131 GPR_ASSERT(!grpc_auth_json_key_is_valid(&json_key));
132 gpr_free(json_string);
133 grpc_auth_json_key_destruct(&json_key);
134 }
135
test_parse_json_key_failure_no_type(void)136 static void test_parse_json_key_failure_no_type(void) {
137 const char no_type_part3[] =
138 "\"private_key_id\": \"e6b5137873db8d2ef81e06a47289e6434ec8a165\", "
139 "\"client_email\": "
140 "\"777-abaslkan11hlb6nmim3bpspl31ud@developer.gserviceaccount."
141 "com\", \"client_id\": "
142 "\"777-abaslkan11hlb6nmim3bpspl31ud.apps.googleusercontent."
143 "com\" }";
144 char* json_string = test_json_key_str(no_type_part3);
145 grpc_auth_json_key json_key =
146 grpc_auth_json_key_create_from_string(json_string);
147 GPR_ASSERT(!grpc_auth_json_key_is_valid(&json_key));
148 gpr_free(json_string);
149 grpc_auth_json_key_destruct(&json_key);
150 }
151
test_parse_json_key_failure_no_client_id(void)152 static void test_parse_json_key_failure_no_client_id(void) {
153 const char no_client_id_part3[] =
154 "\"private_key_id\": \"e6b5137873db8d2ef81e06a47289e6434ec8a165\", "
155 "\"client_email\": "
156 "\"777-abaslkan11hlb6nmim3bpspl31ud@developer.gserviceaccount."
157 "com\", "
158 "\"type\": \"service_account\" }";
159 char* json_string = test_json_key_str(no_client_id_part3);
160 grpc_auth_json_key json_key =
161 grpc_auth_json_key_create_from_string(json_string);
162 GPR_ASSERT(!grpc_auth_json_key_is_valid(&json_key));
163 gpr_free(json_string);
164 grpc_auth_json_key_destruct(&json_key);
165 }
166
test_parse_json_key_failure_no_client_email(void)167 static void test_parse_json_key_failure_no_client_email(void) {
168 const char no_client_email_part3[] =
169 "\"private_key_id\": \"e6b5137873db8d2ef81e06a47289e6434ec8a165\", "
170 "\"client_id\": "
171 "\"777-abaslkan11hlb6nmim3bpspl31ud.apps.googleusercontent."
172 "com\", \"type\": \"service_account\" }";
173 char* json_string = test_json_key_str(no_client_email_part3);
174 grpc_auth_json_key json_key =
175 grpc_auth_json_key_create_from_string(json_string);
176 GPR_ASSERT(!grpc_auth_json_key_is_valid(&json_key));
177 gpr_free(json_string);
178 grpc_auth_json_key_destruct(&json_key);
179 }
180
test_parse_json_key_failure_no_private_key_id(void)181 static void test_parse_json_key_failure_no_private_key_id(void) {
182 const char no_private_key_id_part3[] =
183 "\"client_email\": "
184 "\"777-abaslkan11hlb6nmim3bpspl31ud@developer.gserviceaccount."
185 "com\", \"client_id\": "
186 "\"777-abaslkan11hlb6nmim3bpspl31ud.apps.googleusercontent."
187 "com\", \"type\": \"service_account\" }";
188 char* json_string = test_json_key_str(no_private_key_id_part3);
189 grpc_auth_json_key json_key =
190 grpc_auth_json_key_create_from_string(json_string);
191 GPR_ASSERT(!grpc_auth_json_key_is_valid(&json_key));
192 gpr_free(json_string);
193 grpc_auth_json_key_destruct(&json_key);
194 }
195
test_parse_json_key_failure_no_private_key(void)196 static void test_parse_json_key_failure_no_private_key(void) {
197 const char no_private_key_json_string[] =
198 "{ \"private_key_id\": \"e6b5137873db8d2ef81e06a47289e6434ec8a165\", "
199 "\"client_email\": "
200 "\"777-abaslkan11hlb6nmim3bpspl31ud@developer.gserviceaccount."
201 "com\", \"client_id\": "
202 "\"777-abaslkan11hlb6nmim3bpspl31ud.apps.googleusercontent."
203 "com\", \"type\": \"service_account\" }";
204 grpc_auth_json_key json_key =
205 grpc_auth_json_key_create_from_string(no_private_key_json_string);
206 GPR_ASSERT(!grpc_auth_json_key_is_valid(&json_key));
207 grpc_auth_json_key_destruct(&json_key);
208 }
209
parse_json_part_from_jwt(const char * str,size_t len)210 static Json parse_json_part_from_jwt(const char* str, size_t len) {
211 grpc_core::ExecCtx exec_ctx;
212 char* b64 = static_cast<char*>(gpr_malloc(len + 1));
213 strncpy(b64, str, len);
214 b64[len] = '\0';
215 grpc_slice slice = grpc_base64_decode(b64, 1);
216 gpr_free(b64);
217 GPR_ASSERT(!GRPC_SLICE_IS_EMPTY(slice));
218 grpc_error* error = GRPC_ERROR_NONE;
219 absl::string_view string = grpc_core::StringViewFromSlice(slice);
220 Json json = Json::Parse(string, &error);
221 if (error != GRPC_ERROR_NONE) {
222 gpr_log(GPR_ERROR, "JSON parse error: %s", grpc_error_string(error));
223 GRPC_ERROR_UNREF(error);
224 }
225 grpc_slice_unref(slice);
226 return json;
227 }
228
check_jwt_header(const Json & header)229 static void check_jwt_header(const Json& header) {
230 Json::Object object = header.object_value();
231 Json value = object["alg"];
232 GPR_ASSERT(value.type() == Json::Type::STRING);
233 GPR_ASSERT(strcmp(value.string_value().c_str(), "RS256") == 0);
234 value = object["typ"];
235 GPR_ASSERT(value.type() == Json::Type::STRING);
236 GPR_ASSERT(strcmp(value.string_value().c_str(), "JWT") == 0);
237 value = object["kid"];
238 GPR_ASSERT(value.type() == Json::Type::STRING);
239 GPR_ASSERT(strcmp(value.string_value().c_str(),
240 "e6b5137873db8d2ef81e06a47289e6434ec8a165") == 0);
241 }
242
check_jwt_claim(const Json & claim,const char * expected_audience,const char * expected_scope)243 static void check_jwt_claim(const Json& claim, const char* expected_audience,
244 const char* expected_scope) {
245 Json::Object object = claim.object_value();
246
247 Json value = object["iss"];
248 GPR_ASSERT(value.type() == Json::Type::STRING);
249 GPR_ASSERT(value.string_value() ==
250 "777-abaslkan11hlb6nmim3bpspl31ud@developer.gserviceaccount.com");
251
252 if (expected_scope != nullptr) {
253 GPR_ASSERT(object.find("sub") == object.end());
254 value = object["scope"];
255 GPR_ASSERT(value.type() == Json::Type::STRING);
256 GPR_ASSERT(value.string_value() == expected_scope);
257 } else {
258 /* Claims without scope must have a sub. */
259 GPR_ASSERT(object.find("scope") == object.end());
260 value = object["sub"];
261 GPR_ASSERT(value.type() == Json::Type::STRING);
262 GPR_ASSERT(value.string_value() == object["iss"].string_value());
263 }
264
265 value = object["aud"];
266 GPR_ASSERT(value.type() == Json::Type::STRING);
267 GPR_ASSERT(value.string_value() == expected_audience);
268
269 gpr_timespec expiration = gpr_time_0(GPR_CLOCK_REALTIME);
270 value = object["exp"];
271 GPR_ASSERT(value.type() == Json::Type::NUMBER);
272 expiration.tv_sec = strtol(value.string_value().c_str(), nullptr, 10);
273
274 gpr_timespec issue_time = gpr_time_0(GPR_CLOCK_REALTIME);
275 value = object["iat"];
276 GPR_ASSERT(value.type() == Json::Type::NUMBER);
277 issue_time.tv_sec = strtol(value.string_value().c_str(), nullptr, 10);
278
279 gpr_timespec parsed_lifetime = gpr_time_sub(expiration, issue_time);
280 GPR_ASSERT(parsed_lifetime.tv_sec == grpc_max_auth_token_lifetime().tv_sec);
281 }
282
check_jwt_signature(const char * b64_signature,RSA * rsa_key,const char * signed_data,size_t signed_data_size)283 static void check_jwt_signature(const char* b64_signature, RSA* rsa_key,
284 const char* signed_data,
285 size_t signed_data_size) {
286 grpc_core::ExecCtx exec_ctx;
287
288 EVP_MD_CTX* md_ctx = EVP_MD_CTX_create();
289 EVP_PKEY* key = EVP_PKEY_new();
290
291 grpc_slice sig = grpc_base64_decode(b64_signature, 1);
292 GPR_ASSERT(!GRPC_SLICE_IS_EMPTY(sig));
293 GPR_ASSERT(GRPC_SLICE_LENGTH(sig) == 128);
294
295 GPR_ASSERT(md_ctx != nullptr);
296 GPR_ASSERT(key != nullptr);
297 EVP_PKEY_set1_RSA(key, rsa_key);
298
299 GPR_ASSERT(
300 EVP_DigestVerifyInit(md_ctx, nullptr, EVP_sha256(), nullptr, key) == 1);
301 GPR_ASSERT(EVP_DigestVerifyUpdate(md_ctx, signed_data, signed_data_size) ==
302 1);
303 GPR_ASSERT(EVP_DigestVerifyFinal(md_ctx, GRPC_SLICE_START_PTR(sig),
304 GRPC_SLICE_LENGTH(sig)) == 1);
305
306 grpc_slice_unref_internal(sig);
307 if (key != nullptr) EVP_PKEY_free(key);
308 if (md_ctx != nullptr) EVP_MD_CTX_destroy(md_ctx);
309 }
310
service_account_creds_jwt_encode_and_sign(const grpc_auth_json_key * key)311 static char* service_account_creds_jwt_encode_and_sign(
312 const grpc_auth_json_key* key) {
313 return grpc_jwt_encode_and_sign(key, GRPC_JWT_OAUTH2_AUDIENCE,
314 grpc_max_auth_token_lifetime(), test_scope);
315 }
316
jwt_creds_jwt_encode_and_sign(const grpc_auth_json_key * key)317 static char* jwt_creds_jwt_encode_and_sign(const grpc_auth_json_key* key) {
318 return grpc_jwt_encode_and_sign(key, test_service_url,
319 grpc_max_auth_token_lifetime(), nullptr);
320 }
321
service_account_creds_check_jwt_claim(const Json & claim)322 static void service_account_creds_check_jwt_claim(const Json& claim) {
323 check_jwt_claim(claim, GRPC_JWT_OAUTH2_AUDIENCE, test_scope);
324 }
325
jwt_creds_check_jwt_claim(const Json & claim)326 static void jwt_creds_check_jwt_claim(const Json& claim) {
327 check_jwt_claim(claim, test_service_url, nullptr);
328 }
329
test_jwt_encode_and_sign(char * (* jwt_encode_and_sign_func)(const grpc_auth_json_key *),void (* check_jwt_claim_func)(const Json &))330 static void test_jwt_encode_and_sign(
331 char* (*jwt_encode_and_sign_func)(const grpc_auth_json_key*),
332 void (*check_jwt_claim_func)(const Json&)) {
333 char* json_string = test_json_key_str(nullptr);
334 grpc_auth_json_key json_key =
335 grpc_auth_json_key_create_from_string(json_string);
336 const char* b64_signature;
337 size_t offset = 0;
338 char* jwt = jwt_encode_and_sign_func(&json_key);
339 const char* dot = strchr(jwt, '.');
340 GPR_ASSERT(dot != nullptr);
341 Json parsed_header =
342 parse_json_part_from_jwt(jwt, static_cast<size_t>(dot - jwt));
343 GPR_ASSERT(parsed_header.type() == Json::Type::OBJECT);
344 check_jwt_header(parsed_header);
345 offset = static_cast<size_t>(dot - jwt) + 1;
346
347 dot = strchr(jwt + offset, '.');
348 GPR_ASSERT(dot != nullptr);
349 Json parsed_claim = parse_json_part_from_jwt(
350 jwt + offset, static_cast<size_t>(dot - (jwt + offset)));
351 GPR_ASSERT(parsed_claim.type() == Json::Type::OBJECT);
352 check_jwt_claim_func(parsed_claim);
353 offset = static_cast<size_t>(dot - jwt) + 1;
354
355 dot = strchr(jwt + offset, '.');
356 GPR_ASSERT(dot == nullptr); /* no more part. */
357 b64_signature = jwt + offset;
358 check_jwt_signature(b64_signature, json_key.private_key, jwt, offset - 1);
359
360 gpr_free(json_string);
361 grpc_auth_json_key_destruct(&json_key);
362 gpr_free(jwt);
363 }
364
test_service_account_creds_jwt_encode_and_sign(void)365 static void test_service_account_creds_jwt_encode_and_sign(void) {
366 test_jwt_encode_and_sign(service_account_creds_jwt_encode_and_sign,
367 service_account_creds_check_jwt_claim);
368 }
369
test_jwt_creds_jwt_encode_and_sign(void)370 static void test_jwt_creds_jwt_encode_and_sign(void) {
371 test_jwt_encode_and_sign(jwt_creds_jwt_encode_and_sign,
372 jwt_creds_check_jwt_claim);
373 }
374
test_parse_refresh_token_success(void)375 static void test_parse_refresh_token_success(void) {
376 grpc_auth_refresh_token refresh_token =
377 grpc_auth_refresh_token_create_from_string(test_refresh_token_str);
378 GPR_ASSERT(grpc_auth_refresh_token_is_valid(&refresh_token));
379 GPR_ASSERT(refresh_token.type != nullptr &&
380 (strcmp(refresh_token.type, "authorized_user") == 0));
381 GPR_ASSERT(refresh_token.client_id != nullptr &&
382 (strcmp(refresh_token.client_id,
383 "32555999999.apps.googleusercontent.com") == 0));
384 GPR_ASSERT(
385 refresh_token.client_secret != nullptr &&
386 (strcmp(refresh_token.client_secret, "EmssLNjJy1332hD4KFsecret") == 0));
387 GPR_ASSERT(refresh_token.refresh_token != nullptr &&
388 (strcmp(refresh_token.refresh_token,
389 "1/Blahblasj424jladJDSGNf-u4Sua3HDA2ngjd42") == 0));
390 grpc_auth_refresh_token_destruct(&refresh_token);
391 }
392
test_parse_refresh_token_failure_no_type(void)393 static void test_parse_refresh_token_failure_no_type(void) {
394 const char refresh_token_str[] =
395 "{ \"client_id\": \"32555999999.apps.googleusercontent.com\","
396 " \"client_secret\": \"EmssLNjJy1332hD4KFsecret\","
397 " \"refresh_token\": \"1/Blahblasj424jladJDSGNf-u4Sua3HDA2ngjd42\"}";
398 grpc_auth_refresh_token refresh_token =
399 grpc_auth_refresh_token_create_from_string(refresh_token_str);
400 GPR_ASSERT(!grpc_auth_refresh_token_is_valid(&refresh_token));
401 }
402
test_parse_refresh_token_failure_no_client_id(void)403 static void test_parse_refresh_token_failure_no_client_id(void) {
404 const char refresh_token_str[] =
405 "{ \"client_secret\": \"EmssLNjJy1332hD4KFsecret\","
406 " \"refresh_token\": \"1/Blahblasj424jladJDSGNf-u4Sua3HDA2ngjd42\","
407 " \"type\": \"authorized_user\"}";
408 grpc_auth_refresh_token refresh_token =
409 grpc_auth_refresh_token_create_from_string(refresh_token_str);
410 GPR_ASSERT(!grpc_auth_refresh_token_is_valid(&refresh_token));
411 }
412
test_parse_refresh_token_failure_no_client_secret(void)413 static void test_parse_refresh_token_failure_no_client_secret(void) {
414 const char refresh_token_str[] =
415 "{ \"client_id\": \"32555999999.apps.googleusercontent.com\","
416 " \"refresh_token\": \"1/Blahblasj424jladJDSGNf-u4Sua3HDA2ngjd42\","
417 " \"type\": \"authorized_user\"}";
418 grpc_auth_refresh_token refresh_token =
419 grpc_auth_refresh_token_create_from_string(refresh_token_str);
420 GPR_ASSERT(!grpc_auth_refresh_token_is_valid(&refresh_token));
421 }
422
test_parse_refresh_token_failure_no_refresh_token(void)423 static void test_parse_refresh_token_failure_no_refresh_token(void) {
424 const char refresh_token_str[] =
425 "{ \"client_id\": \"32555999999.apps.googleusercontent.com\","
426 " \"client_secret\": \"EmssLNjJy1332hD4KFsecret\","
427 " \"type\": \"authorized_user\"}";
428 grpc_auth_refresh_token refresh_token =
429 grpc_auth_refresh_token_create_from_string(refresh_token_str);
430 GPR_ASSERT(!grpc_auth_refresh_token_is_valid(&refresh_token));
431 }
432
main(int argc,char ** argv)433 int main(int argc, char** argv) {
434 grpc::testing::TestEnvironment env(argc, argv);
435 grpc_init();
436 test_parse_json_key_success();
437 test_parse_json_key_failure_bad_json();
438 test_parse_json_key_failure_no_type();
439 test_parse_json_key_failure_no_client_id();
440 test_parse_json_key_failure_no_client_email();
441 test_parse_json_key_failure_no_private_key_id();
442 test_parse_json_key_failure_no_private_key();
443 test_service_account_creds_jwt_encode_and_sign();
444 test_jwt_creds_jwt_encode_and_sign();
445 test_parse_refresh_token_success();
446 test_parse_refresh_token_failure_no_type();
447 test_parse_refresh_token_failure_no_client_id();
448 test_parse_refresh_token_failure_no_client_secret();
449 test_parse_refresh_token_failure_no_refresh_token();
450 grpc_shutdown();
451 return 0;
452 }
453