3 * Copyright 2015 gRPC authors.
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include "src/core/lib/security/credentials/jwt/json_token.h"
21 #include <openssl/evp.h>
24 #include <grpc/grpc_security.h>
25 #include <grpc/slice.h>
26 #include <grpc/support/alloc.h>
27 #include <grpc/support/log.h>
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"
35 using grpc_core::Json;
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"
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"
56 "W303SQv2cZTdbeaLKJbB5drz3eo3j7dDKjrTD9JupixFbzcGw\\n8FZi5c8idxiwC36kbAL6Hz"
58 "ZoX+ofI0CQE6KCzPJTtYNqyShgKAZdJ8hwOcvCZtf\\n6z8RJm0+"
59 "6YBd38lfh5j8mZd7aHFf6I17j5AQY7oPEc47TjJj/"
60 "5nZ68ECQQDvYuI3\\nLyK5fS8g0SYbmPOL9TlcHDOqwG0mrX9qpg5DC2fniXNSrrZ64GTDKdzZ"
62 "Ap6LI9W\\nIqv4vr6y38N79TTC\\n-----END PRIVATE KEY-----\\n\", ";
63 static const char test_json_key_str_part3[] =
64 "\"private_key_id\": \"e6b5137873db8d2ef81e06a47289e6434ec8a165\", "
66 "\"777-abaslkan11hlb6nmim3bpspl31ud@developer.gserviceaccount."
67 "com\", \"client_id\": "
68 "\"777-abaslkan11hlb6nmim3bpspl31ud.apps.googleusercontent."
69 "com\", \"type\": \"service_account\" }";
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\"}";
78 static const char test_scope[] = "myperm1 myperm2";
80 static const char test_service_url[] = "https://foo.com/foo.v1";
82 static char* test_json_key_str(const char* bad_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);
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);
120 static void test_parse_json_key_failure_bad_json(void) {
121 const char non_closing_part3[] =
122 "\"private_key_id\": \"e6b5137873db8d2ef81e06a47289e6434ec8a165\", "
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);
136 static void test_parse_json_key_failure_no_type(void) {
137 const char no_type_part3[] =
138 "\"private_key_id\": \"e6b5137873db8d2ef81e06a47289e6434ec8a165\", "
140 "\"777-abaslkan11hlb6nmim3bpspl31ud@developer.gserviceaccount."
141 "com\", \"client_id\": "
142 "\"777-abaslkan11hlb6nmim3bpspl31ud.apps.googleusercontent."
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);
152 static void test_parse_json_key_failure_no_client_id(void) {
153 const char no_client_id_part3[] =
154 "\"private_key_id\": \"e6b5137873db8d2ef81e06a47289e6434ec8a165\", "
156 "\"777-abaslkan11hlb6nmim3bpspl31ud@developer.gserviceaccount."
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);
167 static void test_parse_json_key_failure_no_client_email(void) {
168 const char no_client_email_part3[] =
169 "\"private_key_id\": \"e6b5137873db8d2ef81e06a47289e6434ec8a165\", "
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);
181 static void test_parse_json_key_failure_no_private_key_id(void) {
182 const char no_private_key_id_part3[] =
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);
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\", "
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);
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);
215 grpc_slice slice = grpc_base64_decode(b64, 1);
217 GPR_ASSERT(!GRPC_SLICE_IS_EMPTY(slice));
218 grpc_error_handle 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",
223 grpc_error_std_string(error).c_str());
224 GRPC_ERROR_UNREF(error);
226 grpc_slice_unref(slice);
230 static void check_jwt_header(const Json& header) {
231 Json::Object object = header.object_value();
232 Json value = object["alg"];
233 GPR_ASSERT(value.type() == Json::Type::STRING);
234 GPR_ASSERT(strcmp(value.string_value().c_str(), "RS256") == 0);
235 value = object["typ"];
236 GPR_ASSERT(value.type() == Json::Type::STRING);
237 GPR_ASSERT(strcmp(value.string_value().c_str(), "JWT") == 0);
238 value = object["kid"];
239 GPR_ASSERT(value.type() == Json::Type::STRING);
240 GPR_ASSERT(strcmp(value.string_value().c_str(),
241 "e6b5137873db8d2ef81e06a47289e6434ec8a165") == 0);
244 static void check_jwt_claim(const Json& claim, const char* expected_audience,
245 const char* expected_scope) {
246 Json::Object object = claim.object_value();
248 Json value = object["iss"];
249 GPR_ASSERT(value.type() == Json::Type::STRING);
250 GPR_ASSERT(value.string_value() ==
251 "777-abaslkan11hlb6nmim3bpspl31ud@developer.gserviceaccount.com");
253 if (expected_scope != nullptr) {
254 GPR_ASSERT(object.find("sub") == object.end());
255 value = object["scope"];
256 GPR_ASSERT(value.type() == Json::Type::STRING);
257 GPR_ASSERT(value.string_value() == expected_scope);
259 /* Claims without scope must have a sub. */
260 GPR_ASSERT(object.find("scope") == object.end());
261 value = object["sub"];
262 GPR_ASSERT(value.type() == Json::Type::STRING);
263 GPR_ASSERT(value.string_value() == object["iss"].string_value());
266 value = object["aud"];
267 GPR_ASSERT(value.type() == Json::Type::STRING);
268 GPR_ASSERT(value.string_value() == expected_audience);
270 gpr_timespec expiration = gpr_time_0(GPR_CLOCK_REALTIME);
271 value = object["exp"];
272 GPR_ASSERT(value.type() == Json::Type::NUMBER);
273 expiration.tv_sec = strtol(value.string_value().c_str(), nullptr, 10);
275 gpr_timespec issue_time = gpr_time_0(GPR_CLOCK_REALTIME);
276 value = object["iat"];
277 GPR_ASSERT(value.type() == Json::Type::NUMBER);
278 issue_time.tv_sec = strtol(value.string_value().c_str(), nullptr, 10);
280 gpr_timespec parsed_lifetime = gpr_time_sub(expiration, issue_time);
281 GPR_ASSERT(parsed_lifetime.tv_sec == grpc_max_auth_token_lifetime().tv_sec);
284 static void check_jwt_signature(const char* b64_signature, RSA* rsa_key,
285 const char* signed_data,
286 size_t signed_data_size) {
287 grpc_core::ExecCtx exec_ctx;
289 EVP_MD_CTX* md_ctx = EVP_MD_CTX_create();
290 EVP_PKEY* key = EVP_PKEY_new();
292 grpc_slice sig = grpc_base64_decode(b64_signature, 1);
293 GPR_ASSERT(!GRPC_SLICE_IS_EMPTY(sig));
294 GPR_ASSERT(GRPC_SLICE_LENGTH(sig) == 128);
296 GPR_ASSERT(md_ctx != nullptr);
297 GPR_ASSERT(key != nullptr);
298 EVP_PKEY_set1_RSA(key, rsa_key);
301 EVP_DigestVerifyInit(md_ctx, nullptr, EVP_sha256(), nullptr, key) == 1);
302 GPR_ASSERT(EVP_DigestVerifyUpdate(md_ctx, signed_data, signed_data_size) ==
304 GPR_ASSERT(EVP_DigestVerifyFinal(md_ctx, GRPC_SLICE_START_PTR(sig),
305 GRPC_SLICE_LENGTH(sig)) == 1);
307 grpc_slice_unref_internal(sig);
308 if (key != nullptr) EVP_PKEY_free(key);
309 if (md_ctx != nullptr) EVP_MD_CTX_destroy(md_ctx);
312 static char* service_account_creds_jwt_encode_and_sign(
313 const grpc_auth_json_key* key) {
314 return grpc_jwt_encode_and_sign(key, GRPC_JWT_OAUTH2_AUDIENCE,
315 grpc_max_auth_token_lifetime(), test_scope);
318 static char* jwt_creds_jwt_encode_and_sign(const grpc_auth_json_key* key) {
319 return grpc_jwt_encode_and_sign(key, test_service_url,
320 grpc_max_auth_token_lifetime(), nullptr);
323 static void service_account_creds_check_jwt_claim(const Json& claim) {
324 check_jwt_claim(claim, GRPC_JWT_OAUTH2_AUDIENCE, test_scope);
327 static void jwt_creds_check_jwt_claim(const Json& claim) {
328 check_jwt_claim(claim, test_service_url, nullptr);
331 static void test_jwt_encode_and_sign(
332 char* (*jwt_encode_and_sign_func)(const grpc_auth_json_key*),
333 void (*check_jwt_claim_func)(const Json&)) {
334 char* json_string = test_json_key_str(nullptr);
335 grpc_auth_json_key json_key =
336 grpc_auth_json_key_create_from_string(json_string);
337 const char* b64_signature;
339 char* jwt = jwt_encode_and_sign_func(&json_key);
340 const char* dot = strchr(jwt, '.');
341 GPR_ASSERT(dot != nullptr);
343 parse_json_part_from_jwt(jwt, static_cast<size_t>(dot - jwt));
344 GPR_ASSERT(parsed_header.type() == Json::Type::OBJECT);
345 check_jwt_header(parsed_header);
346 offset = static_cast<size_t>(dot - jwt) + 1;
348 dot = strchr(jwt + offset, '.');
349 GPR_ASSERT(dot != nullptr);
350 Json parsed_claim = parse_json_part_from_jwt(
351 jwt + offset, static_cast<size_t>(dot - (jwt + offset)));
352 GPR_ASSERT(parsed_claim.type() == Json::Type::OBJECT);
353 check_jwt_claim_func(parsed_claim);
354 offset = static_cast<size_t>(dot - jwt) + 1;
356 dot = strchr(jwt + offset, '.');
357 GPR_ASSERT(dot == nullptr); /* no more part. */
358 b64_signature = jwt + offset;
359 check_jwt_signature(b64_signature, json_key.private_key, jwt, offset - 1);
361 gpr_free(json_string);
362 grpc_auth_json_key_destruct(&json_key);
366 static void test_service_account_creds_jwt_encode_and_sign(void) {
367 test_jwt_encode_and_sign(service_account_creds_jwt_encode_and_sign,
368 service_account_creds_check_jwt_claim);
371 static void test_jwt_creds_jwt_encode_and_sign(void) {
372 test_jwt_encode_and_sign(jwt_creds_jwt_encode_and_sign,
373 jwt_creds_check_jwt_claim);
376 static void test_parse_refresh_token_success(void) {
377 grpc_auth_refresh_token refresh_token =
378 grpc_auth_refresh_token_create_from_string(test_refresh_token_str);
379 GPR_ASSERT(grpc_auth_refresh_token_is_valid(&refresh_token));
380 GPR_ASSERT(refresh_token.type != nullptr &&
381 (strcmp(refresh_token.type, "authorized_user") == 0));
382 GPR_ASSERT(refresh_token.client_id != nullptr &&
383 (strcmp(refresh_token.client_id,
384 "32555999999.apps.googleusercontent.com") == 0));
386 refresh_token.client_secret != nullptr &&
387 (strcmp(refresh_token.client_secret, "EmssLNjJy1332hD4KFsecret") == 0));
388 GPR_ASSERT(refresh_token.refresh_token != nullptr &&
389 (strcmp(refresh_token.refresh_token,
390 "1/Blahblasj424jladJDSGNf-u4Sua3HDA2ngjd42") == 0));
391 grpc_auth_refresh_token_destruct(&refresh_token);
394 static void test_parse_refresh_token_failure_no_type(void) {
395 const char refresh_token_str[] =
396 "{ \"client_id\": \"32555999999.apps.googleusercontent.com\","
397 " \"client_secret\": \"EmssLNjJy1332hD4KFsecret\","
398 " \"refresh_token\": \"1/Blahblasj424jladJDSGNf-u4Sua3HDA2ngjd42\"}";
399 grpc_auth_refresh_token refresh_token =
400 grpc_auth_refresh_token_create_from_string(refresh_token_str);
401 GPR_ASSERT(!grpc_auth_refresh_token_is_valid(&refresh_token));
404 static void test_parse_refresh_token_failure_no_client_id(void) {
405 const char refresh_token_str[] =
406 "{ \"client_secret\": \"EmssLNjJy1332hD4KFsecret\","
407 " \"refresh_token\": \"1/Blahblasj424jladJDSGNf-u4Sua3HDA2ngjd42\","
408 " \"type\": \"authorized_user\"}";
409 grpc_auth_refresh_token refresh_token =
410 grpc_auth_refresh_token_create_from_string(refresh_token_str);
411 GPR_ASSERT(!grpc_auth_refresh_token_is_valid(&refresh_token));
414 static void test_parse_refresh_token_failure_no_client_secret(void) {
415 const char refresh_token_str[] =
416 "{ \"client_id\": \"32555999999.apps.googleusercontent.com\","
417 " \"refresh_token\": \"1/Blahblasj424jladJDSGNf-u4Sua3HDA2ngjd42\","
418 " \"type\": \"authorized_user\"}";
419 grpc_auth_refresh_token refresh_token =
420 grpc_auth_refresh_token_create_from_string(refresh_token_str);
421 GPR_ASSERT(!grpc_auth_refresh_token_is_valid(&refresh_token));
424 static void test_parse_refresh_token_failure_no_refresh_token(void) {
425 const char refresh_token_str[] =
426 "{ \"client_id\": \"32555999999.apps.googleusercontent.com\","
427 " \"client_secret\": \"EmssLNjJy1332hD4KFsecret\","
428 " \"type\": \"authorized_user\"}";
429 grpc_auth_refresh_token refresh_token =
430 grpc_auth_refresh_token_create_from_string(refresh_token_str);
431 GPR_ASSERT(!grpc_auth_refresh_token_is_valid(&refresh_token));
434 int main(int argc, char** argv) {
435 grpc::testing::TestEnvironment env(argc, argv);
437 test_parse_json_key_success();
438 test_parse_json_key_failure_bad_json();
439 test_parse_json_key_failure_no_type();
440 test_parse_json_key_failure_no_client_id();
441 test_parse_json_key_failure_no_client_email();
442 test_parse_json_key_failure_no_private_key_id();
443 test_parse_json_key_failure_no_private_key();
444 test_service_account_creds_jwt_encode_and_sign();
445 test_jwt_creds_jwt_encode_and_sign();
446 test_parse_refresh_token_success();
447 test_parse_refresh_token_failure_no_type();
448 test_parse_refresh_token_failure_no_client_id();
449 test_parse_refresh_token_failure_no_client_secret();
450 test_parse_refresh_token_failure_no_refresh_token();