credentials_provider_cognito.c 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859
  1. /**
  2. * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  3. * SPDX-License-Identifier: Apache-2.0.
  4. */
  5. #include <aws/auth/credentials.h>
  6. #include <aws/auth/private/credentials_utils.h>
  7. #include <aws/common/clock.h>
  8. #include <aws/common/json.h>
  9. #include <aws/common/string.h>
  10. #include <aws/http/connection.h>
  11. #include <aws/http/connection_manager.h>
  12. #include <aws/http/request_response.h>
  13. #include <aws/http/status_code.h>
  14. #include <aws/io/channel_bootstrap.h>
  15. #include <aws/io/retry_strategy.h>
  16. #include <aws/io/socket.h>
  17. #include <aws/io/stream.h>
  18. #include <aws/io/tls_channel_handler.h>
  19. #include <inttypes.h>
  20. #define COGNITO_CONNECT_TIMEOUT_DEFAULT_IN_SECONDS 5
  21. #define COGNITO_MAX_RETRIES 8
  22. #define HTTP_REQUEST_BODY_INITIAL_SIZE 1024
  23. #define HTTP_RESPONSE_BODY_INITIAL_SIZE 4096
  24. static void s_on_connection_manager_shutdown(void *user_data);
  25. static void s_on_connection_setup_fn(struct aws_http_connection *connection, int error_code, void *user_data);
  26. struct aws_cognito_login {
  27. struct aws_byte_cursor identity_provider_name;
  28. struct aws_byte_cursor identity_provider_token;
  29. struct aws_byte_buf login_buffer;
  30. };
  31. static int s_aws_cognito_login_init(
  32. struct aws_cognito_login *login,
  33. struct aws_allocator *allocator,
  34. struct aws_byte_cursor identity_provider_name,
  35. struct aws_byte_cursor identity_provider_token) {
  36. AWS_ZERO_STRUCT(*login);
  37. login->identity_provider_name = identity_provider_name;
  38. login->identity_provider_token = identity_provider_token;
  39. return aws_byte_buf_init_cache_and_update_cursors(
  40. &login->login_buffer, allocator, &login->identity_provider_name, &login->identity_provider_token, NULL);
  41. }
  42. static void s_aws_cognito_login_clean_up(struct aws_cognito_login *login) {
  43. aws_byte_buf_clean_up(&login->login_buffer);
  44. AWS_ZERO_STRUCT(*login);
  45. }
  46. struct aws_credentials_provider_cognito_impl {
  47. struct aws_http_connection_manager *connection_manager;
  48. struct aws_retry_strategy *retry_strategy;
  49. const struct aws_auth_http_system_vtable *function_table;
  50. struct aws_string *endpoint;
  51. struct aws_string *identity;
  52. struct aws_array_list logins;
  53. struct aws_string *custom_role_arn;
  54. };
  55. struct cognito_user_data {
  56. struct aws_allocator *allocator;
  57. struct aws_credentials_provider *provider;
  58. aws_on_get_credentials_callback_fn *original_callback;
  59. void *original_user_data;
  60. struct aws_http_connection *connection;
  61. struct aws_http_message *get_credentials_request;
  62. struct aws_byte_buf request_body_buffer;
  63. struct aws_input_stream *request_body_stream;
  64. struct aws_retry_token *retry_token;
  65. struct aws_credentials *credentials;
  66. struct aws_byte_buf response_body;
  67. };
  68. static void s_user_data_reset(struct cognito_user_data *user_data) {
  69. aws_byte_buf_clean_up(&user_data->request_body_buffer);
  70. user_data->request_body_stream = aws_input_stream_release(user_data->request_body_stream);
  71. user_data->get_credentials_request = aws_http_message_release(user_data->get_credentials_request);
  72. struct aws_credentials_provider_cognito_impl *impl = user_data->provider->impl;
  73. if (user_data->connection != NULL) {
  74. impl->function_table->aws_http_connection_manager_release_connection(
  75. impl->connection_manager, user_data->connection);
  76. user_data->connection = NULL;
  77. }
  78. aws_byte_buf_reset(&user_data->response_body, false);
  79. }
  80. static void s_user_data_destroy(struct cognito_user_data *user_data) {
  81. if (user_data == NULL) {
  82. return;
  83. }
  84. s_user_data_reset(user_data);
  85. aws_byte_buf_clean_up(&user_data->response_body);
  86. aws_retry_token_release(user_data->retry_token);
  87. aws_credentials_provider_release(user_data->provider);
  88. aws_credentials_release(user_data->credentials);
  89. aws_mem_release(user_data->allocator, user_data);
  90. }
  91. static struct cognito_user_data *s_user_data_new(
  92. struct aws_credentials_provider *provider,
  93. aws_on_get_credentials_callback_fn callback,
  94. void *user_data) {
  95. struct aws_allocator *allocator = provider->allocator;
  96. struct cognito_user_data *cognito_user_data = aws_mem_calloc(allocator, 1, sizeof(struct cognito_user_data));
  97. cognito_user_data->allocator = allocator;
  98. aws_byte_buf_init(&cognito_user_data->response_body, cognito_user_data->allocator, HTTP_RESPONSE_BODY_INITIAL_SIZE);
  99. cognito_user_data->provider = aws_credentials_provider_acquire(provider);
  100. cognito_user_data->original_callback = callback;
  101. cognito_user_data->original_user_data = user_data;
  102. return cognito_user_data;
  103. }
  104. static void s_finalize_credentials_query(struct cognito_user_data *user_data, int error_code) {
  105. AWS_FATAL_ASSERT(user_data != NULL);
  106. if (user_data->credentials == NULL && error_code == AWS_ERROR_SUCCESS) {
  107. error_code = AWS_AUTH_CREDENTIALS_PROVIDER_COGNITO_SOURCE_FAILURE;
  108. }
  109. (user_data->original_callback)(user_data->credentials, error_code, user_data->original_user_data);
  110. s_user_data_destroy(user_data);
  111. }
  112. /* Keys per Cognito-Identity service model */
  113. AWS_STATIC_STRING_FROM_LITERAL(s_credentials_key, "Credentials");
  114. AWS_STATIC_STRING_FROM_LITERAL(s_access_key_id_name, "AccessKeyId");
  115. AWS_STATIC_STRING_FROM_LITERAL(s_secret_access_key_name, "SecretKey");
  116. AWS_STATIC_STRING_FROM_LITERAL(s_session_token_name, "SessionToken");
  117. AWS_STATIC_STRING_FROM_LITERAL(s_expiration_name, "Expiration");
  118. static int s_parse_credentials_from_response(struct cognito_user_data *user_data) {
  119. int result = AWS_OP_ERR;
  120. struct aws_json_value *response_document =
  121. aws_json_value_new_from_string(user_data->allocator, aws_byte_cursor_from_buf(&user_data->response_body));
  122. if (response_document == NULL) {
  123. goto done;
  124. }
  125. struct aws_json_value *credentials_entry =
  126. aws_json_value_get_from_object(response_document, aws_byte_cursor_from_string(s_credentials_key));
  127. if (credentials_entry == NULL) {
  128. goto done;
  129. }
  130. struct aws_parse_credentials_from_json_doc_options credentials_parse_options = {
  131. .access_key_id_name = aws_string_c_str(s_access_key_id_name),
  132. .secret_access_key_name = aws_string_c_str(s_secret_access_key_name),
  133. .token_name = aws_string_c_str(s_session_token_name),
  134. .expiration_name = aws_string_c_str(s_expiration_name),
  135. .expiration_format = AWS_PCEF_NUMBER_UNIX_EPOCH,
  136. .token_required = true,
  137. .expiration_required = true,
  138. };
  139. user_data->credentials =
  140. aws_parse_credentials_from_aws_json_object(user_data->allocator, credentials_entry, &credentials_parse_options);
  141. if (user_data->credentials == NULL) {
  142. goto done;
  143. }
  144. result = AWS_OP_SUCCESS;
  145. done:
  146. aws_json_value_destroy(response_document);
  147. if (result != AWS_OP_SUCCESS) {
  148. aws_raise_error(AWS_AUTH_PROVIDER_PARSER_UNEXPECTED_RESPONSE);
  149. }
  150. return result;
  151. }
  152. static void s_on_retry_ready(struct aws_retry_token *token, int error_code, void *user_data) {
  153. (void)token;
  154. struct cognito_user_data *provider_user_data = user_data;
  155. if (error_code != AWS_ERROR_SUCCESS) {
  156. AWS_LOGF_ERROR(
  157. AWS_LS_AUTH_CREDENTIALS_PROVIDER,
  158. "(id=%p): Cognito credentials provider retry task failed: %s",
  159. (void *)provider_user_data->provider,
  160. aws_error_str(error_code));
  161. s_finalize_credentials_query(user_data, error_code);
  162. return;
  163. }
  164. s_user_data_reset(provider_user_data);
  165. struct aws_credentials_provider_cognito_impl *impl = provider_user_data->provider->impl;
  166. impl->function_table->aws_http_connection_manager_acquire_connection(
  167. impl->connection_manager, s_on_connection_setup_fn, provider_user_data);
  168. }
  169. static void s_on_stream_complete_fn(struct aws_http_stream *stream, int error_code, void *user_data) {
  170. struct cognito_user_data *provider_user_data = user_data;
  171. struct aws_credentials_provider_cognito_impl *impl = provider_user_data->provider->impl;
  172. int http_response_code = 0;
  173. impl->function_table->aws_http_stream_get_incoming_response_status(stream, &http_response_code);
  174. if (http_response_code != 200) {
  175. error_code = AWS_AUTH_CREDENTIALS_PROVIDER_HTTP_STATUS_FAILURE;
  176. }
  177. impl->function_table->aws_http_stream_release(stream);
  178. AWS_LOGF_DEBUG(
  179. AWS_LS_AUTH_CREDENTIALS_PROVIDER,
  180. "(id=%p): GetCredentialsForIdentity call completed with http status %d",
  181. (void *)provider_user_data->provider,
  182. http_response_code);
  183. if (http_response_code == AWS_HTTP_STATUS_CODE_200_OK) {
  184. aws_retry_token_record_success(provider_user_data->retry_token);
  185. if (s_parse_credentials_from_response(provider_user_data) == AWS_OP_SUCCESS) {
  186. s_finalize_credentials_query(user_data, AWS_ERROR_SUCCESS);
  187. return;
  188. }
  189. AWS_LOGF_ERROR(
  190. AWS_LS_AUTH_CREDENTIALS_PROVIDER,
  191. "(id=%p): Cognito credentials provider failed to parse GetCredentialsForIdentity response",
  192. (void *)provider_user_data->provider);
  193. error_code = AWS_AUTH_PROVIDER_PARSER_UNEXPECTED_RESPONSE;
  194. }
  195. /* Success path is done, error-only from here on out */
  196. /* Unsure if this should be unconditional or a function of status code. STS does this unconditionally. */
  197. impl->function_table->aws_http_connection_close(provider_user_data->connection);
  198. enum aws_retry_error_type error_type =
  199. aws_credentials_provider_compute_retry_error_type(http_response_code, error_code);
  200. bool can_retry = http_response_code == 0 || error_type != AWS_RETRY_ERROR_TYPE_CLIENT_ERROR;
  201. if (!can_retry) {
  202. s_finalize_credentials_query(user_data, error_code);
  203. return;
  204. }
  205. if (aws_retry_strategy_schedule_retry(
  206. provider_user_data->retry_token, error_type, s_on_retry_ready, provider_user_data)) {
  207. error_code = aws_last_error();
  208. AWS_LOGF_ERROR(
  209. AWS_LS_AUTH_CREDENTIALS_PROVIDER,
  210. "(id=%p): Cognito credentials provider failed to schedule retry: %s",
  211. (void *)provider_user_data->provider,
  212. aws_error_str(error_code));
  213. s_finalize_credentials_query(user_data, error_code);
  214. return;
  215. }
  216. }
  217. static int s_on_incoming_body_fn(struct aws_http_stream *stream, const struct aws_byte_cursor *data, void *user_data) {
  218. (void)stream;
  219. struct cognito_user_data *provider_user_data = user_data;
  220. return aws_byte_buf_append_dynamic(&provider_user_data->response_body, data);
  221. }
  222. AWS_STATIC_STRING_FROM_LITERAL(s_identity_id_key, "IdentityId");
  223. AWS_STATIC_STRING_FROM_LITERAL(s_custom_role_arn_key, "CustomRoleArn");
  224. AWS_STATIC_STRING_FROM_LITERAL(s_logins_key, "Logins");
  225. int s_create_get_credentials_for_identity_body_buffer(
  226. struct aws_byte_buf *buffer,
  227. struct cognito_user_data *provider_user_data) {
  228. struct aws_allocator *allocator = provider_user_data->allocator;
  229. struct aws_credentials_provider_cognito_impl *impl = provider_user_data->provider->impl;
  230. int result = AWS_OP_ERR;
  231. struct aws_json_value *json_body = aws_json_value_new_object(allocator);
  232. if (json_body == NULL) {
  233. return AWS_OP_ERR;
  234. }
  235. struct aws_json_value *identity_string =
  236. aws_json_value_new_string(allocator, aws_byte_cursor_from_string(impl->identity));
  237. if (identity_string == NULL) {
  238. goto done;
  239. }
  240. if (aws_json_value_add_to_object(json_body, aws_byte_cursor_from_string(s_identity_id_key), identity_string)) {
  241. aws_json_value_destroy(identity_string);
  242. goto done;
  243. }
  244. if (impl->custom_role_arn != NULL) {
  245. struct aws_json_value *custom_role_arn_string =
  246. aws_json_value_new_string(allocator, aws_byte_cursor_from_string(impl->custom_role_arn));
  247. if (custom_role_arn_string == NULL) {
  248. goto done;
  249. }
  250. if (aws_json_value_add_to_object(
  251. json_body, aws_byte_cursor_from_string(s_custom_role_arn_key), custom_role_arn_string)) {
  252. aws_json_value_destroy(custom_role_arn_string);
  253. goto done;
  254. }
  255. }
  256. size_t login_count = aws_array_list_length(&impl->logins);
  257. if (login_count > 0) {
  258. struct aws_json_value *logins = aws_json_value_new_object(allocator);
  259. if (logins == NULL) {
  260. goto done;
  261. }
  262. if (aws_json_value_add_to_object(json_body, aws_byte_cursor_from_string(s_logins_key), logins)) {
  263. aws_json_value_destroy(logins);
  264. goto done;
  265. }
  266. for (size_t i = 0; i < login_count; ++i) {
  267. struct aws_cognito_login login;
  268. if (aws_array_list_get_at(&impl->logins, &login, i)) {
  269. goto done;
  270. }
  271. struct aws_json_value *login_value_string =
  272. aws_json_value_new_string(allocator, login.identity_provider_token);
  273. if (login_value_string == NULL) {
  274. goto done;
  275. }
  276. if (aws_json_value_add_to_object(logins, login.identity_provider_name, login_value_string)) {
  277. aws_json_value_destroy(login_value_string);
  278. goto done;
  279. }
  280. }
  281. }
  282. if (aws_byte_buf_append_json_string(json_body, buffer)) {
  283. goto done;
  284. }
  285. result = AWS_OP_SUCCESS;
  286. done:
  287. aws_json_value_destroy(json_body);
  288. return result;
  289. }
  290. static struct aws_http_header s_content_type_header = {
  291. .name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("content-type"),
  292. .value = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("application/x-amz-json-1.1"),
  293. };
  294. AWS_STATIC_STRING_FROM_LITERAL(s_get_credentials_for_identity_path, "/");
  295. static struct aws_http_header s_x_amz_target_header = {
  296. .name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("X-Amz-Target"),
  297. .value = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("AWSCognitoIdentityService.GetCredentialsForIdentity"),
  298. };
  299. static int s_create_get_credentials_for_identity_request(struct cognito_user_data *provider_user_data) {
  300. struct aws_credentials_provider_cognito_impl *impl = provider_user_data->provider->impl;
  301. struct aws_byte_buf body_buffer;
  302. AWS_ZERO_STRUCT(body_buffer);
  303. struct aws_input_stream *body_stream = NULL;
  304. struct aws_http_message *request = aws_http_message_new_request(provider_user_data->allocator);
  305. if (request == NULL) {
  306. return AWS_OP_ERR;
  307. }
  308. if (aws_http_message_set_request_method(request, aws_http_method_post)) {
  309. goto on_error;
  310. }
  311. if (aws_http_message_set_request_path(request, aws_byte_cursor_from_string(s_get_credentials_for_identity_path))) {
  312. goto on_error;
  313. }
  314. struct aws_http_header host_header = {
  315. .name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("host"),
  316. .value = aws_byte_cursor_from_string(impl->endpoint),
  317. };
  318. if (aws_http_message_add_header(request, host_header)) {
  319. goto on_error;
  320. }
  321. if (aws_http_message_add_header(request, s_content_type_header)) {
  322. goto on_error;
  323. }
  324. if (aws_http_message_add_header(request, s_x_amz_target_header)) {
  325. goto on_error;
  326. }
  327. if (aws_byte_buf_init(&body_buffer, provider_user_data->allocator, HTTP_REQUEST_BODY_INITIAL_SIZE)) {
  328. goto on_error;
  329. }
  330. if (s_create_get_credentials_for_identity_body_buffer(&body_buffer, provider_user_data)) {
  331. goto on_error;
  332. }
  333. char content_length[21];
  334. AWS_ZERO_ARRAY(content_length);
  335. snprintf(content_length, sizeof(content_length), "%" PRIu64, (uint64_t)body_buffer.len);
  336. struct aws_http_header content_length_header = {
  337. .name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Content-Length"),
  338. .value = aws_byte_cursor_from_c_str(content_length),
  339. };
  340. if (aws_http_message_add_header(request, content_length_header)) {
  341. goto on_error;
  342. }
  343. struct aws_byte_cursor payload_cur = aws_byte_cursor_from_buf(&body_buffer);
  344. body_stream = aws_input_stream_new_from_cursor(provider_user_data->allocator, &payload_cur);
  345. if (body_stream == NULL) {
  346. goto on_error;
  347. }
  348. aws_http_message_set_body_stream(request, body_stream);
  349. provider_user_data->get_credentials_request = request;
  350. provider_user_data->request_body_buffer = body_buffer;
  351. provider_user_data->request_body_stream = body_stream;
  352. return AWS_OP_SUCCESS;
  353. on_error:
  354. aws_byte_buf_clean_up(&body_buffer);
  355. aws_input_stream_release(body_stream);
  356. aws_http_message_release(request);
  357. return AWS_OP_ERR;
  358. }
  359. static void s_on_connection_setup_fn(struct aws_http_connection *connection, int error_code, void *user_data) {
  360. struct cognito_user_data *wrapped_user_data = user_data;
  361. struct aws_http_stream *stream = NULL;
  362. struct aws_credentials_provider_cognito_impl *impl = wrapped_user_data->provider->impl;
  363. if (connection == NULL) {
  364. AWS_LOGF_ERROR(
  365. AWS_LS_AUTH_CREDENTIALS_PROVIDER,
  366. "(id=%p): Cognito credentials provider failed to acquire http connection: %s",
  367. (void *)wrapped_user_data->provider,
  368. aws_error_debug_str(error_code));
  369. goto on_error;
  370. }
  371. wrapped_user_data->connection = connection;
  372. if (s_create_get_credentials_for_identity_request(wrapped_user_data)) {
  373. error_code = aws_last_error();
  374. AWS_LOGF_ERROR(
  375. AWS_LS_AUTH_CREDENTIALS_PROVIDER,
  376. "(id=%p): Cognito credentials provider failed to create http request: %s",
  377. (void *)wrapped_user_data->provider,
  378. aws_error_debug_str(error_code));
  379. goto on_error;
  380. }
  381. struct aws_http_make_request_options options = {
  382. .user_data = user_data,
  383. .request = wrapped_user_data->get_credentials_request,
  384. .self_size = sizeof(struct aws_http_make_request_options),
  385. .on_response_headers = NULL,
  386. .on_response_header_block_done = NULL,
  387. .on_response_body = s_on_incoming_body_fn,
  388. .on_complete = s_on_stream_complete_fn,
  389. };
  390. stream = impl->function_table->aws_http_connection_make_request(connection, &options);
  391. if (!stream) {
  392. error_code = aws_last_error();
  393. AWS_LOGF_ERROR(
  394. AWS_LS_AUTH_CREDENTIALS_PROVIDER,
  395. "(id=%p): Cognito credentials provider failed to create http stream: %s",
  396. (void *)wrapped_user_data->provider,
  397. aws_error_debug_str(error_code));
  398. goto on_error;
  399. }
  400. if (impl->function_table->aws_http_stream_activate(stream)) {
  401. error_code = aws_last_error();
  402. AWS_LOGF_ERROR(
  403. AWS_LS_AUTH_CREDENTIALS_PROVIDER,
  404. "(id=%p): Cognito credentials provider failed to activate http stream: %s",
  405. (void *)wrapped_user_data->provider,
  406. aws_error_debug_str(error_code));
  407. goto on_error;
  408. }
  409. return;
  410. on_error:
  411. impl->function_table->aws_http_stream_release(stream);
  412. s_finalize_credentials_query(wrapped_user_data, error_code);
  413. }
  414. static void s_on_retry_token_acquired(
  415. struct aws_retry_strategy *strategy,
  416. int error_code,
  417. struct aws_retry_token *token,
  418. void *user_data) {
  419. (void)strategy;
  420. struct cognito_user_data *wrapped_user_data = user_data;
  421. if (token == NULL) {
  422. AWS_LOGF_ERROR(
  423. AWS_LS_AUTH_CREDENTIALS_PROVIDER,
  424. "(id=%p): Cognito credentials provider failed to acquire retry token: %s",
  425. (void *)wrapped_user_data->provider,
  426. aws_error_debug_str(error_code));
  427. s_finalize_credentials_query(wrapped_user_data, error_code);
  428. return;
  429. }
  430. wrapped_user_data->retry_token = token;
  431. struct aws_credentials_provider_cognito_impl *impl = wrapped_user_data->provider->impl;
  432. impl->function_table->aws_http_connection_manager_acquire_connection(
  433. impl->connection_manager, s_on_connection_setup_fn, wrapped_user_data);
  434. }
  435. static int s_credentials_provider_cognito_get_credentials_async(
  436. struct aws_credentials_provider *provider,
  437. aws_on_get_credentials_callback_fn callback,
  438. void *user_data) {
  439. struct aws_credentials_provider_cognito_impl *impl = provider->impl;
  440. struct cognito_user_data *wrapped_user_data = s_user_data_new(provider, callback, user_data);
  441. if (wrapped_user_data == NULL) {
  442. goto on_error;
  443. }
  444. if (aws_retry_strategy_acquire_retry_token(
  445. impl->retry_strategy, NULL, s_on_retry_token_acquired, wrapped_user_data, 100)) {
  446. AWS_LOGF_ERROR(
  447. AWS_LS_AUTH_CREDENTIALS_PROVIDER,
  448. "(id=%p): Cognito credentials provider failed to acquire retry token with error %s",
  449. (void *)provider,
  450. aws_error_debug_str(aws_last_error()));
  451. goto on_error;
  452. }
  453. return AWS_OP_SUCCESS;
  454. on_error:
  455. s_user_data_destroy(wrapped_user_data);
  456. return AWS_OP_ERR;
  457. }
  458. static void s_credentials_provider_cognito_destroy(struct aws_credentials_provider *provider) {
  459. struct aws_credentials_provider_cognito_impl *impl = provider->impl;
  460. if (impl == NULL) {
  461. return;
  462. }
  463. /* aws_http_connection_manager_release will eventually leads to call of s_on_connection_manager_shutdown,
  464. * which will do memory release for provider and impl.
  465. */
  466. if (impl->connection_manager) {
  467. impl->function_table->aws_http_connection_manager_release(impl->connection_manager);
  468. } else {
  469. /* If provider setup failed halfway through, connection_manager might not exist.
  470. * In this case invoke shutdown completion callback directly to finish cleanup */
  471. s_on_connection_manager_shutdown(provider);
  472. }
  473. /* freeing the provider takes place in the shutdown callback below */
  474. }
  475. static struct aws_credentials_provider_vtable s_aws_credentials_provider_cognito_vtable = {
  476. .get_credentials = s_credentials_provider_cognito_get_credentials_async,
  477. .destroy = s_credentials_provider_cognito_destroy,
  478. };
  479. static void s_on_connection_manager_shutdown(void *user_data) {
  480. struct aws_credentials_provider *provider = user_data;
  481. aws_credentials_provider_invoke_shutdown_callback(provider);
  482. struct aws_credentials_provider_cognito_impl *impl = provider->impl;
  483. aws_retry_strategy_release(impl->retry_strategy);
  484. aws_string_destroy(impl->endpoint);
  485. aws_string_destroy(impl->identity);
  486. aws_string_destroy(impl->custom_role_arn);
  487. for (size_t i = 0; i < aws_array_list_length(&impl->logins); ++i) {
  488. struct aws_cognito_login login;
  489. if (aws_array_list_get_at(&impl->logins, &login, i)) {
  490. continue;
  491. }
  492. s_aws_cognito_login_clean_up(&login);
  493. }
  494. aws_array_list_clean_up(&impl->logins);
  495. aws_mem_release(provider->allocator, provider);
  496. }
  497. static int s_validate_options(const struct aws_credentials_provider_cognito_options *options) {
  498. if (options == NULL) {
  499. return AWS_OP_ERR;
  500. }
  501. if (options->tls_ctx == NULL) {
  502. AWS_LOGF_ERROR(
  503. AWS_LS_AUTH_CREDENTIALS_PROVIDER,
  504. "(static) Cognito credentials provider options must include a TLS context");
  505. return AWS_OP_ERR;
  506. }
  507. if (options->bootstrap == NULL) {
  508. AWS_LOGF_ERROR(
  509. AWS_LS_AUTH_CREDENTIALS_PROVIDER,
  510. "(static) Cognito credentials provider options must include a client bootstrap");
  511. return AWS_OP_ERR;
  512. }
  513. if (options->endpoint.len == 0) {
  514. AWS_LOGF_ERROR(
  515. AWS_LS_AUTH_CREDENTIALS_PROVIDER,
  516. "(static) Cognito credentials provider options must have a non-empty endpoint");
  517. return AWS_OP_ERR;
  518. }
  519. if (options->identity.len == 0) {
  520. AWS_LOGF_ERROR(
  521. AWS_LS_AUTH_CREDENTIALS_PROVIDER,
  522. "(static) Cognito credentials provider options must have a non-empty identity");
  523. return AWS_OP_ERR;
  524. }
  525. return AWS_OP_SUCCESS;
  526. }
  527. struct aws_credentials_provider *aws_credentials_provider_new_cognito(
  528. struct aws_allocator *allocator,
  529. const struct aws_credentials_provider_cognito_options *options) {
  530. struct aws_credentials_provider *provider = NULL;
  531. struct aws_credentials_provider_cognito_impl *impl = NULL;
  532. if (s_validate_options(options)) {
  533. aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
  534. return NULL;
  535. }
  536. aws_mem_acquire_many(
  537. allocator,
  538. 2,
  539. &provider,
  540. sizeof(struct aws_credentials_provider),
  541. &impl,
  542. sizeof(struct aws_credentials_provider_cognito_impl));
  543. if (!provider) {
  544. return NULL;
  545. }
  546. AWS_ZERO_STRUCT(*provider);
  547. AWS_ZERO_STRUCT(*impl);
  548. aws_credentials_provider_init_base(provider, allocator, &s_aws_credentials_provider_cognito_vtable, impl);
  549. struct aws_tls_connection_options tls_connection_options;
  550. AWS_ZERO_STRUCT(tls_connection_options);
  551. aws_tls_connection_options_init_from_ctx(&tls_connection_options, options->tls_ctx);
  552. struct aws_byte_cursor host = options->endpoint;
  553. if (aws_tls_connection_options_set_server_name(&tls_connection_options, allocator, &host)) {
  554. AWS_LOGF_ERROR(
  555. AWS_LS_AUTH_CREDENTIALS_PROVIDER,
  556. "(id=%p): Cognito credentials provider failed to create tls connection options with error %s",
  557. (void *)provider,
  558. aws_error_debug_str(aws_last_error()));
  559. goto on_error;
  560. }
  561. struct aws_socket_options socket_options;
  562. AWS_ZERO_STRUCT(socket_options);
  563. socket_options.type = AWS_SOCKET_STREAM;
  564. socket_options.domain = AWS_SOCKET_IPV4;
  565. socket_options.connect_timeout_ms = (uint32_t)aws_timestamp_convert(
  566. COGNITO_CONNECT_TIMEOUT_DEFAULT_IN_SECONDS, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_MILLIS, NULL);
  567. struct aws_http_connection_manager_options manager_options;
  568. AWS_ZERO_STRUCT(manager_options);
  569. manager_options.bootstrap = options->bootstrap;
  570. manager_options.initial_window_size = SIZE_MAX;
  571. manager_options.socket_options = &socket_options;
  572. manager_options.host = options->endpoint;
  573. manager_options.port = 443;
  574. manager_options.max_connections = 2;
  575. manager_options.shutdown_complete_callback = s_on_connection_manager_shutdown;
  576. manager_options.shutdown_complete_user_data = provider;
  577. manager_options.tls_connection_options = &tls_connection_options;
  578. manager_options.proxy_options = options->http_proxy_options;
  579. impl->function_table = options->function_table;
  580. if (impl->function_table == NULL) {
  581. impl->function_table = g_aws_credentials_provider_http_function_table;
  582. }
  583. impl->connection_manager = impl->function_table->aws_http_connection_manager_new(allocator, &manager_options);
  584. if (impl->connection_manager == NULL) {
  585. AWS_LOGF_ERROR(
  586. AWS_LS_AUTH_CREDENTIALS_PROVIDER,
  587. "(id=%p): Cognito credentials provider failed to create http connection manager with error %s",
  588. (void *)provider,
  589. aws_error_debug_str(aws_last_error()));
  590. goto on_error;
  591. }
  592. impl->endpoint = aws_string_new_from_cursor(allocator, &options->endpoint);
  593. impl->identity = aws_string_new_from_cursor(allocator, &options->identity);
  594. if (options->custom_role_arn != NULL) {
  595. impl->custom_role_arn = aws_string_new_from_cursor(allocator, options->custom_role_arn);
  596. }
  597. aws_array_list_init_dynamic(&impl->logins, allocator, options->login_count, sizeof(struct aws_cognito_login));
  598. for (size_t i = 0; i < options->login_count; ++i) {
  599. struct aws_cognito_identity_provider_token_pair *login_token_pair = &options->logins[i];
  600. struct aws_cognito_login login;
  601. if (s_aws_cognito_login_init(
  602. &login,
  603. allocator,
  604. login_token_pair->identity_provider_name,
  605. login_token_pair->identity_provider_token)) {
  606. AWS_LOGF_ERROR(
  607. AWS_LS_AUTH_CREDENTIALS_PROVIDER,
  608. "(id=%p): Cognito credentials provider failed to initialize login entry with error %s",
  609. (void *)provider,
  610. aws_error_debug_str(aws_last_error()));
  611. goto on_error;
  612. }
  613. aws_array_list_push_back(&impl->logins, &login);
  614. }
  615. struct aws_standard_retry_options retry_options = {
  616. .backoff_retry_options =
  617. {
  618. .el_group = options->bootstrap->event_loop_group,
  619. .max_retries = COGNITO_MAX_RETRIES,
  620. },
  621. };
  622. impl->retry_strategy = aws_retry_strategy_new_standard(allocator, &retry_options);
  623. if (!impl->retry_strategy) {
  624. AWS_LOGF_ERROR(
  625. AWS_LS_AUTH_CREDENTIALS_PROVIDER,
  626. "(id=%p): Cognito credentials provider failed to create a retry strategy with error %s",
  627. (void *)provider,
  628. aws_error_debug_str(aws_last_error()));
  629. goto on_error;
  630. }
  631. provider->shutdown_options = options->shutdown_options;
  632. aws_tls_connection_options_clean_up(&tls_connection_options);
  633. return provider;
  634. on_error:
  635. aws_tls_connection_options_clean_up(&tls_connection_options);
  636. aws_credentials_provider_destroy(provider);
  637. return NULL;
  638. }
  639. /*************************************************************************/
  640. #define DEFAULT_CREDENTIAL_PROVIDER_REFRESH_MS (15 * 60 * 1000)
  641. /*
  642. * Cognito provider with caching implementation
  643. */
  644. struct aws_credentials_provider *aws_credentials_provider_new_cognito_caching(
  645. struct aws_allocator *allocator,
  646. const struct aws_credentials_provider_cognito_options *options) {
  647. struct aws_credentials_provider *cognito_provider = NULL;
  648. struct aws_credentials_provider *caching_provider = NULL;
  649. cognito_provider = aws_credentials_provider_new_cognito(allocator, options);
  650. if (cognito_provider == NULL) {
  651. goto on_error;
  652. }
  653. struct aws_credentials_provider_cached_options cached_options = {
  654. .source = cognito_provider,
  655. .refresh_time_in_milliseconds = DEFAULT_CREDENTIAL_PROVIDER_REFRESH_MS,
  656. };
  657. caching_provider = aws_credentials_provider_new_cached(allocator, &cached_options);
  658. if (caching_provider == NULL) {
  659. goto on_error;
  660. }
  661. aws_credentials_provider_release(cognito_provider);
  662. return caching_provider;
  663. on_error:
  664. aws_credentials_provider_release(caching_provider);
  665. aws_credentials_provider_release(cognito_provider);
  666. return NULL;
  667. }