• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <grpc/support/port_platform.h>
20 
21 #include "src/core/lib/security/credentials/oauth2/oauth2_credentials.h"
22 
23 #include <string.h>
24 
25 #include "src/core/lib/security/util/json_util.h"
26 #include "src/core/lib/surface/api_trace.h"
27 
28 #include <grpc/support/alloc.h>
29 #include <grpc/support/log.h>
30 #include <grpc/support/string_util.h>
31 
32 //
33 // Auth Refresh Token.
34 //
35 
grpc_auth_refresh_token_is_valid(const grpc_auth_refresh_token * refresh_token)36 int grpc_auth_refresh_token_is_valid(
37     const grpc_auth_refresh_token* refresh_token) {
38   return (refresh_token != nullptr) &&
39          strcmp(refresh_token->type, GRPC_AUTH_JSON_TYPE_INVALID);
40 }
41 
grpc_auth_refresh_token_create_from_json(const grpc_json * json)42 grpc_auth_refresh_token grpc_auth_refresh_token_create_from_json(
43     const grpc_json* json) {
44   grpc_auth_refresh_token result;
45   const char* prop_value;
46   int success = 0;
47 
48   memset(&result, 0, sizeof(grpc_auth_refresh_token));
49   result.type = GRPC_AUTH_JSON_TYPE_INVALID;
50   if (json == nullptr) {
51     gpr_log(GPR_ERROR, "Invalid json.");
52     goto end;
53   }
54 
55   prop_value = grpc_json_get_string_property(json, "type");
56   if (prop_value == nullptr ||
57       strcmp(prop_value, GRPC_AUTH_JSON_TYPE_AUTHORIZED_USER)) {
58     goto end;
59   }
60   result.type = GRPC_AUTH_JSON_TYPE_AUTHORIZED_USER;
61 
62   if (!grpc_copy_json_string_property(json, "client_secret",
63                                       &result.client_secret) ||
64       !grpc_copy_json_string_property(json, "client_id", &result.client_id) ||
65       !grpc_copy_json_string_property(json, "refresh_token",
66                                       &result.refresh_token)) {
67     goto end;
68   }
69   success = 1;
70 
71 end:
72   if (!success) grpc_auth_refresh_token_destruct(&result);
73   return result;
74 }
75 
grpc_auth_refresh_token_create_from_string(const char * json_string)76 grpc_auth_refresh_token grpc_auth_refresh_token_create_from_string(
77     const char* json_string) {
78   char* scratchpad = gpr_strdup(json_string);
79   grpc_json* json = grpc_json_parse_string(scratchpad);
80   grpc_auth_refresh_token result =
81       grpc_auth_refresh_token_create_from_json(json);
82   if (json != nullptr) grpc_json_destroy(json);
83   gpr_free(scratchpad);
84   return result;
85 }
86 
grpc_auth_refresh_token_destruct(grpc_auth_refresh_token * refresh_token)87 void grpc_auth_refresh_token_destruct(grpc_auth_refresh_token* refresh_token) {
88   if (refresh_token == nullptr) return;
89   refresh_token->type = GRPC_AUTH_JSON_TYPE_INVALID;
90   if (refresh_token->client_id != nullptr) {
91     gpr_free(refresh_token->client_id);
92     refresh_token->client_id = nullptr;
93   }
94   if (refresh_token->client_secret != nullptr) {
95     gpr_free(refresh_token->client_secret);
96     refresh_token->client_secret = nullptr;
97   }
98   if (refresh_token->refresh_token != nullptr) {
99     gpr_free(refresh_token->refresh_token);
100     refresh_token->refresh_token = nullptr;
101   }
102 }
103 
104 //
105 // Oauth2 Token Fetcher credentials.
106 //
107 
oauth2_token_fetcher_destruct(grpc_call_credentials * creds)108 static void oauth2_token_fetcher_destruct(grpc_call_credentials* creds) {
109   grpc_oauth2_token_fetcher_credentials* c =
110       reinterpret_cast<grpc_oauth2_token_fetcher_credentials*>(creds);
111   GRPC_MDELEM_UNREF(c->access_token_md);
112   gpr_mu_destroy(&c->mu);
113   grpc_pollset_set_destroy(grpc_polling_entity_pollset_set(&c->pollent));
114   grpc_httpcli_context_destroy(&c->httpcli_context);
115 }
116 
117 grpc_credentials_status
grpc_oauth2_token_fetcher_credentials_parse_server_response(const grpc_http_response * response,grpc_mdelem * token_md,grpc_millis * token_lifetime)118 grpc_oauth2_token_fetcher_credentials_parse_server_response(
119     const grpc_http_response* response, grpc_mdelem* token_md,
120     grpc_millis* token_lifetime) {
121   char* null_terminated_body = nullptr;
122   char* new_access_token = nullptr;
123   grpc_credentials_status status = GRPC_CREDENTIALS_OK;
124   grpc_json* json = nullptr;
125 
126   if (response == nullptr) {
127     gpr_log(GPR_ERROR, "Received NULL response.");
128     status = GRPC_CREDENTIALS_ERROR;
129     goto end;
130   }
131 
132   if (response->body_length > 0) {
133     null_terminated_body =
134         static_cast<char*>(gpr_malloc(response->body_length + 1));
135     null_terminated_body[response->body_length] = '\0';
136     memcpy(null_terminated_body, response->body, response->body_length);
137   }
138 
139   if (response->status != 200) {
140     gpr_log(GPR_ERROR, "Call to http server ended with error %d [%s].",
141             response->status,
142             null_terminated_body != nullptr ? null_terminated_body : "");
143     status = GRPC_CREDENTIALS_ERROR;
144     goto end;
145   } else {
146     grpc_json* access_token = nullptr;
147     grpc_json* token_type = nullptr;
148     grpc_json* expires_in = nullptr;
149     grpc_json* ptr;
150     json = grpc_json_parse_string(null_terminated_body);
151     if (json == nullptr) {
152       gpr_log(GPR_ERROR, "Could not parse JSON from %s", null_terminated_body);
153       status = GRPC_CREDENTIALS_ERROR;
154       goto end;
155     }
156     if (json->type != GRPC_JSON_OBJECT) {
157       gpr_log(GPR_ERROR, "Response should be a JSON object");
158       status = GRPC_CREDENTIALS_ERROR;
159       goto end;
160     }
161     for (ptr = json->child; ptr; ptr = ptr->next) {
162       if (strcmp(ptr->key, "access_token") == 0) {
163         access_token = ptr;
164       } else if (strcmp(ptr->key, "token_type") == 0) {
165         token_type = ptr;
166       } else if (strcmp(ptr->key, "expires_in") == 0) {
167         expires_in = ptr;
168       }
169     }
170     if (access_token == nullptr || access_token->type != GRPC_JSON_STRING) {
171       gpr_log(GPR_ERROR, "Missing or invalid access_token in JSON.");
172       status = GRPC_CREDENTIALS_ERROR;
173       goto end;
174     }
175     if (token_type == nullptr || token_type->type != GRPC_JSON_STRING) {
176       gpr_log(GPR_ERROR, "Missing or invalid token_type in JSON.");
177       status = GRPC_CREDENTIALS_ERROR;
178       goto end;
179     }
180     if (expires_in == nullptr || expires_in->type != GRPC_JSON_NUMBER) {
181       gpr_log(GPR_ERROR, "Missing or invalid expires_in in JSON.");
182       status = GRPC_CREDENTIALS_ERROR;
183       goto end;
184     }
185     gpr_asprintf(&new_access_token, "%s %s", token_type->value,
186                  access_token->value);
187     *token_lifetime = strtol(expires_in->value, nullptr, 10) * GPR_MS_PER_SEC;
188     if (!GRPC_MDISNULL(*token_md)) GRPC_MDELEM_UNREF(*token_md);
189     *token_md = grpc_mdelem_from_slices(
190         grpc_slice_from_static_string(GRPC_AUTHORIZATION_METADATA_KEY),
191         grpc_slice_from_copied_string(new_access_token));
192     status = GRPC_CREDENTIALS_OK;
193   }
194 
195 end:
196   if (status != GRPC_CREDENTIALS_OK && !GRPC_MDISNULL(*token_md)) {
197     GRPC_MDELEM_UNREF(*token_md);
198     *token_md = GRPC_MDNULL;
199   }
200   if (null_terminated_body != nullptr) gpr_free(null_terminated_body);
201   if (new_access_token != nullptr) gpr_free(new_access_token);
202   if (json != nullptr) grpc_json_destroy(json);
203   return status;
204 }
205 
on_oauth2_token_fetcher_http_response(void * user_data,grpc_error * error)206 static void on_oauth2_token_fetcher_http_response(void* user_data,
207                                                   grpc_error* error) {
208   GRPC_LOG_IF_ERROR("oauth_fetch", GRPC_ERROR_REF(error));
209   grpc_credentials_metadata_request* r =
210       static_cast<grpc_credentials_metadata_request*>(user_data);
211   grpc_oauth2_token_fetcher_credentials* c =
212       reinterpret_cast<grpc_oauth2_token_fetcher_credentials*>(r->creds);
213   grpc_mdelem access_token_md = GRPC_MDNULL;
214   grpc_millis token_lifetime;
215   grpc_credentials_status status =
216       grpc_oauth2_token_fetcher_credentials_parse_server_response(
217           &r->response, &access_token_md, &token_lifetime);
218   // Update cache and grab list of pending requests.
219   gpr_mu_lock(&c->mu);
220   c->token_fetch_pending = false;
221   c->access_token_md = GRPC_MDELEM_REF(access_token_md);
222   c->token_expiration =
223       status == GRPC_CREDENTIALS_OK
224           ? gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
225                          gpr_time_from_millis(token_lifetime, GPR_TIMESPAN))
226           : gpr_inf_past(GPR_CLOCK_MONOTONIC);
227   grpc_oauth2_pending_get_request_metadata* pending_request =
228       c->pending_requests;
229   c->pending_requests = nullptr;
230   gpr_mu_unlock(&c->mu);
231   // Invoke callbacks for all pending requests.
232   while (pending_request != nullptr) {
233     if (status == GRPC_CREDENTIALS_OK) {
234       grpc_credentials_mdelem_array_add(pending_request->md_array,
235                                         access_token_md);
236     } else {
237       error = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
238           "Error occurred when fetching oauth2 token.", &error, 1);
239     }
240     GRPC_CLOSURE_SCHED(pending_request->on_request_metadata, error);
241     grpc_polling_entity_del_from_pollset_set(
242         pending_request->pollent, grpc_polling_entity_pollset_set(&c->pollent));
243     grpc_oauth2_pending_get_request_metadata* prev = pending_request;
244     pending_request = pending_request->next;
245     gpr_free(prev);
246   }
247   GRPC_MDELEM_UNREF(access_token_md);
248   grpc_call_credentials_unref(r->creds);
249   grpc_credentials_metadata_request_destroy(r);
250 }
251 
oauth2_token_fetcher_get_request_metadata(grpc_call_credentials * creds,grpc_polling_entity * pollent,grpc_auth_metadata_context context,grpc_credentials_mdelem_array * md_array,grpc_closure * on_request_metadata,grpc_error ** error)252 static bool oauth2_token_fetcher_get_request_metadata(
253     grpc_call_credentials* creds, grpc_polling_entity* pollent,
254     grpc_auth_metadata_context context, grpc_credentials_mdelem_array* md_array,
255     grpc_closure* on_request_metadata, grpc_error** error) {
256   grpc_oauth2_token_fetcher_credentials* c =
257       reinterpret_cast<grpc_oauth2_token_fetcher_credentials*>(creds);
258   // Check if we can use the cached token.
259   grpc_millis refresh_threshold =
260       GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS * GPR_MS_PER_SEC;
261   grpc_mdelem cached_access_token_md = GRPC_MDNULL;
262   gpr_mu_lock(&c->mu);
263   if (!GRPC_MDISNULL(c->access_token_md) &&
264       gpr_time_cmp(
265           gpr_time_sub(c->token_expiration, gpr_now(GPR_CLOCK_MONOTONIC)),
266           gpr_time_from_seconds(GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS,
267                                 GPR_TIMESPAN)) > 0) {
268     cached_access_token_md = GRPC_MDELEM_REF(c->access_token_md);
269   }
270   if (!GRPC_MDISNULL(cached_access_token_md)) {
271     gpr_mu_unlock(&c->mu);
272     grpc_credentials_mdelem_array_add(md_array, cached_access_token_md);
273     GRPC_MDELEM_UNREF(cached_access_token_md);
274     return true;
275   }
276   // Couldn't get the token from the cache.
277   // Add request to c->pending_requests and start a new fetch if needed.
278   grpc_oauth2_pending_get_request_metadata* pending_request =
279       static_cast<grpc_oauth2_pending_get_request_metadata*>(
280           gpr_malloc(sizeof(*pending_request)));
281   pending_request->md_array = md_array;
282   pending_request->on_request_metadata = on_request_metadata;
283   pending_request->pollent = pollent;
284   grpc_polling_entity_add_to_pollset_set(
285       pollent, grpc_polling_entity_pollset_set(&c->pollent));
286   pending_request->next = c->pending_requests;
287   c->pending_requests = pending_request;
288   bool start_fetch = false;
289   if (!c->token_fetch_pending) {
290     c->token_fetch_pending = true;
291     start_fetch = true;
292   }
293   gpr_mu_unlock(&c->mu);
294   if (start_fetch) {
295     grpc_call_credentials_ref(creds);
296     c->fetch_func(grpc_credentials_metadata_request_create(creds),
297                   &c->httpcli_context, &c->pollent,
298                   on_oauth2_token_fetcher_http_response,
299                   grpc_core::ExecCtx::Get()->Now() + refresh_threshold);
300   }
301   return false;
302 }
303 
oauth2_token_fetcher_cancel_get_request_metadata(grpc_call_credentials * creds,grpc_credentials_mdelem_array * md_array,grpc_error * error)304 static void oauth2_token_fetcher_cancel_get_request_metadata(
305     grpc_call_credentials* creds, grpc_credentials_mdelem_array* md_array,
306     grpc_error* error) {
307   grpc_oauth2_token_fetcher_credentials* c =
308       reinterpret_cast<grpc_oauth2_token_fetcher_credentials*>(creds);
309   gpr_mu_lock(&c->mu);
310   grpc_oauth2_pending_get_request_metadata* prev = nullptr;
311   grpc_oauth2_pending_get_request_metadata* pending_request =
312       c->pending_requests;
313   while (pending_request != nullptr) {
314     if (pending_request->md_array == md_array) {
315       // Remove matching pending request from the list.
316       if (prev != nullptr) {
317         prev->next = pending_request->next;
318       } else {
319         c->pending_requests = pending_request->next;
320       }
321       // Invoke the callback immediately with an error.
322       GRPC_CLOSURE_SCHED(pending_request->on_request_metadata,
323                          GRPC_ERROR_REF(error));
324       gpr_free(pending_request);
325       break;
326     }
327     prev = pending_request;
328     pending_request = pending_request->next;
329   }
330   gpr_mu_unlock(&c->mu);
331   GRPC_ERROR_UNREF(error);
332 }
333 
init_oauth2_token_fetcher(grpc_oauth2_token_fetcher_credentials * c,grpc_fetch_oauth2_func fetch_func)334 static void init_oauth2_token_fetcher(grpc_oauth2_token_fetcher_credentials* c,
335                                       grpc_fetch_oauth2_func fetch_func) {
336   memset(c, 0, sizeof(grpc_oauth2_token_fetcher_credentials));
337   c->base.type = GRPC_CALL_CREDENTIALS_TYPE_OAUTH2;
338   gpr_ref_init(&c->base.refcount, 1);
339   gpr_mu_init(&c->mu);
340   c->token_expiration = gpr_inf_past(GPR_CLOCK_MONOTONIC);
341   c->fetch_func = fetch_func;
342   c->pollent =
343       grpc_polling_entity_create_from_pollset_set(grpc_pollset_set_create());
344   grpc_httpcli_context_init(&c->httpcli_context);
345 }
346 
347 //
348 //  Google Compute Engine credentials.
349 //
350 
351 static grpc_call_credentials_vtable compute_engine_vtable = {
352     oauth2_token_fetcher_destruct, oauth2_token_fetcher_get_request_metadata,
353     oauth2_token_fetcher_cancel_get_request_metadata};
354 
compute_engine_fetch_oauth2(grpc_credentials_metadata_request * metadata_req,grpc_httpcli_context * httpcli_context,grpc_polling_entity * pollent,grpc_iomgr_cb_func response_cb,grpc_millis deadline)355 static void compute_engine_fetch_oauth2(
356     grpc_credentials_metadata_request* metadata_req,
357     grpc_httpcli_context* httpcli_context, grpc_polling_entity* pollent,
358     grpc_iomgr_cb_func response_cb, grpc_millis deadline) {
359   grpc_http_header header = {(char*)"Metadata-Flavor", (char*)"Google"};
360   grpc_httpcli_request request;
361   memset(&request, 0, sizeof(grpc_httpcli_request));
362   request.host = (char*)GRPC_COMPUTE_ENGINE_METADATA_HOST;
363   request.http.path = (char*)GRPC_COMPUTE_ENGINE_METADATA_TOKEN_PATH;
364   request.http.hdr_count = 1;
365   request.http.hdrs = &header;
366   /* TODO(ctiller): Carry the resource_quota in ctx and share it with the host
367      channel. This would allow us to cancel an authentication query when under
368      extreme memory pressure. */
369   grpc_resource_quota* resource_quota =
370       grpc_resource_quota_create("oauth2_credentials");
371   grpc_httpcli_get(
372       httpcli_context, pollent, resource_quota, &request, deadline,
373       GRPC_CLOSURE_CREATE(response_cb, metadata_req, grpc_schedule_on_exec_ctx),
374       &metadata_req->response);
375   grpc_resource_quota_unref_internal(resource_quota);
376 }
377 
grpc_google_compute_engine_credentials_create(void * reserved)378 grpc_call_credentials* grpc_google_compute_engine_credentials_create(
379     void* reserved) {
380   grpc_oauth2_token_fetcher_credentials* c =
381       static_cast<grpc_oauth2_token_fetcher_credentials*>(
382           gpr_malloc(sizeof(grpc_oauth2_token_fetcher_credentials)));
383   GRPC_API_TRACE("grpc_compute_engine_credentials_create(reserved=%p)", 1,
384                  (reserved));
385   GPR_ASSERT(reserved == nullptr);
386   init_oauth2_token_fetcher(c, compute_engine_fetch_oauth2);
387   c->base.vtable = &compute_engine_vtable;
388   return &c->base;
389 }
390 
391 //
392 // Google Refresh Token credentials.
393 //
394 
refresh_token_destruct(grpc_call_credentials * creds)395 static void refresh_token_destruct(grpc_call_credentials* creds) {
396   grpc_google_refresh_token_credentials* c =
397       reinterpret_cast<grpc_google_refresh_token_credentials*>(creds);
398   grpc_auth_refresh_token_destruct(&c->refresh_token);
399   oauth2_token_fetcher_destruct(&c->base.base);
400 }
401 
402 static grpc_call_credentials_vtable refresh_token_vtable = {
403     refresh_token_destruct, oauth2_token_fetcher_get_request_metadata,
404     oauth2_token_fetcher_cancel_get_request_metadata};
405 
refresh_token_fetch_oauth2(grpc_credentials_metadata_request * metadata_req,grpc_httpcli_context * httpcli_context,grpc_polling_entity * pollent,grpc_iomgr_cb_func response_cb,grpc_millis deadline)406 static void refresh_token_fetch_oauth2(
407     grpc_credentials_metadata_request* metadata_req,
408     grpc_httpcli_context* httpcli_context, grpc_polling_entity* pollent,
409     grpc_iomgr_cb_func response_cb, grpc_millis deadline) {
410   grpc_google_refresh_token_credentials* c =
411       reinterpret_cast<grpc_google_refresh_token_credentials*>(
412           metadata_req->creds);
413   grpc_http_header header = {(char*)"Content-Type",
414                              (char*)"application/x-www-form-urlencoded"};
415   grpc_httpcli_request request;
416   char* body = nullptr;
417   gpr_asprintf(&body, GRPC_REFRESH_TOKEN_POST_BODY_FORMAT_STRING,
418                c->refresh_token.client_id, c->refresh_token.client_secret,
419                c->refresh_token.refresh_token);
420   memset(&request, 0, sizeof(grpc_httpcli_request));
421   request.host = (char*)GRPC_GOOGLE_OAUTH2_SERVICE_HOST;
422   request.http.path = (char*)GRPC_GOOGLE_OAUTH2_SERVICE_TOKEN_PATH;
423   request.http.hdr_count = 1;
424   request.http.hdrs = &header;
425   request.handshaker = &grpc_httpcli_ssl;
426   /* TODO(ctiller): Carry the resource_quota in ctx and share it with the host
427      channel. This would allow us to cancel an authentication query when under
428      extreme memory pressure. */
429   grpc_resource_quota* resource_quota =
430       grpc_resource_quota_create("oauth2_credentials_refresh");
431   grpc_httpcli_post(
432       httpcli_context, pollent, resource_quota, &request, body, strlen(body),
433       deadline,
434       GRPC_CLOSURE_CREATE(response_cb, metadata_req, grpc_schedule_on_exec_ctx),
435       &metadata_req->response);
436   grpc_resource_quota_unref_internal(resource_quota);
437   gpr_free(body);
438 }
439 
440 grpc_call_credentials*
grpc_refresh_token_credentials_create_from_auth_refresh_token(grpc_auth_refresh_token refresh_token)441 grpc_refresh_token_credentials_create_from_auth_refresh_token(
442     grpc_auth_refresh_token refresh_token) {
443   grpc_google_refresh_token_credentials* c;
444   if (!grpc_auth_refresh_token_is_valid(&refresh_token)) {
445     gpr_log(GPR_ERROR, "Invalid input for refresh token credentials creation");
446     return nullptr;
447   }
448   c = static_cast<grpc_google_refresh_token_credentials*>(
449       gpr_zalloc(sizeof(grpc_google_refresh_token_credentials)));
450   init_oauth2_token_fetcher(&c->base, refresh_token_fetch_oauth2);
451   c->base.base.vtable = &refresh_token_vtable;
452   c->refresh_token = refresh_token;
453   return &c->base.base;
454 }
455 
create_loggable_refresh_token(grpc_auth_refresh_token * token)456 static char* create_loggable_refresh_token(grpc_auth_refresh_token* token) {
457   if (strcmp(token->type, GRPC_AUTH_JSON_TYPE_INVALID) == 0) {
458     return gpr_strdup("<Invalid json token>");
459   }
460   char* loggable_token = nullptr;
461   gpr_asprintf(&loggable_token,
462                "{\n type: %s\n client_id: %s\n client_secret: "
463                "<redacted>\n refresh_token: <redacted>\n}",
464                token->type, token->client_id);
465   return loggable_token;
466 }
467 
grpc_google_refresh_token_credentials_create(const char * json_refresh_token,void * reserved)468 grpc_call_credentials* grpc_google_refresh_token_credentials_create(
469     const char* json_refresh_token, void* reserved) {
470   grpc_auth_refresh_token token =
471       grpc_auth_refresh_token_create_from_string(json_refresh_token);
472   if (grpc_api_trace.enabled()) {
473     char* loggable_token = create_loggable_refresh_token(&token);
474     gpr_log(GPR_INFO,
475             "grpc_refresh_token_credentials_create(json_refresh_token=%s, "
476             "reserved=%p)",
477             loggable_token, reserved);
478     gpr_free(loggable_token);
479   }
480   GPR_ASSERT(reserved == nullptr);
481   return grpc_refresh_token_credentials_create_from_auth_refresh_token(token);
482 }
483 
484 //
485 // Oauth2 Access Token credentials.
486 //
487 
access_token_destruct(grpc_call_credentials * creds)488 static void access_token_destruct(grpc_call_credentials* creds) {
489   grpc_access_token_credentials* c =
490       reinterpret_cast<grpc_access_token_credentials*>(creds);
491   GRPC_MDELEM_UNREF(c->access_token_md);
492 }
493 
access_token_get_request_metadata(grpc_call_credentials * creds,grpc_polling_entity * pollent,grpc_auth_metadata_context context,grpc_credentials_mdelem_array * md_array,grpc_closure * on_request_metadata,grpc_error ** error)494 static bool access_token_get_request_metadata(
495     grpc_call_credentials* creds, grpc_polling_entity* pollent,
496     grpc_auth_metadata_context context, grpc_credentials_mdelem_array* md_array,
497     grpc_closure* on_request_metadata, grpc_error** error) {
498   grpc_access_token_credentials* c =
499       reinterpret_cast<grpc_access_token_credentials*>(creds);
500   grpc_credentials_mdelem_array_add(md_array, c->access_token_md);
501   return true;
502 }
503 
access_token_cancel_get_request_metadata(grpc_call_credentials * c,grpc_credentials_mdelem_array * md_array,grpc_error * error)504 static void access_token_cancel_get_request_metadata(
505     grpc_call_credentials* c, grpc_credentials_mdelem_array* md_array,
506     grpc_error* error) {
507   GRPC_ERROR_UNREF(error);
508 }
509 
510 static grpc_call_credentials_vtable access_token_vtable = {
511     access_token_destruct, access_token_get_request_metadata,
512     access_token_cancel_get_request_metadata};
513 
grpc_access_token_credentials_create(const char * access_token,void * reserved)514 grpc_call_credentials* grpc_access_token_credentials_create(
515     const char* access_token, void* reserved) {
516   grpc_access_token_credentials* c =
517       static_cast<grpc_access_token_credentials*>(
518           gpr_zalloc(sizeof(grpc_access_token_credentials)));
519   GRPC_API_TRACE(
520       "grpc_access_token_credentials_create(access_token=<redacted>, "
521       "reserved=%p)",
522       1, (reserved));
523   GPR_ASSERT(reserved == nullptr);
524   c->base.type = GRPC_CALL_CREDENTIALS_TYPE_OAUTH2;
525   c->base.vtable = &access_token_vtable;
526   gpr_ref_init(&c->base.refcount, 1);
527   char* token_md_value;
528   gpr_asprintf(&token_md_value, "Bearer %s", access_token);
529   grpc_core::ExecCtx exec_ctx;
530   c->access_token_md = grpc_mdelem_from_slices(
531       grpc_slice_from_static_string(GRPC_AUTHORIZATION_METADATA_KEY),
532       grpc_slice_from_copied_string(token_md_value));
533 
534   gpr_free(token_md_value);
535   return &c->base;
536 }
537