blob: 10b270c49ccc2a84ca60751b09402425218f2a70 [file] [log] [blame]
Julien Boeuf8ca294e2016-05-02 14:56:30 -07001/*
2 *
Jan Tattermusch7897ae92017-06-07 22:57:36 +02003 * Copyright 2015 gRPC authors.
Julien Boeuf8ca294e2016-05-02 14:56:30 -07004 *
Jan Tattermusch7897ae92017-06-07 22:57:36 +02005 * 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
Julien Boeuf8ca294e2016-05-02 14:56:30 -07008 *
Jan Tattermusch7897ae92017-06-07 22:57:36 +02009 * http://www.apache.org/licenses/LICENSE-2.0
Julien Boeuf8ca294e2016-05-02 14:56:30 -070010 *
Jan Tattermusch7897ae92017-06-07 22:57:36 +020011 * 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.
Julien Boeuf8ca294e2016-05-02 14:56:30 -070016 *
17 */
18
19#include "src/core/lib/security/credentials/oauth2/oauth2_credentials.h"
20
21#include <string.h>
22
23#include "src/core/lib/security/util/json_util.h"
24#include "src/core/lib/surface/api_trace.h"
25
26#include <grpc/support/alloc.h>
27#include <grpc/support/log.h>
28#include <grpc/support/string_util.h>
29
30//
31// Auth Refresh Token.
32//
33
34int grpc_auth_refresh_token_is_valid(
35 const grpc_auth_refresh_token *refresh_token) {
36 return (refresh_token != NULL) &&
37 strcmp(refresh_token->type, GRPC_AUTH_JSON_TYPE_INVALID);
38}
39
40grpc_auth_refresh_token grpc_auth_refresh_token_create_from_json(
41 const grpc_json *json) {
42 grpc_auth_refresh_token result;
43 const char *prop_value;
44 int success = 0;
45
46 memset(&result, 0, sizeof(grpc_auth_refresh_token));
47 result.type = GRPC_AUTH_JSON_TYPE_INVALID;
48 if (json == NULL) {
49 gpr_log(GPR_ERROR, "Invalid json.");
50 goto end;
51 }
52
53 prop_value = grpc_json_get_string_property(json, "type");
54 if (prop_value == NULL ||
55 strcmp(prop_value, GRPC_AUTH_JSON_TYPE_AUTHORIZED_USER)) {
56 goto end;
57 }
58 result.type = GRPC_AUTH_JSON_TYPE_AUTHORIZED_USER;
59
60 if (!grpc_copy_json_string_property(json, "client_secret",
61 &result.client_secret) ||
62 !grpc_copy_json_string_property(json, "client_id", &result.client_id) ||
63 !grpc_copy_json_string_property(json, "refresh_token",
64 &result.refresh_token)) {
65 goto end;
66 }
67 success = 1;
68
69end:
70 if (!success) grpc_auth_refresh_token_destruct(&result);
71 return result;
72}
73
74grpc_auth_refresh_token grpc_auth_refresh_token_create_from_string(
75 const char *json_string) {
76 char *scratchpad = gpr_strdup(json_string);
77 grpc_json *json = grpc_json_parse_string(scratchpad);
78 grpc_auth_refresh_token result =
79 grpc_auth_refresh_token_create_from_json(json);
80 if (json != NULL) grpc_json_destroy(json);
81 gpr_free(scratchpad);
82 return result;
83}
84
85void grpc_auth_refresh_token_destruct(grpc_auth_refresh_token *refresh_token) {
86 if (refresh_token == NULL) return;
87 refresh_token->type = GRPC_AUTH_JSON_TYPE_INVALID;
88 if (refresh_token->client_id != NULL) {
89 gpr_free(refresh_token->client_id);
90 refresh_token->client_id = NULL;
91 }
92 if (refresh_token->client_secret != NULL) {
93 gpr_free(refresh_token->client_secret);
94 refresh_token->client_secret = NULL;
95 }
96 if (refresh_token->refresh_token != NULL) {
97 gpr_free(refresh_token->refresh_token);
98 refresh_token->refresh_token = NULL;
99 }
100}
101
102//
103// Oauth2 Token Fetcher credentials.
104//
105
Craig Tillerbd1795c2016-10-31 15:30:00 -0700106static void oauth2_token_fetcher_destruct(grpc_exec_ctx *exec_ctx,
107 grpc_call_credentials *creds) {
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700108 grpc_oauth2_token_fetcher_credentials *c =
109 (grpc_oauth2_token_fetcher_credentials *)creds;
Mark D. Rothe0778b22017-07-21 15:42:00 -0700110 GRPC_MDELEM_UNREF(exec_ctx, c->access_token_md);
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700111 gpr_mu_destroy(&c->mu);
Yuchen Zenga0a7b572017-08-09 10:33:07 -0700112 grpc_pollset_set_destroy(exec_ctx,
113 grpc_polling_entity_pollset_set(&c->pollent));
Craig Tiller9e5ac1b2017-02-14 22:25:50 -0800114 grpc_httpcli_context_destroy(exec_ctx, &c->httpcli_context);
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700115}
116
117grpc_credentials_status
118grpc_oauth2_token_fetcher_credentials_parse_server_response(
Craig Tillerbd1795c2016-10-31 15:30:00 -0700119 grpc_exec_ctx *exec_ctx, const grpc_http_response *response,
Mark D. Rothe0778b22017-07-21 15:42:00 -0700120 grpc_mdelem *token_md, gpr_timespec *token_lifetime) {
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700121 char *null_terminated_body = NULL;
122 char *new_access_token = NULL;
123 grpc_credentials_status status = GRPC_CREDENTIALS_OK;
124 grpc_json *json = NULL;
125
126 if (response == NULL) {
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 = gpr_malloc(response->body_length + 1);
134 null_terminated_body[response->body_length] = '\0';
135 memcpy(null_terminated_body, response->body, response->body_length);
136 }
137
138 if (response->status != 200) {
139 gpr_log(GPR_ERROR, "Call to http server ended with error %d [%s].",
140 response->status,
141 null_terminated_body != NULL ? null_terminated_body : "");
142 status = GRPC_CREDENTIALS_ERROR;
143 goto end;
144 } else {
145 grpc_json *access_token = NULL;
146 grpc_json *token_type = NULL;
147 grpc_json *expires_in = NULL;
148 grpc_json *ptr;
149 json = grpc_json_parse_string(null_terminated_body);
150 if (json == NULL) {
151 gpr_log(GPR_ERROR, "Could not parse JSON from %s", null_terminated_body);
152 status = GRPC_CREDENTIALS_ERROR;
153 goto end;
154 }
155 if (json->type != GRPC_JSON_OBJECT) {
156 gpr_log(GPR_ERROR, "Response should be a JSON object");
157 status = GRPC_CREDENTIALS_ERROR;
158 goto end;
159 }
160 for (ptr = json->child; ptr; ptr = ptr->next) {
161 if (strcmp(ptr->key, "access_token") == 0) {
162 access_token = ptr;
163 } else if (strcmp(ptr->key, "token_type") == 0) {
164 token_type = ptr;
165 } else if (strcmp(ptr->key, "expires_in") == 0) {
166 expires_in = ptr;
167 }
168 }
169 if (access_token == NULL || access_token->type != GRPC_JSON_STRING) {
170 gpr_log(GPR_ERROR, "Missing or invalid access_token in JSON.");
171 status = GRPC_CREDENTIALS_ERROR;
172 goto end;
173 }
174 if (token_type == NULL || token_type->type != GRPC_JSON_STRING) {
175 gpr_log(GPR_ERROR, "Missing or invalid token_type in JSON.");
176 status = GRPC_CREDENTIALS_ERROR;
177 goto end;
178 }
179 if (expires_in == NULL || expires_in->type != GRPC_JSON_NUMBER) {
180 gpr_log(GPR_ERROR, "Missing or invalid expires_in in JSON.");
181 status = GRPC_CREDENTIALS_ERROR;
182 goto end;
183 }
184 gpr_asprintf(&new_access_token, "%s %s", token_type->value,
185 access_token->value);
186 token_lifetime->tv_sec = strtol(expires_in->value, NULL, 10);
187 token_lifetime->tv_nsec = 0;
188 token_lifetime->clock_type = GPR_TIMESPAN;
Mark D. Rothe0778b22017-07-21 15:42:00 -0700189 if (!GRPC_MDISNULL(*token_md)) GRPC_MDELEM_UNREF(exec_ctx, *token_md);
190 *token_md = grpc_mdelem_from_slices(
191 exec_ctx,
192 grpc_slice_from_static_string(GRPC_AUTHORIZATION_METADATA_KEY),
193 grpc_slice_from_copied_string(new_access_token));
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700194 status = GRPC_CREDENTIALS_OK;
195 }
196
197end:
Mark D. Rothe0778b22017-07-21 15:42:00 -0700198 if (status != GRPC_CREDENTIALS_OK && !GRPC_MDISNULL(*token_md)) {
199 GRPC_MDELEM_UNREF(exec_ctx, *token_md);
200 *token_md = GRPC_MDNULL;
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700201 }
202 if (null_terminated_body != NULL) gpr_free(null_terminated_body);
203 if (new_access_token != NULL) gpr_free(new_access_token);
204 if (json != NULL) grpc_json_destroy(json);
205 return status;
206}
207
Craig Tiller804ff712016-05-05 16:25:40 -0700208static void on_oauth2_token_fetcher_http_response(grpc_exec_ctx *exec_ctx,
209 void *user_data,
210 grpc_error *error) {
Mark D. Rothe0778b22017-07-21 15:42:00 -0700211 GRPC_LOG_IF_ERROR("oauth_fetch", GRPC_ERROR_REF(error));
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700212 grpc_credentials_metadata_request *r =
213 (grpc_credentials_metadata_request *)user_data;
214 grpc_oauth2_token_fetcher_credentials *c =
215 (grpc_oauth2_token_fetcher_credentials *)r->creds;
Mark D. Rothe0778b22017-07-21 15:42:00 -0700216 grpc_mdelem access_token_md = GRPC_MDNULL;
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700217 gpr_timespec token_lifetime;
Mark D. Rothe0778b22017-07-21 15:42:00 -0700218 grpc_credentials_status status =
219 grpc_oauth2_token_fetcher_credentials_parse_server_response(
220 exec_ctx, &r->response, &access_token_md, &token_lifetime);
221 // Update cache and grab list of pending requests.
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700222 gpr_mu_lock(&c->mu);
Mark D. Rothe0778b22017-07-21 15:42:00 -0700223 c->token_fetch_pending = false;
224 c->access_token_md = GRPC_MDELEM_REF(access_token_md);
225 c->token_expiration =
226 status == GRPC_CREDENTIALS_OK
227 ? gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), token_lifetime)
228 : gpr_inf_past(GPR_CLOCK_REALTIME);
229 grpc_oauth2_pending_get_request_metadata *pending_request =
230 c->pending_requests;
231 c->pending_requests = NULL;
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700232 gpr_mu_unlock(&c->mu);
Mark D. Rothe0778b22017-07-21 15:42:00 -0700233 // Invoke callbacks for all pending requests.
234 while (pending_request != NULL) {
235 if (status == GRPC_CREDENTIALS_OK) {
236 grpc_credentials_mdelem_array_add(pending_request->md_array,
237 access_token_md);
238 } else {
239 error = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
240 "Error occured when fetching oauth2 token.", &error, 1);
241 }
242 GRPC_CLOSURE_SCHED(exec_ctx, pending_request->on_request_metadata, error);
Yuchen Zenga0a7b572017-08-09 10:33:07 -0700243 grpc_polling_entity_del_from_pollset_set(
244 exec_ctx, pending_request->pollent,
245 grpc_polling_entity_pollset_set(&c->pollent));
Mark D. Rothe0778b22017-07-21 15:42:00 -0700246 grpc_oauth2_pending_get_request_metadata *prev = pending_request;
247 pending_request = pending_request->next;
248 gpr_free(prev);
249 }
250 GRPC_MDELEM_UNREF(exec_ctx, access_token_md);
251 grpc_call_credentials_unref(exec_ctx, r->creds);
Craig Tillerbd1795c2016-10-31 15:30:00 -0700252 grpc_credentials_metadata_request_destroy(exec_ctx, r);
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700253}
254
Mark D. Rothe0778b22017-07-21 15:42:00 -0700255static bool oauth2_token_fetcher_get_request_metadata(
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700256 grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds,
David Garcia Quintas2a50dfe2016-05-31 15:09:12 -0700257 grpc_polling_entity *pollent, grpc_auth_metadata_context context,
Mark D. Rothe0778b22017-07-21 15:42:00 -0700258 grpc_credentials_mdelem_array *md_array, grpc_closure *on_request_metadata,
259 grpc_error **error) {
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700260 grpc_oauth2_token_fetcher_credentials *c =
261 (grpc_oauth2_token_fetcher_credentials *)creds;
Mark D. Rothe0778b22017-07-21 15:42:00 -0700262 // Check if we can use the cached token.
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700263 gpr_timespec refresh_threshold = gpr_time_from_seconds(
264 GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS, GPR_TIMESPAN);
Mark D. Rothe0778b22017-07-21 15:42:00 -0700265 grpc_mdelem cached_access_token_md = GRPC_MDNULL;
266 gpr_mu_lock(&c->mu);
267 if (!GRPC_MDISNULL(c->access_token_md) &&
268 (gpr_time_cmp(
269 gpr_time_sub(c->token_expiration, gpr_now(GPR_CLOCK_REALTIME)),
270 refresh_threshold) > 0)) {
271 cached_access_token_md = GRPC_MDELEM_REF(c->access_token_md);
272 }
273 if (!GRPC_MDISNULL(cached_access_token_md)) {
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700274 gpr_mu_unlock(&c->mu);
Mark D. Rothe0778b22017-07-21 15:42:00 -0700275 grpc_credentials_mdelem_array_add(md_array, cached_access_token_md);
276 GRPC_MDELEM_UNREF(exec_ctx, cached_access_token_md);
277 return true;
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700278 }
Mark D. Rothe0778b22017-07-21 15:42:00 -0700279 // Couldn't get the token from the cache.
280 // Add request to c->pending_requests and start a new fetch if needed.
281 grpc_oauth2_pending_get_request_metadata *pending_request =
282 (grpc_oauth2_pending_get_request_metadata *)gpr_malloc(
283 sizeof(*pending_request));
284 pending_request->md_array = md_array;
285 pending_request->on_request_metadata = on_request_metadata;
Yuchen Zenga0a7b572017-08-09 10:33:07 -0700286 pending_request->pollent = pollent;
287 grpc_polling_entity_add_to_pollset_set(
288 exec_ctx, pollent, grpc_polling_entity_pollset_set(&c->pollent));
Mark D. Rothe0778b22017-07-21 15:42:00 -0700289 pending_request->next = c->pending_requests;
290 c->pending_requests = pending_request;
291 bool start_fetch = false;
292 if (!c->token_fetch_pending) {
293 c->token_fetch_pending = true;
294 start_fetch = true;
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700295 }
Mark D. Rothe0778b22017-07-21 15:42:00 -0700296 gpr_mu_unlock(&c->mu);
297 if (start_fetch) {
298 grpc_call_credentials_ref(creds);
Maxim Dzoba22cc56f2017-08-16 17:09:34 -0400299 c->fetch_func(
300 exec_ctx, grpc_credentials_metadata_request_create(creds),
301 &c->httpcli_context, &c->pollent, on_oauth2_token_fetcher_http_response,
302 gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), refresh_threshold));
Mark D. Rothe0778b22017-07-21 15:42:00 -0700303 }
304 return false;
305}
306
307static void oauth2_token_fetcher_cancel_get_request_metadata(
308 grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds,
309 grpc_credentials_mdelem_array *md_array, grpc_error *error) {
310 grpc_oauth2_token_fetcher_credentials *c =
311 (grpc_oauth2_token_fetcher_credentials *)creds;
312 gpr_mu_lock(&c->mu);
313 grpc_oauth2_pending_get_request_metadata *prev = NULL;
314 grpc_oauth2_pending_get_request_metadata *pending_request =
315 c->pending_requests;
316 while (pending_request != NULL) {
317 if (pending_request->md_array == md_array) {
318 // Remove matching pending request from the list.
319 if (prev != NULL) {
320 prev->next = pending_request->next;
321 } else {
322 c->pending_requests = pending_request->next;
323 }
324 // Invoke the callback immediately with an error.
325 GRPC_CLOSURE_SCHED(exec_ctx, pending_request->on_request_metadata,
326 GRPC_ERROR_REF(error));
327 gpr_free(pending_request);
328 break;
329 }
330 prev = pending_request;
331 pending_request = pending_request->next;
332 }
333 gpr_mu_unlock(&c->mu);
334 GRPC_ERROR_UNREF(error);
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700335}
336
337static void init_oauth2_token_fetcher(grpc_oauth2_token_fetcher_credentials *c,
338 grpc_fetch_oauth2_func fetch_func) {
339 memset(c, 0, sizeof(grpc_oauth2_token_fetcher_credentials));
340 c->base.type = GRPC_CALL_CREDENTIALS_TYPE_OAUTH2;
341 gpr_ref_init(&c->base.refcount, 1);
342 gpr_mu_init(&c->mu);
343 c->token_expiration = gpr_inf_past(GPR_CLOCK_REALTIME);
344 c->fetch_func = fetch_func;
Yuchen Zenga0a7b572017-08-09 10:33:07 -0700345 c->pollent =
346 grpc_polling_entity_create_from_pollset_set(grpc_pollset_set_create());
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700347 grpc_httpcli_context_init(&c->httpcli_context);
348}
349
350//
351// Google Compute Engine credentials.
352//
353
354static grpc_call_credentials_vtable compute_engine_vtable = {
Mark D. Rothe0778b22017-07-21 15:42:00 -0700355 oauth2_token_fetcher_destruct, oauth2_token_fetcher_get_request_metadata,
356 oauth2_token_fetcher_cancel_get_request_metadata};
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700357
358static void compute_engine_fetch_oauth2(
359 grpc_exec_ctx *exec_ctx, grpc_credentials_metadata_request *metadata_req,
David Garcia Quintas2a50dfe2016-05-31 15:09:12 -0700360 grpc_httpcli_context *httpcli_context, grpc_polling_entity *pollent,
Craig Tiller804ff712016-05-05 16:25:40 -0700361 grpc_iomgr_cb_func response_cb, gpr_timespec deadline) {
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700362 grpc_http_header header = {"Metadata-Flavor", "Google"};
363 grpc_httpcli_request request;
364 memset(&request, 0, sizeof(grpc_httpcli_request));
365 request.host = GRPC_COMPUTE_ENGINE_METADATA_HOST;
366 request.http.path = GRPC_COMPUTE_ENGINE_METADATA_TOKEN_PATH;
367 request.http.hdr_count = 1;
368 request.http.hdrs = &header;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700369 /* TODO(ctiller): Carry the resource_quota in ctx and share it with the host
Craig Tiller45881862016-09-23 10:48:53 -0700370 channel. This would allow us to cancel an authentication query when under
371 extreme memory pressure. */
Craig Tillerafcc8752016-10-18 16:10:06 -0700372 grpc_resource_quota *resource_quota =
373 grpc_resource_quota_create("oauth2_credentials");
Craig Tiller91031da2016-12-28 15:44:25 -0800374 grpc_httpcli_get(
375 exec_ctx, httpcli_context, pollent, resource_quota, &request, deadline,
ncteisen274bbbe2017-06-08 14:57:11 -0700376 GRPC_CLOSURE_CREATE(response_cb, metadata_req, grpc_schedule_on_exec_ctx),
Craig Tiller91031da2016-12-28 15:44:25 -0800377 &metadata_req->response);
Craig Tillera59c16c2016-10-31 07:25:01 -0700378 grpc_resource_quota_unref_internal(exec_ctx, resource_quota);
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700379}
380
381grpc_call_credentials *grpc_google_compute_engine_credentials_create(
382 void *reserved) {
383 grpc_oauth2_token_fetcher_credentials *c =
384 gpr_malloc(sizeof(grpc_oauth2_token_fetcher_credentials));
385 GRPC_API_TRACE("grpc_compute_engine_credentials_create(reserved=%p)", 1,
386 (reserved));
387 GPR_ASSERT(reserved == NULL);
388 init_oauth2_token_fetcher(c, compute_engine_fetch_oauth2);
389 c->base.vtable = &compute_engine_vtable;
390 return &c->base;
391}
392
393//
394// Google Refresh Token credentials.
395//
396
Craig Tillerbd1795c2016-10-31 15:30:00 -0700397static void refresh_token_destruct(grpc_exec_ctx *exec_ctx,
398 grpc_call_credentials *creds) {
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700399 grpc_google_refresh_token_credentials *c =
400 (grpc_google_refresh_token_credentials *)creds;
401 grpc_auth_refresh_token_destruct(&c->refresh_token);
Craig Tillerbd1795c2016-10-31 15:30:00 -0700402 oauth2_token_fetcher_destruct(exec_ctx, &c->base.base);
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700403}
404
405static grpc_call_credentials_vtable refresh_token_vtable = {
Mark D. Rothe0778b22017-07-21 15:42:00 -0700406 refresh_token_destruct, oauth2_token_fetcher_get_request_metadata,
407 oauth2_token_fetcher_cancel_get_request_metadata};
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700408
409static void refresh_token_fetch_oauth2(
410 grpc_exec_ctx *exec_ctx, grpc_credentials_metadata_request *metadata_req,
David Garcia Quintas2a50dfe2016-05-31 15:09:12 -0700411 grpc_httpcli_context *httpcli_context, grpc_polling_entity *pollent,
Craig Tiller804ff712016-05-05 16:25:40 -0700412 grpc_iomgr_cb_func response_cb, gpr_timespec deadline) {
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700413 grpc_google_refresh_token_credentials *c =
414 (grpc_google_refresh_token_credentials *)metadata_req->creds;
415 grpc_http_header header = {"Content-Type",
416 "application/x-www-form-urlencoded"};
417 grpc_httpcli_request request;
418 char *body = NULL;
419 gpr_asprintf(&body, GRPC_REFRESH_TOKEN_POST_BODY_FORMAT_STRING,
420 c->refresh_token.client_id, c->refresh_token.client_secret,
421 c->refresh_token.refresh_token);
422 memset(&request, 0, sizeof(grpc_httpcli_request));
423 request.host = GRPC_GOOGLE_OAUTH2_SERVICE_HOST;
424 request.http.path = GRPC_GOOGLE_OAUTH2_SERVICE_TOKEN_PATH;
425 request.http.hdr_count = 1;
426 request.http.hdrs = &header;
427 request.handshaker = &grpc_httpcli_ssl;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700428 /* TODO(ctiller): Carry the resource_quota in ctx and share it with the host
Craig Tiller45881862016-09-23 10:48:53 -0700429 channel. This would allow us to cancel an authentication query when under
430 extreme memory pressure. */
Craig Tiller20afa3d2016-10-17 14:52:14 -0700431 grpc_resource_quota *resource_quota =
432 grpc_resource_quota_create("oauth2_credentials_refresh");
Craig Tiller91031da2016-12-28 15:44:25 -0800433 grpc_httpcli_post(
434 exec_ctx, httpcli_context, pollent, resource_quota, &request, body,
435 strlen(body), deadline,
ncteisen274bbbe2017-06-08 14:57:11 -0700436 GRPC_CLOSURE_CREATE(response_cb, metadata_req, grpc_schedule_on_exec_ctx),
Craig Tiller91031da2016-12-28 15:44:25 -0800437 &metadata_req->response);
Craig Tillera59c16c2016-10-31 07:25:01 -0700438 grpc_resource_quota_unref_internal(exec_ctx, resource_quota);
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700439 gpr_free(body);
440}
441
442grpc_call_credentials *
443grpc_refresh_token_credentials_create_from_auth_refresh_token(
444 grpc_auth_refresh_token refresh_token) {
445 grpc_google_refresh_token_credentials *c;
446 if (!grpc_auth_refresh_token_is_valid(&refresh_token)) {
447 gpr_log(GPR_ERROR, "Invalid input for refresh token credentials creation");
448 return NULL;
449 }
Craig Tiller6f417882017-02-16 14:09:39 -0800450 c = gpr_zalloc(sizeof(grpc_google_refresh_token_credentials));
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700451 init_oauth2_token_fetcher(&c->base, refresh_token_fetch_oauth2);
452 c->base.base.vtable = &refresh_token_vtable;
453 c->refresh_token = refresh_token;
454 return &c->base.base;
455}
456
yang-g414af9b2016-11-12 01:09:51 -0800457static char *create_loggable_refresh_token(grpc_auth_refresh_token *token) {
458 if (strcmp(token->type, GRPC_AUTH_JSON_TYPE_INVALID) == 0) {
459 return gpr_strdup("<Invalid json token>");
460 }
461 char *loggable_token = NULL;
462 gpr_asprintf(&loggable_token,
463 "{\n type: %s\n client_id: %s\n client_secret: "
464 "<redacted>\n refresh_token: <redacted>\n}",
465 token->type, token->client_id);
466 return loggable_token;
467}
468
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700469grpc_call_credentials *grpc_google_refresh_token_credentials_create(
470 const char *json_refresh_token, void *reserved) {
yang-g414af9b2016-11-12 01:09:51 -0800471 grpc_auth_refresh_token token =
472 grpc_auth_refresh_token_create_from_string(json_refresh_token);
Craig Tiller84f75d42017-05-03 13:06:35 -0700473 if (GRPC_TRACER_ON(grpc_api_trace)) {
yang-g414af9b2016-11-12 01:09:51 -0800474 char *loggable_token = create_loggable_refresh_token(&token);
475 gpr_log(GPR_INFO,
476 "grpc_refresh_token_credentials_create(json_refresh_token=%s, "
477 "reserved=%p)",
478 loggable_token, reserved);
479 gpr_free(loggable_token);
480 }
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700481 GPR_ASSERT(reserved == NULL);
yang-g414af9b2016-11-12 01:09:51 -0800482 return grpc_refresh_token_credentials_create_from_auth_refresh_token(token);
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700483}
484
485//
486// Oauth2 Access Token credentials.
487//
488
Craig Tillerbd1795c2016-10-31 15:30:00 -0700489static void access_token_destruct(grpc_exec_ctx *exec_ctx,
490 grpc_call_credentials *creds) {
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700491 grpc_access_token_credentials *c = (grpc_access_token_credentials *)creds;
Mark D. Rothe0778b22017-07-21 15:42:00 -0700492 GRPC_MDELEM_UNREF(exec_ctx, c->access_token_md);
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700493}
494
Mark D. Rothe0778b22017-07-21 15:42:00 -0700495static bool access_token_get_request_metadata(
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700496 grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds,
David Garcia Quintas2a50dfe2016-05-31 15:09:12 -0700497 grpc_polling_entity *pollent, grpc_auth_metadata_context context,
Mark D. Rothe0778b22017-07-21 15:42:00 -0700498 grpc_credentials_mdelem_array *md_array, grpc_closure *on_request_metadata,
499 grpc_error **error) {
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700500 grpc_access_token_credentials *c = (grpc_access_token_credentials *)creds;
Mark D. Rothe0778b22017-07-21 15:42:00 -0700501 grpc_credentials_mdelem_array_add(md_array, c->access_token_md);
502 return true;
503}
504
505static void access_token_cancel_get_request_metadata(
506 grpc_exec_ctx *exec_ctx, grpc_call_credentials *c,
507 grpc_credentials_mdelem_array *md_array, grpc_error *error) {
508 GRPC_ERROR_UNREF(error);
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700509}
510
511static grpc_call_credentials_vtable access_token_vtable = {
Mark D. Rothe0778b22017-07-21 15:42:00 -0700512 access_token_destruct, access_token_get_request_metadata,
513 access_token_cancel_get_request_metadata};
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700514
515grpc_call_credentials *grpc_access_token_credentials_create(
516 const char *access_token, void *reserved) {
517 grpc_access_token_credentials *c =
Craig Tiller6f417882017-02-16 14:09:39 -0800518 gpr_zalloc(sizeof(grpc_access_token_credentials));
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700519 GRPC_API_TRACE(
yang-g414af9b2016-11-12 01:09:51 -0800520 "grpc_access_token_credentials_create(access_token=<redacted>, "
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700521 "reserved=%p)",
yang-g414af9b2016-11-12 01:09:51 -0800522 1, (reserved));
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700523 GPR_ASSERT(reserved == NULL);
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700524 c->base.type = GRPC_CALL_CREDENTIALS_TYPE_OAUTH2;
525 c->base.vtable = &access_token_vtable;
526 gpr_ref_init(&c->base.refcount, 1);
Mark D. Rothe0778b22017-07-21 15:42:00 -0700527 char *token_md_value;
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700528 gpr_asprintf(&token_md_value, "Bearer %s", access_token);
Mark D. Rothe0778b22017-07-21 15:42:00 -0700529 grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
530 c->access_token_md = grpc_mdelem_from_slices(
531 &exec_ctx, grpc_slice_from_static_string(GRPC_AUTHORIZATION_METADATA_KEY),
532 grpc_slice_from_copied_string(token_md_value));
533 grpc_exec_ctx_finish(&exec_ctx);
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700534 gpr_free(token_md_value);
535 return &c->base;
536}