Julien Boeuf | 8ca294e | 2016-05-02 14:56:30 -0700 | [diff] [blame] | 1 | /* |
| 2 | * |
Jan Tattermusch | 7897ae9 | 2017-06-07 22:57:36 +0200 | [diff] [blame] | 3 | * Copyright 2015 gRPC authors. |
Julien Boeuf | 8ca294e | 2016-05-02 14:56:30 -0700 | [diff] [blame] | 4 | * |
Jan Tattermusch | 7897ae9 | 2017-06-07 22:57:36 +0200 | [diff] [blame] | 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 |
Julien Boeuf | 8ca294e | 2016-05-02 14:56:30 -0700 | [diff] [blame] | 8 | * |
Jan Tattermusch | 7897ae9 | 2017-06-07 22:57:36 +0200 | [diff] [blame] | 9 | * http://www.apache.org/licenses/LICENSE-2.0 |
Julien Boeuf | 8ca294e | 2016-05-02 14:56:30 -0700 | [diff] [blame] | 10 | * |
Jan Tattermusch | 7897ae9 | 2017-06-07 22:57:36 +0200 | [diff] [blame] | 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. |
Julien Boeuf | 8ca294e | 2016-05-02 14:56:30 -0700 | [diff] [blame] | 16 | * |
| 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 | |
| 34 | int 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 | |
| 40 | grpc_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 | |
| 69 | end: |
| 70 | if (!success) grpc_auth_refresh_token_destruct(&result); |
| 71 | return result; |
| 72 | } |
| 73 | |
| 74 | grpc_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 | |
| 85 | void 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 Tiller | bd1795c | 2016-10-31 15:30:00 -0700 | [diff] [blame] | 106 | static void oauth2_token_fetcher_destruct(grpc_exec_ctx *exec_ctx, |
| 107 | grpc_call_credentials *creds) { |
Julien Boeuf | 8ca294e | 2016-05-02 14:56:30 -0700 | [diff] [blame] | 108 | grpc_oauth2_token_fetcher_credentials *c = |
| 109 | (grpc_oauth2_token_fetcher_credentials *)creds; |
Mark D. Roth | e0778b2 | 2017-07-21 15:42:00 -0700 | [diff] [blame] | 110 | GRPC_MDELEM_UNREF(exec_ctx, c->access_token_md); |
Julien Boeuf | 8ca294e | 2016-05-02 14:56:30 -0700 | [diff] [blame] | 111 | gpr_mu_destroy(&c->mu); |
Yuchen Zeng | a0a7b57 | 2017-08-09 10:33:07 -0700 | [diff] [blame] | 112 | grpc_pollset_set_destroy(exec_ctx, |
| 113 | grpc_polling_entity_pollset_set(&c->pollent)); |
Craig Tiller | 9e5ac1b | 2017-02-14 22:25:50 -0800 | [diff] [blame] | 114 | grpc_httpcli_context_destroy(exec_ctx, &c->httpcli_context); |
Julien Boeuf | 8ca294e | 2016-05-02 14:56:30 -0700 | [diff] [blame] | 115 | } |
| 116 | |
| 117 | grpc_credentials_status |
| 118 | grpc_oauth2_token_fetcher_credentials_parse_server_response( |
Craig Tiller | bd1795c | 2016-10-31 15:30:00 -0700 | [diff] [blame] | 119 | grpc_exec_ctx *exec_ctx, const grpc_http_response *response, |
Mark D. Roth | e0778b2 | 2017-07-21 15:42:00 -0700 | [diff] [blame] | 120 | grpc_mdelem *token_md, gpr_timespec *token_lifetime) { |
Julien Boeuf | 8ca294e | 2016-05-02 14:56:30 -0700 | [diff] [blame] | 121 | 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. Roth | e0778b2 | 2017-07-21 15:42:00 -0700 | [diff] [blame] | 189 | 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 Boeuf | 8ca294e | 2016-05-02 14:56:30 -0700 | [diff] [blame] | 194 | status = GRPC_CREDENTIALS_OK; |
| 195 | } |
| 196 | |
| 197 | end: |
Mark D. Roth | e0778b2 | 2017-07-21 15:42:00 -0700 | [diff] [blame] | 198 | if (status != GRPC_CREDENTIALS_OK && !GRPC_MDISNULL(*token_md)) { |
| 199 | GRPC_MDELEM_UNREF(exec_ctx, *token_md); |
| 200 | *token_md = GRPC_MDNULL; |
Julien Boeuf | 8ca294e | 2016-05-02 14:56:30 -0700 | [diff] [blame] | 201 | } |
| 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 Tiller | 804ff71 | 2016-05-05 16:25:40 -0700 | [diff] [blame] | 208 | static void on_oauth2_token_fetcher_http_response(grpc_exec_ctx *exec_ctx, |
| 209 | void *user_data, |
| 210 | grpc_error *error) { |
Mark D. Roth | e0778b2 | 2017-07-21 15:42:00 -0700 | [diff] [blame] | 211 | GRPC_LOG_IF_ERROR("oauth_fetch", GRPC_ERROR_REF(error)); |
Julien Boeuf | 8ca294e | 2016-05-02 14:56:30 -0700 | [diff] [blame] | 212 | 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. Roth | e0778b2 | 2017-07-21 15:42:00 -0700 | [diff] [blame] | 216 | grpc_mdelem access_token_md = GRPC_MDNULL; |
Julien Boeuf | 8ca294e | 2016-05-02 14:56:30 -0700 | [diff] [blame] | 217 | gpr_timespec token_lifetime; |
Mark D. Roth | e0778b2 | 2017-07-21 15:42:00 -0700 | [diff] [blame] | 218 | 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 Boeuf | 8ca294e | 2016-05-02 14:56:30 -0700 | [diff] [blame] | 222 | gpr_mu_lock(&c->mu); |
Mark D. Roth | e0778b2 | 2017-07-21 15:42:00 -0700 | [diff] [blame] | 223 | 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 Boeuf | 8ca294e | 2016-05-02 14:56:30 -0700 | [diff] [blame] | 232 | gpr_mu_unlock(&c->mu); |
Mark D. Roth | e0778b2 | 2017-07-21 15:42:00 -0700 | [diff] [blame] | 233 | // 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 Zeng | a0a7b57 | 2017-08-09 10:33:07 -0700 | [diff] [blame] | 243 | grpc_polling_entity_del_from_pollset_set( |
| 244 | exec_ctx, pending_request->pollent, |
| 245 | grpc_polling_entity_pollset_set(&c->pollent)); |
Mark D. Roth | e0778b2 | 2017-07-21 15:42:00 -0700 | [diff] [blame] | 246 | 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 Tiller | bd1795c | 2016-10-31 15:30:00 -0700 | [diff] [blame] | 252 | grpc_credentials_metadata_request_destroy(exec_ctx, r); |
Julien Boeuf | 8ca294e | 2016-05-02 14:56:30 -0700 | [diff] [blame] | 253 | } |
| 254 | |
Mark D. Roth | e0778b2 | 2017-07-21 15:42:00 -0700 | [diff] [blame] | 255 | static bool oauth2_token_fetcher_get_request_metadata( |
Julien Boeuf | 8ca294e | 2016-05-02 14:56:30 -0700 | [diff] [blame] | 256 | grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, |
David Garcia Quintas | 2a50dfe | 2016-05-31 15:09:12 -0700 | [diff] [blame] | 257 | grpc_polling_entity *pollent, grpc_auth_metadata_context context, |
Mark D. Roth | e0778b2 | 2017-07-21 15:42:00 -0700 | [diff] [blame] | 258 | grpc_credentials_mdelem_array *md_array, grpc_closure *on_request_metadata, |
| 259 | grpc_error **error) { |
Julien Boeuf | 8ca294e | 2016-05-02 14:56:30 -0700 | [diff] [blame] | 260 | grpc_oauth2_token_fetcher_credentials *c = |
| 261 | (grpc_oauth2_token_fetcher_credentials *)creds; |
Mark D. Roth | e0778b2 | 2017-07-21 15:42:00 -0700 | [diff] [blame] | 262 | // Check if we can use the cached token. |
Julien Boeuf | 8ca294e | 2016-05-02 14:56:30 -0700 | [diff] [blame] | 263 | gpr_timespec refresh_threshold = gpr_time_from_seconds( |
| 264 | GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS, GPR_TIMESPAN); |
Mark D. Roth | e0778b2 | 2017-07-21 15:42:00 -0700 | [diff] [blame] | 265 | 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 Boeuf | 8ca294e | 2016-05-02 14:56:30 -0700 | [diff] [blame] | 274 | gpr_mu_unlock(&c->mu); |
Mark D. Roth | e0778b2 | 2017-07-21 15:42:00 -0700 | [diff] [blame] | 275 | 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 Boeuf | 8ca294e | 2016-05-02 14:56:30 -0700 | [diff] [blame] | 278 | } |
Mark D. Roth | e0778b2 | 2017-07-21 15:42:00 -0700 | [diff] [blame] | 279 | // 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 Zeng | a0a7b57 | 2017-08-09 10:33:07 -0700 | [diff] [blame] | 286 | 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. Roth | e0778b2 | 2017-07-21 15:42:00 -0700 | [diff] [blame] | 289 | 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 Boeuf | 8ca294e | 2016-05-02 14:56:30 -0700 | [diff] [blame] | 295 | } |
Mark D. Roth | e0778b2 | 2017-07-21 15:42:00 -0700 | [diff] [blame] | 296 | gpr_mu_unlock(&c->mu); |
| 297 | if (start_fetch) { |
| 298 | grpc_call_credentials_ref(creds); |
Maxim Dzoba | 22cc56f | 2017-08-16 17:09:34 -0400 | [diff] [blame] | 299 | 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. Roth | e0778b2 | 2017-07-21 15:42:00 -0700 | [diff] [blame] | 303 | } |
| 304 | return false; |
| 305 | } |
| 306 | |
| 307 | static 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 Boeuf | 8ca294e | 2016-05-02 14:56:30 -0700 | [diff] [blame] | 335 | } |
| 336 | |
| 337 | static 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 Zeng | a0a7b57 | 2017-08-09 10:33:07 -0700 | [diff] [blame] | 345 | c->pollent = |
| 346 | grpc_polling_entity_create_from_pollset_set(grpc_pollset_set_create()); |
Julien Boeuf | 8ca294e | 2016-05-02 14:56:30 -0700 | [diff] [blame] | 347 | grpc_httpcli_context_init(&c->httpcli_context); |
| 348 | } |
| 349 | |
| 350 | // |
| 351 | // Google Compute Engine credentials. |
| 352 | // |
| 353 | |
| 354 | static grpc_call_credentials_vtable compute_engine_vtable = { |
Mark D. Roth | e0778b2 | 2017-07-21 15:42:00 -0700 | [diff] [blame] | 355 | oauth2_token_fetcher_destruct, oauth2_token_fetcher_get_request_metadata, |
| 356 | oauth2_token_fetcher_cancel_get_request_metadata}; |
Julien Boeuf | 8ca294e | 2016-05-02 14:56:30 -0700 | [diff] [blame] | 357 | |
| 358 | static void compute_engine_fetch_oauth2( |
| 359 | grpc_exec_ctx *exec_ctx, grpc_credentials_metadata_request *metadata_req, |
David Garcia Quintas | 2a50dfe | 2016-05-31 15:09:12 -0700 | [diff] [blame] | 360 | grpc_httpcli_context *httpcli_context, grpc_polling_entity *pollent, |
Craig Tiller | 804ff71 | 2016-05-05 16:25:40 -0700 | [diff] [blame] | 361 | grpc_iomgr_cb_func response_cb, gpr_timespec deadline) { |
Julien Boeuf | 8ca294e | 2016-05-02 14:56:30 -0700 | [diff] [blame] | 362 | 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 Tiller | 20afa3d | 2016-10-17 14:52:14 -0700 | [diff] [blame] | 369 | /* TODO(ctiller): Carry the resource_quota in ctx and share it with the host |
Craig Tiller | 4588186 | 2016-09-23 10:48:53 -0700 | [diff] [blame] | 370 | channel. This would allow us to cancel an authentication query when under |
| 371 | extreme memory pressure. */ |
Craig Tiller | afcc875 | 2016-10-18 16:10:06 -0700 | [diff] [blame] | 372 | grpc_resource_quota *resource_quota = |
| 373 | grpc_resource_quota_create("oauth2_credentials"); |
Craig Tiller | 91031da | 2016-12-28 15:44:25 -0800 | [diff] [blame] | 374 | grpc_httpcli_get( |
| 375 | exec_ctx, httpcli_context, pollent, resource_quota, &request, deadline, |
ncteisen | 274bbbe | 2017-06-08 14:57:11 -0700 | [diff] [blame] | 376 | GRPC_CLOSURE_CREATE(response_cb, metadata_req, grpc_schedule_on_exec_ctx), |
Craig Tiller | 91031da | 2016-12-28 15:44:25 -0800 | [diff] [blame] | 377 | &metadata_req->response); |
Craig Tiller | a59c16c | 2016-10-31 07:25:01 -0700 | [diff] [blame] | 378 | grpc_resource_quota_unref_internal(exec_ctx, resource_quota); |
Julien Boeuf | 8ca294e | 2016-05-02 14:56:30 -0700 | [diff] [blame] | 379 | } |
| 380 | |
| 381 | grpc_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 Tiller | bd1795c | 2016-10-31 15:30:00 -0700 | [diff] [blame] | 397 | static void refresh_token_destruct(grpc_exec_ctx *exec_ctx, |
| 398 | grpc_call_credentials *creds) { |
Julien Boeuf | 8ca294e | 2016-05-02 14:56:30 -0700 | [diff] [blame] | 399 | grpc_google_refresh_token_credentials *c = |
| 400 | (grpc_google_refresh_token_credentials *)creds; |
| 401 | grpc_auth_refresh_token_destruct(&c->refresh_token); |
Craig Tiller | bd1795c | 2016-10-31 15:30:00 -0700 | [diff] [blame] | 402 | oauth2_token_fetcher_destruct(exec_ctx, &c->base.base); |
Julien Boeuf | 8ca294e | 2016-05-02 14:56:30 -0700 | [diff] [blame] | 403 | } |
| 404 | |
| 405 | static grpc_call_credentials_vtable refresh_token_vtable = { |
Mark D. Roth | e0778b2 | 2017-07-21 15:42:00 -0700 | [diff] [blame] | 406 | refresh_token_destruct, oauth2_token_fetcher_get_request_metadata, |
| 407 | oauth2_token_fetcher_cancel_get_request_metadata}; |
Julien Boeuf | 8ca294e | 2016-05-02 14:56:30 -0700 | [diff] [blame] | 408 | |
| 409 | static void refresh_token_fetch_oauth2( |
| 410 | grpc_exec_ctx *exec_ctx, grpc_credentials_metadata_request *metadata_req, |
David Garcia Quintas | 2a50dfe | 2016-05-31 15:09:12 -0700 | [diff] [blame] | 411 | grpc_httpcli_context *httpcli_context, grpc_polling_entity *pollent, |
Craig Tiller | 804ff71 | 2016-05-05 16:25:40 -0700 | [diff] [blame] | 412 | grpc_iomgr_cb_func response_cb, gpr_timespec deadline) { |
Julien Boeuf | 8ca294e | 2016-05-02 14:56:30 -0700 | [diff] [blame] | 413 | 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 Tiller | 20afa3d | 2016-10-17 14:52:14 -0700 | [diff] [blame] | 428 | /* TODO(ctiller): Carry the resource_quota in ctx and share it with the host |
Craig Tiller | 4588186 | 2016-09-23 10:48:53 -0700 | [diff] [blame] | 429 | channel. This would allow us to cancel an authentication query when under |
| 430 | extreme memory pressure. */ |
Craig Tiller | 20afa3d | 2016-10-17 14:52:14 -0700 | [diff] [blame] | 431 | grpc_resource_quota *resource_quota = |
| 432 | grpc_resource_quota_create("oauth2_credentials_refresh"); |
Craig Tiller | 91031da | 2016-12-28 15:44:25 -0800 | [diff] [blame] | 433 | grpc_httpcli_post( |
| 434 | exec_ctx, httpcli_context, pollent, resource_quota, &request, body, |
| 435 | strlen(body), deadline, |
ncteisen | 274bbbe | 2017-06-08 14:57:11 -0700 | [diff] [blame] | 436 | GRPC_CLOSURE_CREATE(response_cb, metadata_req, grpc_schedule_on_exec_ctx), |
Craig Tiller | 91031da | 2016-12-28 15:44:25 -0800 | [diff] [blame] | 437 | &metadata_req->response); |
Craig Tiller | a59c16c | 2016-10-31 07:25:01 -0700 | [diff] [blame] | 438 | grpc_resource_quota_unref_internal(exec_ctx, resource_quota); |
Julien Boeuf | 8ca294e | 2016-05-02 14:56:30 -0700 | [diff] [blame] | 439 | gpr_free(body); |
| 440 | } |
| 441 | |
| 442 | grpc_call_credentials * |
| 443 | grpc_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 Tiller | 6f41788 | 2017-02-16 14:09:39 -0800 | [diff] [blame] | 450 | c = gpr_zalloc(sizeof(grpc_google_refresh_token_credentials)); |
Julien Boeuf | 8ca294e | 2016-05-02 14:56:30 -0700 | [diff] [blame] | 451 | 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-g | 414af9b | 2016-11-12 01:09:51 -0800 | [diff] [blame] | 457 | static 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 Boeuf | 8ca294e | 2016-05-02 14:56:30 -0700 | [diff] [blame] | 469 | grpc_call_credentials *grpc_google_refresh_token_credentials_create( |
| 470 | const char *json_refresh_token, void *reserved) { |
yang-g | 414af9b | 2016-11-12 01:09:51 -0800 | [diff] [blame] | 471 | grpc_auth_refresh_token token = |
| 472 | grpc_auth_refresh_token_create_from_string(json_refresh_token); |
Craig Tiller | 84f75d4 | 2017-05-03 13:06:35 -0700 | [diff] [blame] | 473 | if (GRPC_TRACER_ON(grpc_api_trace)) { |
yang-g | 414af9b | 2016-11-12 01:09:51 -0800 | [diff] [blame] | 474 | 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 Boeuf | 8ca294e | 2016-05-02 14:56:30 -0700 | [diff] [blame] | 481 | GPR_ASSERT(reserved == NULL); |
yang-g | 414af9b | 2016-11-12 01:09:51 -0800 | [diff] [blame] | 482 | return grpc_refresh_token_credentials_create_from_auth_refresh_token(token); |
Julien Boeuf | 8ca294e | 2016-05-02 14:56:30 -0700 | [diff] [blame] | 483 | } |
| 484 | |
| 485 | // |
| 486 | // Oauth2 Access Token credentials. |
| 487 | // |
| 488 | |
Craig Tiller | bd1795c | 2016-10-31 15:30:00 -0700 | [diff] [blame] | 489 | static void access_token_destruct(grpc_exec_ctx *exec_ctx, |
| 490 | grpc_call_credentials *creds) { |
Julien Boeuf | 8ca294e | 2016-05-02 14:56:30 -0700 | [diff] [blame] | 491 | grpc_access_token_credentials *c = (grpc_access_token_credentials *)creds; |
Mark D. Roth | e0778b2 | 2017-07-21 15:42:00 -0700 | [diff] [blame] | 492 | GRPC_MDELEM_UNREF(exec_ctx, c->access_token_md); |
Julien Boeuf | 8ca294e | 2016-05-02 14:56:30 -0700 | [diff] [blame] | 493 | } |
| 494 | |
Mark D. Roth | e0778b2 | 2017-07-21 15:42:00 -0700 | [diff] [blame] | 495 | static bool access_token_get_request_metadata( |
Julien Boeuf | 8ca294e | 2016-05-02 14:56:30 -0700 | [diff] [blame] | 496 | grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, |
David Garcia Quintas | 2a50dfe | 2016-05-31 15:09:12 -0700 | [diff] [blame] | 497 | grpc_polling_entity *pollent, grpc_auth_metadata_context context, |
Mark D. Roth | e0778b2 | 2017-07-21 15:42:00 -0700 | [diff] [blame] | 498 | grpc_credentials_mdelem_array *md_array, grpc_closure *on_request_metadata, |
| 499 | grpc_error **error) { |
Julien Boeuf | 8ca294e | 2016-05-02 14:56:30 -0700 | [diff] [blame] | 500 | grpc_access_token_credentials *c = (grpc_access_token_credentials *)creds; |
Mark D. Roth | e0778b2 | 2017-07-21 15:42:00 -0700 | [diff] [blame] | 501 | grpc_credentials_mdelem_array_add(md_array, c->access_token_md); |
| 502 | return true; |
| 503 | } |
| 504 | |
| 505 | static 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 Boeuf | 8ca294e | 2016-05-02 14:56:30 -0700 | [diff] [blame] | 509 | } |
| 510 | |
| 511 | static grpc_call_credentials_vtable access_token_vtable = { |
Mark D. Roth | e0778b2 | 2017-07-21 15:42:00 -0700 | [diff] [blame] | 512 | access_token_destruct, access_token_get_request_metadata, |
| 513 | access_token_cancel_get_request_metadata}; |
Julien Boeuf | 8ca294e | 2016-05-02 14:56:30 -0700 | [diff] [blame] | 514 | |
| 515 | grpc_call_credentials *grpc_access_token_credentials_create( |
| 516 | const char *access_token, void *reserved) { |
| 517 | grpc_access_token_credentials *c = |
Craig Tiller | 6f41788 | 2017-02-16 14:09:39 -0800 | [diff] [blame] | 518 | gpr_zalloc(sizeof(grpc_access_token_credentials)); |
Julien Boeuf | 8ca294e | 2016-05-02 14:56:30 -0700 | [diff] [blame] | 519 | GRPC_API_TRACE( |
yang-g | 414af9b | 2016-11-12 01:09:51 -0800 | [diff] [blame] | 520 | "grpc_access_token_credentials_create(access_token=<redacted>, " |
Julien Boeuf | 8ca294e | 2016-05-02 14:56:30 -0700 | [diff] [blame] | 521 | "reserved=%p)", |
yang-g | 414af9b | 2016-11-12 01:09:51 -0800 | [diff] [blame] | 522 | 1, (reserved)); |
Julien Boeuf | 8ca294e | 2016-05-02 14:56:30 -0700 | [diff] [blame] | 523 | GPR_ASSERT(reserved == NULL); |
Julien Boeuf | 8ca294e | 2016-05-02 14:56:30 -0700 | [diff] [blame] | 524 | 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. Roth | e0778b2 | 2017-07-21 15:42:00 -0700 | [diff] [blame] | 527 | char *token_md_value; |
Julien Boeuf | 8ca294e | 2016-05-02 14:56:30 -0700 | [diff] [blame] | 528 | gpr_asprintf(&token_md_value, "Bearer %s", access_token); |
Mark D. Roth | e0778b2 | 2017-07-21 15:42:00 -0700 | [diff] [blame] | 529 | 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 Boeuf | 8ca294e | 2016-05-02 14:56:30 -0700 | [diff] [blame] | 534 | gpr_free(token_md_value); |
| 535 | return &c->base; |
| 536 | } |