2 This file is part of GNUnet.
3 Copyright (C) 2012-2018 GNUnet e.V.
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
21 * @author Martin Schanzenbach
22 * @author Philippe Buschmann
23 * @file identity/plugin_rest_openid_connect.c
24 * @brief GNUnet Namestore REST plugin
29 #include "gnunet_rest_plugin.h"
30 #include "gnunet_identity_service.h"
31 #include "gnunet_gns_service.h"
32 #include "gnunet_gnsrecord_lib.h"
33 #include "gnunet_namestore_service.h"
34 #include "gnunet_rest_lib.h"
35 #include "microhttpd.h"
38 #include "gnunet_signatures.h"
39 #include "gnunet_reclaim_attribute_lib.h"
40 #include "gnunet_reclaim_service.h"
41 #include "oidc_helper.h"
46 #define GNUNET_REST_API_NS_OIDC "/openid"
51 #define GNUNET_REST_API_NS_AUTHORIZE "/openid/authorize"
56 #define GNUNET_REST_API_NS_TOKEN "/openid/token"
61 #define GNUNET_REST_API_NS_USERINFO "/openid/userinfo"
66 #define GNUNET_REST_API_NS_LOGIN "/openid/login"
69 * State while collecting all egos
71 #define ID_REST_STATE_INIT 0
74 * Done collecting egos
76 #define ID_REST_STATE_POST_INIT 1
81 #define OIDC_GRANT_TYPE_KEY "grant_type"
86 #define OIDC_GRANT_TYPE_VALUE "authorization_code"
91 #define OIDC_CODE_KEY "code"
94 * OIDC response_type key
96 #define OIDC_RESPONSE_TYPE_KEY "response_type"
101 #define OIDC_CLIENT_ID_KEY "client_id"
106 #define OIDC_SCOPE_KEY "scope"
109 * OIDC redirect_uri key
111 #define OIDC_REDIRECT_URI_KEY "redirect_uri"
116 #define OIDC_STATE_KEY "state"
121 #define OIDC_NONCE_KEY "nonce"
124 * OIDC cookie expiration (in seconds)
126 #define OIDC_COOKIE_EXPIRATION 3
129 * OIDC cookie header key
131 #define OIDC_COOKIE_HEADER_KEY "cookie"
134 * OIDC cookie header information key
136 #define OIDC_AUTHORIZATION_HEADER_KEY "authorization"
139 * OIDC cookie header information key
141 #define OIDC_COOKIE_HEADER_INFORMATION_KEY "Identity="
144 * OIDC cookie header if user cancelled
146 #define OIDC_COOKIE_HEADER_ACCESS_DENIED "Identity=Denied"
149 * OIDC expected response_type while authorizing
151 #define OIDC_EXPECTED_AUTHORIZATION_RESPONSE_TYPE "code"
154 * OIDC expected scope part while authorizing
156 #define OIDC_EXPECTED_AUTHORIZATION_SCOPE "openid"
159 * OIDC error key for invalid client
161 #define OIDC_ERROR_KEY_INVALID_CLIENT "invalid_client"
164 * OIDC error key for invalid scopes
166 #define OIDC_ERROR_KEY_INVALID_SCOPE "invalid_scope"
169 * OIDC error key for invalid requests
171 #define OIDC_ERROR_KEY_INVALID_REQUEST "invalid_request"
174 * OIDC error key for invalid tokens
176 #define OIDC_ERROR_KEY_INVALID_TOKEN "invalid_token"
179 * OIDC error key for invalid cookies
181 #define OIDC_ERROR_KEY_INVALID_COOKIE "invalid_cookie"
184 * OIDC error key for generic server errors
186 #define OIDC_ERROR_KEY_SERVER_ERROR "server_error"
189 * OIDC error key for unsupported grants
191 #define OIDC_ERROR_KEY_UNSUPPORTED_GRANT_TYPE "unsupported_grant_type"
194 * OIDC error key for unsupported response types
196 #define OIDC_ERROR_KEY_UNSUPPORTED_RESPONSE_TYPE "unsupported_response_type"
199 * OIDC error key for unauthorized clients
201 #define OIDC_ERROR_KEY_UNAUTHORIZED_CLIENT "unauthorized_client"
204 * OIDC error key for denied access
206 #define OIDC_ERROR_KEY_ACCESS_DENIED "access_denied"
211 * OIDC ignored parameter array
213 static char* OIDC_ignored_parameter_array [] =
225 * OIDC Hash map that keeps track of issued cookies
227 struct GNUNET_CONTAINER_MultiHashMap *OIDC_cookie_jar_map;
230 * OIDC authorized identities and times hashmap
232 struct GNUNET_CONTAINER_MultiHashMap *OIDC_identity_grants;
235 * OIDC Hash map that keeps track of used authorization code(s)
237 struct GNUNET_CONTAINER_MultiHashMap *OIDC_used_ticket_map;
240 * Hash map that links the issued access token to the corresponding ticket and ego
242 struct GNUNET_CONTAINER_MultiHashMap *OIDC_access_token_map;
245 * The configuration handle
247 const struct GNUNET_CONFIGURATION_Handle *cfg;
250 * HTTP methods allows for this plugin
252 static char* allow_methods;
255 * @brief struct returned by the initialization function of the plugin
259 const struct GNUNET_CONFIGURATION_Handle *cfg;
263 * OIDC needed variables
265 struct OIDC_Variables
268 * The RP client public key
270 struct GNUNET_CRYPTO_EcdsaPublicKey client_pkey;
273 * The OIDC client id of the RP
278 * The OIDC redirect uri
283 * The list of oidc scopes
298 * The OIDC response type
303 * The identity chosen by the user to login
305 char *login_identity;
308 * User cancelled authorization/login
327 struct EgoEntry *next;
332 struct EgoEntry *prev;
347 struct GNUNET_IDENTITY_Ego *ego;
356 struct EgoEntry *ego_head;
361 struct EgoEntry *ego_tail;
366 struct EgoEntry *ego_entry;
369 * Pointer to ego private key
371 struct GNUNET_CRYPTO_EcdsaPrivateKey priv_key;
376 struct OIDC_Variables *oidc;
379 * The processing state
384 * Handle to Identity service.
386 struct GNUNET_IDENTITY_Handle *identity_handle;
391 struct GNUNET_REST_RequestHandle *rest_handle;
396 struct GNUNET_GNS_Handle *gns_handle;
401 struct GNUNET_GNS_LookupRequest *gns_op;
404 * Handle to NAMESTORE
406 struct GNUNET_NAMESTORE_Handle *namestore_handle;
409 * Iterator for NAMESTORE
411 struct GNUNET_NAMESTORE_ZoneIterator *namestore_handle_it;
414 * Attribute claim list
416 struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attr_list;
421 struct GNUNET_IDENTITY_Operation *op;
426 struct GNUNET_RECLAIM_Handle *idp;
431 struct GNUNET_RECLAIM_Operation *idp_op;
436 struct GNUNET_RECLAIM_AttributeIterator *attr_it;
441 struct GNUNET_RECLAIM_TicketIterator *ticket_it;
446 struct GNUNET_RECLAIM_Ticket ticket;
449 * Desired timeout for the lookup (default is no timeout).
451 struct GNUNET_TIME_Relative timeout;
454 * ID of a task associated with the resolution process.
456 struct GNUNET_SCHEDULER_Task *timeout_task;
459 * The plugin result processor
461 GNUNET_REST_ResultProcessor proc;
464 * The closure of the result processor
474 * The tld for redirect
479 * The redirect prefix
481 char *redirect_prefix;
484 * The redirect suffix
486 char *redirect_suffix;
489 * Error response message
494 * Error response description
506 * Cleanup lookup handle
507 * @param handle Handle to clean up
510 cleanup_handle (struct RequestHandle *handle)
512 struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *claim_entry;
513 struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *claim_tmp;
514 struct EgoEntry *ego_entry;
515 struct EgoEntry *ego_tmp;
516 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
518 if (NULL != handle->timeout_task)
519 GNUNET_SCHEDULER_cancel (handle->timeout_task);
520 if (NULL != handle->identity_handle)
521 GNUNET_IDENTITY_disconnect (handle->identity_handle);
522 if (NULL != handle->attr_it)
523 GNUNET_RECLAIM_get_attributes_stop (handle->attr_it);
524 if (NULL != handle->ticket_it)
525 GNUNET_RECLAIM_ticket_iteration_stop (handle->ticket_it);
526 if (NULL != handle->idp)
527 GNUNET_RECLAIM_disconnect (handle->idp);
528 GNUNET_free_non_null (handle->url);
529 GNUNET_free_non_null (handle->tld);
530 GNUNET_free_non_null (handle->redirect_prefix);
531 GNUNET_free_non_null (handle->redirect_suffix);
532 GNUNET_free_non_null (handle->emsg);
533 GNUNET_free_non_null (handle->edesc);
534 if (NULL != handle->gns_op)
535 GNUNET_GNS_lookup_cancel (handle->gns_op);
536 if (NULL != handle->gns_handle)
537 GNUNET_GNS_disconnect (handle->gns_handle);
539 if (NULL != handle->namestore_handle)
540 GNUNET_NAMESTORE_disconnect (handle->namestore_handle);
541 if (NULL != handle->oidc)
543 GNUNET_free_non_null (handle->oidc->client_id);
544 GNUNET_free_non_null (handle->oidc->login_identity);
545 GNUNET_free_non_null (handle->oidc->nonce);
546 GNUNET_free_non_null (handle->oidc->redirect_uri);
547 GNUNET_free_non_null (handle->oidc->response_type);
548 GNUNET_free_non_null (handle->oidc->scope);
549 GNUNET_free_non_null (handle->oidc->state);
550 json_decref (handle->oidc->response);
551 GNUNET_free (handle->oidc);
553 if ( NULL != handle->attr_list )
555 for (claim_entry = handle->attr_list->list_head;
556 NULL != claim_entry;)
558 claim_tmp = claim_entry;
559 claim_entry = claim_entry->next;
560 GNUNET_free (claim_tmp->claim);
561 GNUNET_free (claim_tmp);
563 GNUNET_free (handle->attr_list);
565 for (ego_entry = handle->ego_head;
569 ego_entry = ego_entry->next;
570 GNUNET_free (ego_tmp->identifier);
571 GNUNET_free (ego_tmp->keystring);
572 GNUNET_free (ego_tmp);
574 GNUNET_free_non_null (handle->attr_it);
575 GNUNET_free (handle);
579 cleanup_handle_delayed (void *cls)
581 cleanup_handle (cls);
586 * Task run on error, sends error message. Cleans up everything.
588 * @param cls the `struct RequestHandle`
593 struct RequestHandle *handle = cls;
594 struct MHD_Response *resp;
597 GNUNET_asprintf (&json_error, "{ \"error\" : \"%s\", \"error_description\" : \"%s\"%s%s%s}",
599 (NULL != handle->edesc) ? handle->edesc : "",
600 (NULL != handle->oidc->state) ? ", \"state\":\"" : "",
601 (NULL != handle->oidc->state) ? handle->oidc->state : "",
602 (NULL != handle->oidc->state) ? "\"" : "");
603 if ( 0 == handle->response_code )
604 handle->response_code = MHD_HTTP_BAD_REQUEST;
605 resp = GNUNET_REST_create_response (json_error);
606 if (MHD_HTTP_UNAUTHORIZED == handle->response_code)
607 MHD_add_response_header (resp,
608 MHD_HTTP_HEADER_WWW_AUTHENTICATE,
610 MHD_add_response_header (resp,
611 MHD_HTTP_HEADER_CONTENT_TYPE,
613 handle->proc (handle->proc_cls, resp, handle->response_code);
614 GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
615 GNUNET_free (json_error);
620 * Task run on error in userinfo endpoint, sends error header. Cleans up
623 * @param cls the `struct RequestHandle`
626 do_userinfo_error (void *cls)
628 struct RequestHandle *handle = cls;
629 struct MHD_Response *resp;
632 GNUNET_asprintf (&error, "error=\"%s\", error_description=\"%s\"",
634 (NULL != handle->edesc) ? handle->edesc : "");
635 resp = GNUNET_REST_create_response ("");
636 MHD_add_response_header (resp,
637 MHD_HTTP_HEADER_WWW_AUTHENTICATE,
639 handle->proc (handle->proc_cls, resp, handle->response_code);
640 GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
646 * Task run on error, sends error message and redirects. Cleans up everything.
648 * @param cls the `struct RequestHandle`
651 do_redirect_error (void *cls)
653 struct RequestHandle *handle = cls;
654 struct MHD_Response *resp;
656 GNUNET_asprintf (&redirect,
657 "%s?error=%s&error_description=%s%s%s",
658 handle->oidc->redirect_uri, handle->emsg, handle->edesc,
659 (NULL != handle->oidc->state) ? "&state=" : "",
660 (NULL != handle->oidc->state) ? handle->oidc->state : "");
661 resp = GNUNET_REST_create_response ("");
662 MHD_add_response_header (resp, "Location", redirect);
663 handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
664 GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
665 GNUNET_free (redirect);
669 * Task run on timeout, sends error message. Cleans up everything.
671 * @param cls the `struct RequestHandle`
674 do_timeout (void *cls)
676 struct RequestHandle *handle = cls;
678 handle->timeout_task = NULL;
683 * Return attributes for claim
685 * @param cls the request handle
688 return_userinfo_response (void *cls)
691 struct RequestHandle *handle = cls;
692 struct MHD_Response *resp;
694 result_str = json_dumps (handle->oidc->response, 0);
696 resp = GNUNET_REST_create_response (result_str);
697 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
698 GNUNET_free (result_str);
699 cleanup_handle (handle);
703 * Returns base64 encoded string urlencoded
705 * @param string the string to encode
706 * @return base64 encoded string
709 base64_encode (const char *s)
717 GNUNET_STRINGS_base64_encode (s, strlen (s), &enc);
718 tmp = strchr (enc, '=');
719 num_pads = strlen (enc) - (tmp - enc);
720 GNUNET_assert ((3 > num_pads) && (0 <= num_pads));
723 enc_urlencode = GNUNET_malloc (strlen (enc) + num_pads*2);
724 strcpy (enc_urlencode, enc);
726 tmp = strchr (enc_urlencode, '=');
727 for (i = 0; i < num_pads; i++)
729 strcpy (tmp, "%3D"); //replace '=' with '%3D'
732 return enc_urlencode;
736 * Respond to OPTIONS request
738 * @param con_handle the connection handle
740 * @param cls the RequestHandle
743 options_cont (struct GNUNET_REST_RequestHandle *con_handle,
747 struct MHD_Response *resp;
748 struct RequestHandle *handle = cls;
750 //For now, independent of path return all options
751 resp = GNUNET_REST_create_response (NULL);
752 MHD_add_response_header (resp,
753 "Access-Control-Allow-Methods",
755 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
756 cleanup_handle (handle);
762 * Interprets cookie header and pass its identity keystring to handle
765 cookie_identity_interpretation (struct RequestHandle *handle)
767 struct GNUNET_HashCode cache_key;
769 struct GNUNET_TIME_Absolute current_time, *relog_time;
770 char delimiter[] = "; ";
775 //gets identity of login try with cookie
776 GNUNET_CRYPTO_hash (OIDC_COOKIE_HEADER_KEY, strlen (OIDC_COOKIE_HEADER_KEY),
778 if ( GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->header_param_map,
781 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
782 "No cookie found\n");
785 //splits cookies and find 'Identity' cookie
786 tmp_cookies = GNUNET_CONTAINER_multihashmap_get ( handle->rest_handle->header_param_map, &cache_key);
787 cookies = GNUNET_strdup (tmp_cookies);
788 token = strtok (cookies, delimiter);
789 handle->oidc->user_cancelled = GNUNET_NO;
790 handle->oidc->login_identity = NULL;
793 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
794 "Unable to parse cookie: %s\n",
796 GNUNET_free (cookies);
800 while (NULL != token)
802 if (0 == strcmp (token,
803 OIDC_COOKIE_HEADER_ACCESS_DENIED))
805 handle->oidc->user_cancelled = GNUNET_YES;
806 GNUNET_free (cookies);
809 if (NULL != strstr (token, OIDC_COOKIE_HEADER_INFORMATION_KEY))
811 token = strtok (NULL, delimiter);
815 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
816 "No cookie value to process: %s\n",
818 GNUNET_free (cookies);
821 GNUNET_CRYPTO_hash (token, strlen (token),
823 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (OIDC_cookie_jar_map, &cache_key))
825 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
826 "Found cookie `%s', but no corresponding expiration entry present...\n",
828 GNUNET_free (cookies);
831 relog_time = GNUNET_CONTAINER_multihashmap_get (OIDC_cookie_jar_map,
833 current_time = GNUNET_TIME_absolute_get ();
834 // 30 min after old login -> redirect to login
835 if ( current_time.abs_value_us > relog_time->abs_value_us )
837 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
838 "Found cookie `%s', but it is expired.\n",
840 GNUNET_free (cookies);
843 value = strtok (token, OIDC_COOKIE_HEADER_INFORMATION_KEY);
844 GNUNET_assert (NULL != value);
845 handle->oidc->login_identity = GNUNET_strdup (value);
849 * Redirects to login page stored in configuration file
852 login_redirect (void *cls)
854 char *login_base_url;
856 struct MHD_Response *resp;
857 struct RequestHandle *handle = cls;
860 == GNUNET_CONFIGURATION_get_value_string (cfg, "reclaim-rest-plugin",
861 "address", &login_base_url) )
863 GNUNET_asprintf (&new_redirect, "%s?%s=%s&%s=%s&%s=%s&%s=%s&%s=%s&%s=%s",
865 OIDC_RESPONSE_TYPE_KEY,
866 handle->oidc->response_type,
868 handle->oidc->client_id,
869 OIDC_REDIRECT_URI_KEY,
870 handle->oidc->redirect_uri,
874 (NULL != handle->oidc->state) ? handle->oidc->state : "",
876 (NULL != handle->oidc->nonce) ? handle->oidc->nonce : "");
877 resp = GNUNET_REST_create_response ("");
878 MHD_add_response_header (resp, "Location", new_redirect);
879 GNUNET_free (login_base_url);
883 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
884 handle->edesc = GNUNET_strdup ("gnunet configuration failed");
885 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
886 GNUNET_SCHEDULER_add_now (&do_error, handle);
889 handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
890 GNUNET_free (new_redirect);
891 GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
895 * Does internal server error when iteration failed.
898 oidc_iteration_error (void *cls)
900 struct RequestHandle *handle = cls;
901 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
902 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
903 GNUNET_SCHEDULER_add_now (&do_error, handle);
908 * Issues ticket and redirects to relying party with the authorization code as
909 * parameter. Otherwise redirects with error
912 oidc_ticket_issue_cb (void* cls,
913 const struct GNUNET_RECLAIM_Ticket *ticket)
915 struct RequestHandle *handle = cls;
916 struct MHD_Response *resp;
919 char *code_json_string;
920 char *code_base64_final_string;
922 handle->idp_op = NULL;
923 handle->ticket = *ticket;
926 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
927 handle->edesc = GNUNET_strdup ("Server cannot generate ticket.");
928 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
931 ticket_str = GNUNET_STRINGS_data_to_string_alloc (&handle->ticket,
932 sizeof (struct GNUNET_RECLAIM_Ticket));
933 //TODO change if more attributes are needed (see max_age)
934 code_json_string = OIDC_build_authz_code (&handle->priv_key,
936 handle->oidc->nonce);
937 code_base64_final_string = base64_encode (code_json_string);
938 if ( (NULL != handle->redirect_prefix) &&
939 (NULL != handle->redirect_suffix) &&
940 (NULL != handle->tld) )
943 GNUNET_asprintf (&redirect_uri, "%s.%s/%s?%s=%s&state=%s",
944 handle->redirect_prefix,
946 handle->redirect_suffix,
947 handle->oidc->response_type,
948 code_base64_final_string, handle->oidc->state);
950 GNUNET_asprintf (&redirect_uri, "%s?%s=%s&state=%s",
951 handle->oidc->redirect_uri,
952 handle->oidc->response_type,
953 code_base64_final_string, handle->oidc->state);
956 resp = GNUNET_REST_create_response ("");
957 MHD_add_response_header (resp, "Location", redirect_uri);
958 handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
959 GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
960 GNUNET_free (redirect_uri);
961 GNUNET_free (ticket_str);
962 GNUNET_free (code_json_string);
963 GNUNET_free (code_base64_final_string);
967 oidc_collect_finished_cb (void *cls)
969 struct RequestHandle *handle = cls;
970 handle->attr_it = NULL;
971 handle->ticket_it = NULL;
972 if (NULL == handle->attr_list->list_head)
974 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_SCOPE);
975 handle->edesc = GNUNET_strdup ("The requested scope is not available.");
976 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
979 handle->idp_op = GNUNET_RECLAIM_ticket_issue (handle->idp,
981 &handle->oidc->client_pkey,
983 &oidc_ticket_issue_cb,
989 * Collects all attributes for an ego if in scope parameter
992 oidc_attr_collect (void *cls,
993 const struct GNUNET_CRYPTO_EcdsaPublicKey *identity,
994 const struct GNUNET_RECLAIM_ATTRIBUTE_Claim *attr)
996 struct RequestHandle *handle = cls;
997 struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *le;
998 char* scope_variables;
999 char* scope_variable;
1000 char delimiter[]=" ";
1002 if ( (NULL == attr->name) || (NULL == attr->data) )
1004 GNUNET_RECLAIM_get_attributes_next (handle->attr_it);
1008 scope_variables = GNUNET_strdup (handle->oidc->scope);
1009 scope_variable = strtok (scope_variables, delimiter);
1010 while (NULL != scope_variable)
1012 if ( 0 == strcmp (attr->name, scope_variable) )
1016 scope_variable = strtok (NULL, delimiter);
1018 if ( NULL == scope_variable )
1020 GNUNET_RECLAIM_get_attributes_next (handle->attr_it);
1021 GNUNET_free (scope_variables);
1024 GNUNET_free (scope_variables);
1026 le = GNUNET_new (struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry);
1027 le->claim = GNUNET_RECLAIM_ATTRIBUTE_claim_new (attr->name, attr->type,
1028 attr->data, attr->data_size);
1029 GNUNET_CONTAINER_DLL_insert (handle->attr_list->list_head,
1030 handle->attr_list->list_tail, le);
1031 GNUNET_RECLAIM_get_attributes_next (handle->attr_it);
1036 * Checks time and cookie and redirects accordingly
1039 code_redirect (void *cls)
1041 struct RequestHandle *handle = cls;
1042 struct GNUNET_TIME_Absolute current_time, *relog_time;
1043 struct GNUNET_CRYPTO_EcdsaPublicKey pubkey, ego_pkey;
1044 struct GNUNET_HashCode cache_key;
1045 char *identity_cookie;
1047 GNUNET_asprintf (&identity_cookie, "Identity=%s", handle->oidc->login_identity);
1048 GNUNET_CRYPTO_hash (identity_cookie, strlen (identity_cookie), &cache_key);
1049 GNUNET_free (identity_cookie);
1050 //No login time for identity -> redirect to login
1052 == GNUNET_CONTAINER_multihashmap_contains (OIDC_cookie_jar_map,
1055 relog_time = GNUNET_CONTAINER_multihashmap_get (OIDC_cookie_jar_map,
1057 current_time = GNUNET_TIME_absolute_get ();
1058 // 30 min after old login -> redirect to login
1059 if ( current_time.abs_value_us <= relog_time->abs_value_us )
1062 != GNUNET_CRYPTO_ecdsa_public_key_from_string (
1063 handle->oidc->login_identity,
1064 strlen (handle->oidc->login_identity), &pubkey) )
1066 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_COOKIE);
1067 handle->edesc = GNUNET_strdup ("The cookie of a login identity is not valid");
1068 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1071 // iterate over egos and compare their public key
1072 for (handle->ego_entry = handle->ego_head;
1073 NULL != handle->ego_entry; handle->ego_entry = handle->ego_entry->next)
1075 GNUNET_IDENTITY_ego_get_public_key (handle->ego_entry->ego, &ego_pkey);
1076 if ( 0 == memcmp (&ego_pkey,
1078 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)) )
1080 handle->priv_key = *GNUNET_IDENTITY_ego_get_private_key (handle->ego_entry->ego);
1081 handle->idp = GNUNET_RECLAIM_connect (cfg);
1082 handle->attr_list = GNUNET_new (struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList);
1083 handle->attr_it = GNUNET_RECLAIM_get_attributes_start (handle->idp,
1085 &oidc_iteration_error,
1089 &oidc_collect_finished_cb,
1094 GNUNET_SCHEDULER_add_now (&login_redirect, handle);
1102 build_redirect (void *cls)
1104 struct RequestHandle *handle = cls;
1105 struct MHD_Response *resp;
1108 if (GNUNET_YES == handle->oidc->user_cancelled)
1110 if ( (NULL != handle->redirect_prefix) &&
1111 (NULL != handle->redirect_suffix) &&
1112 (NULL != handle->tld) )
1114 GNUNET_asprintf (&redirect_uri, "%s.%s/%s?error=%s&error_description=%s&state=%s",
1115 handle->redirect_prefix,
1117 handle->redirect_suffix,
1119 "User denied access",
1120 handle->oidc->state);
1122 GNUNET_asprintf (&redirect_uri, "%s?error=%s&error_description=%s&state=%s",
1123 handle->oidc->redirect_uri,
1125 "User denied access",
1126 handle->oidc->state);
1129 resp = GNUNET_REST_create_response ("");
1130 MHD_add_response_header (resp, "Location", redirect_uri);
1131 handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
1132 GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
1133 GNUNET_free (redirect_uri);
1136 GNUNET_SCHEDULER_add_now (&code_redirect, handle);
1141 lookup_redirect_uri_result (void *cls,
1143 const struct GNUNET_GNSRECORD_Data *rd)
1145 struct RequestHandle *handle = cls;
1149 struct GNUNET_CRYPTO_EcdsaPublicKey redirect_zone;
1151 handle->gns_op = NULL;
1154 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
1155 handle->edesc = GNUNET_strdup ("Server cannot generate ticket, redirect uri not found.");
1156 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1159 for (int i = 0; i < rd_count; i++)
1161 if (GNUNET_GNSRECORD_TYPE_RECLAIM_OIDC_REDIRECT != rd[i].record_type)
1163 if (0 != strncmp (rd[i].data,
1164 handle->oidc->redirect_uri,
1167 tmp = GNUNET_strndup (rd[i].data,
1169 if (NULL == strstr (tmp,
1170 handle->oidc->client_id))
1172 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1173 "Redirect uri %s does not contain client_id %s",
1175 handle->oidc->client_id);
1179 (unsigned char) '.');
1181 handle->redirect_prefix = GNUNET_strdup (tmp);
1182 tmp_key_str = pos + 1;
1183 pos = strchr (tmp_key_str,
1184 (unsigned char) '/');
1186 handle->redirect_suffix = GNUNET_strdup (pos + 1);
1188 GNUNET_STRINGS_string_to_data (tmp_key_str,
1189 strlen (tmp_key_str),
1191 sizeof (redirect_zone));
1193 GNUNET_SCHEDULER_add_now (&build_redirect, handle);
1197 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
1198 handle->edesc = GNUNET_strdup ("Server cannot generate ticket, redirect uri not found.");
1199 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1205 * Initiate redirect back to client.
1208 client_redirect (void *cls)
1210 struct RequestHandle *handle = cls;
1212 /* Lookup client redirect uri to verify request */
1213 handle->gns_op = GNUNET_GNS_lookup (handle->gns_handle,
1214 GNUNET_GNS_EMPTY_LABEL_AT,
1215 &handle->oidc->client_pkey,
1216 GNUNET_GNSRECORD_TYPE_RECLAIM_OIDC_REDIRECT,
1217 GNUNET_GNS_LO_DEFAULT,
1218 &lookup_redirect_uri_result,
1225 * Iteration over all results finished, build final
1228 * @param cls the `struct RequestHandle`
1231 build_authz_response (void *cls)
1233 struct RequestHandle *handle = cls;
1234 struct GNUNET_HashCode cache_key;
1236 char *expected_scope;
1237 char delimiter[]=" ";
1238 int number_of_ignored_parameter, iterator;
1241 // REQUIRED value: redirect_uri
1242 GNUNET_CRYPTO_hash (OIDC_REDIRECT_URI_KEY, strlen (OIDC_REDIRECT_URI_KEY),
1244 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1247 handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1248 handle->edesc=GNUNET_strdup ("missing parameter redirect_uri");
1249 GNUNET_SCHEDULER_add_now (&do_error, handle);
1252 handle->oidc->redirect_uri =
1253 GNUNET_strdup (GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map,
1256 // REQUIRED value: response_type
1257 GNUNET_CRYPTO_hash (OIDC_RESPONSE_TYPE_KEY, strlen (OIDC_RESPONSE_TYPE_KEY),
1259 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1262 handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1263 handle->edesc=GNUNET_strdup ("missing parameter response_type");
1264 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1267 handle->oidc->response_type = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map,
1269 handle->oidc->response_type = GNUNET_strdup (handle->oidc->response_type);
1271 // REQUIRED value: scope
1272 GNUNET_CRYPTO_hash (OIDC_SCOPE_KEY, strlen (OIDC_SCOPE_KEY), &cache_key);
1273 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1276 handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_INVALID_SCOPE);
1277 handle->edesc=GNUNET_strdup ("missing parameter scope");
1278 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1281 handle->oidc->scope = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map,
1283 handle->oidc->scope = GNUNET_strdup (handle->oidc->scope);
1285 //OPTIONAL value: nonce
1286 GNUNET_CRYPTO_hash (OIDC_NONCE_KEY, strlen (OIDC_NONCE_KEY), &cache_key);
1287 if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1290 handle->oidc->nonce = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map,
1292 handle->oidc->nonce = GNUNET_strdup (handle->oidc->nonce);
1295 //TODO check other values if needed
1296 number_of_ignored_parameter = sizeof (OIDC_ignored_parameter_array) / sizeof (char *);
1297 for (iterator = 0; iterator < number_of_ignored_parameter; iterator++)
1299 GNUNET_CRYPTO_hash (OIDC_ignored_parameter_array[iterator],
1300 strlen (OIDC_ignored_parameter_array[iterator]),
1302 if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1305 handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_ACCESS_DENIED);
1306 GNUNET_asprintf (&handle->edesc, "Server will not handle parameter: %s",
1307 OIDC_ignored_parameter_array[iterator]);
1308 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1313 // Checks if response_type is 'code'
1314 if (0 != strcmp (handle->oidc->response_type, OIDC_EXPECTED_AUTHORIZATION_RESPONSE_TYPE))
1316 handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_UNSUPPORTED_RESPONSE_TYPE);
1317 handle->edesc=GNUNET_strdup ("The authorization server does not support "
1318 "obtaining this authorization code.");
1319 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1323 // Checks if scope contains 'openid'
1324 expected_scope = GNUNET_strdup (handle->oidc->scope);
1326 test = strtok (expected_scope, delimiter);
1327 while (NULL != test)
1329 if ( 0 == strcmp (OIDC_EXPECTED_AUTHORIZATION_SCOPE, expected_scope) )
1331 test = strtok (NULL, delimiter);
1335 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_SCOPE);
1336 handle->edesc=GNUNET_strdup ("The requested scope is invalid, unknown, or "
1338 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1339 GNUNET_free (expected_scope);
1343 GNUNET_free (expected_scope);
1344 if ( (NULL == handle->oidc->login_identity) &&
1345 (GNUNET_NO == handle->oidc->user_cancelled))
1346 GNUNET_SCHEDULER_add_now (&login_redirect, handle);
1348 GNUNET_SCHEDULER_add_now (&client_redirect, handle);
1352 * Iterate over tlds in config
1355 tld_iter (void *cls,
1356 const char *section,
1360 struct RequestHandle *handle = cls;
1361 struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
1364 GNUNET_CRYPTO_ecdsa_public_key_from_string (value,
1368 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1369 "Skipping non key %s\n",
1373 if (0 == memcmp (&pkey, &handle->oidc->client_pkey,
1374 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)))
1375 handle->tld = GNUNET_strdup (option+1);
1379 * Responds to authorization GET and url-encoded POST request
1381 * @param con_handle the connection handle
1382 * @param url the url
1383 * @param cls the RequestHandle
1386 authorize_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
1390 struct RequestHandle *handle = cls;
1391 struct GNUNET_HashCode cache_key;
1392 struct EgoEntry *tmp_ego;
1393 const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key;
1394 struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
1396 cookie_identity_interpretation (handle);
1398 //RECOMMENDED value: state - REQUIRED for answers
1399 GNUNET_CRYPTO_hash (OIDC_STATE_KEY, strlen (OIDC_STATE_KEY), &cache_key);
1400 if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1403 handle->oidc->state = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map,
1405 handle->oidc->state = GNUNET_strdup (handle->oidc->state);
1408 // REQUIRED value: client_id
1409 GNUNET_CRYPTO_hash (OIDC_CLIENT_ID_KEY, strlen (OIDC_CLIENT_ID_KEY),
1411 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1414 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1415 handle->edesc = GNUNET_strdup ("missing parameter client_id");
1416 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1417 GNUNET_SCHEDULER_add_now (&do_error, handle);
1420 handle->oidc->client_id = GNUNET_strdup (GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map,
1424 != GNUNET_CRYPTO_ecdsa_public_key_from_string (handle->oidc->client_id,
1425 strlen (handle->oidc->client_id),
1426 &handle->oidc->client_pkey) )
1428 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_UNAUTHORIZED_CLIENT);
1429 handle->edesc = GNUNET_strdup ("The client is not authorized to request an "
1430 "authorization code using this method.");
1431 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1432 GNUNET_SCHEDULER_add_now (&do_error, handle);
1437 if ( NULL == handle->ego_head )
1439 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
1440 handle->edesc = GNUNET_strdup ("Egos are missing");
1441 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1442 GNUNET_SCHEDULER_add_now (&do_error, handle);
1446 handle->ego_entry = handle->ego_head;
1447 handle->priv_key = *GNUNET_IDENTITY_ego_get_private_key (handle->ego_head->ego);
1448 //If we know this identity, translated the corresponding TLD
1449 //TODO: We might want to have a reverse lookup functionality for TLDs?
1450 for (tmp_ego = handle->ego_head; NULL != tmp_ego; tmp_ego = tmp_ego->next)
1452 priv_key = GNUNET_IDENTITY_ego_get_private_key (tmp_ego->ego);
1453 GNUNET_CRYPTO_ecdsa_key_get_public (priv_key,
1455 if (0 == memcmp (&pkey, &handle->oidc->client_pkey,
1456 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)))
1458 handle->tld = GNUNET_strdup (tmp_ego->identifier);
1459 handle->ego_entry = handle->ego_tail;
1462 if (NULL == handle->tld)
1463 GNUNET_CONFIGURATION_iterate_section_values (cfg,
1467 if (NULL == handle->tld)
1468 handle->tld = GNUNET_strdup (handle->oidc->client_id);
1469 GNUNET_SCHEDULER_add_now (&build_authz_response, handle);
1473 * Combines an identity with a login time and responds OK to login request
1475 * @param con_handle the connection handle
1476 * @param url the url
1477 * @param cls the RequestHandle
1480 login_cont (struct GNUNET_REST_RequestHandle *con_handle,
1484 struct MHD_Response *resp = GNUNET_REST_create_response ("");
1485 struct RequestHandle *handle = cls;
1486 struct GNUNET_HashCode cache_key;
1487 struct GNUNET_TIME_Absolute *current_time;
1488 struct GNUNET_TIME_Absolute *last_time;
1494 char term_data[handle->rest_handle->data_size+1];
1495 term_data[handle->rest_handle->data_size] = '\0';
1496 GNUNET_memcpy (term_data, handle->rest_handle->data, handle->rest_handle->data_size);
1497 root = json_loads (term_data, JSON_DECODE_ANY, &error);
1498 identity = json_object_get (root, "identity");
1499 if (!json_is_string (identity))
1501 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1502 "Error parsing json string from %s\n",
1504 handle->proc (handle->proc_cls, resp, MHD_HTTP_BAD_REQUEST);
1506 GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
1509 GNUNET_asprintf (&cookie,
1511 json_string_value (identity));
1512 GNUNET_asprintf (&header_val,
1515 OIDC_COOKIE_EXPIRATION);
1516 MHD_add_response_header (resp, "Set-Cookie", header_val);
1517 MHD_add_response_header (resp, "Access-Control-Allow-Methods", "POST");
1518 GNUNET_CRYPTO_hash (cookie, strlen (cookie), &cache_key);
1520 if (0 != strcmp (json_string_value (identity),
1523 current_time = GNUNET_new (struct GNUNET_TIME_Absolute);
1524 *current_time = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_multiply (GNUNET_TIME_relative_get_second_ (),
1525 OIDC_COOKIE_EXPIRATION));
1526 last_time = GNUNET_CONTAINER_multihashmap_get (OIDC_cookie_jar_map, &cache_key);
1527 GNUNET_free_non_null (last_time);
1528 GNUNET_CONTAINER_multihashmap_put (OIDC_cookie_jar_map,
1531 GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
1533 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
1534 GNUNET_free (cookie);
1535 GNUNET_free (header_val);
1537 GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
1541 check_authorization (struct RequestHandle *handle,
1542 struct GNUNET_CRYPTO_EcdsaPublicKey *cid)
1544 struct GNUNET_HashCode cache_key;
1545 char *authorization;
1547 char *basic_authorization;
1550 char *expected_pass;
1551 int client_exists = GNUNET_NO;
1553 GNUNET_CRYPTO_hash (OIDC_AUTHORIZATION_HEADER_KEY,
1554 strlen (OIDC_AUTHORIZATION_HEADER_KEY),
1556 if ( GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->header_param_map,
1559 handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1560 handle->edesc=GNUNET_strdup ("missing authorization");
1561 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1562 return GNUNET_SYSERR;
1564 authorization = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->header_param_map,
1567 //split header in "Basic" and [content]
1568 credentials = strtok (authorization, " ");
1569 if (0 != strcmp ("Basic", credentials))
1571 handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1572 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1573 return GNUNET_SYSERR;
1575 credentials = strtok (NULL, " ");
1576 if (NULL == credentials)
1578 handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1579 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1580 return GNUNET_SYSERR;
1582 GNUNET_STRINGS_base64_decode (credentials,
1583 strlen (credentials),
1584 (void**)&basic_authorization);
1586 if ( NULL == basic_authorization )
1588 handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1589 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1590 return GNUNET_SYSERR;
1592 client_id = strtok (basic_authorization, ":");
1593 if ( NULL == client_id )
1595 GNUNET_free_non_null (basic_authorization);
1596 handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1597 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1598 return GNUNET_SYSERR;
1600 pass = strtok (NULL, ":");
1603 GNUNET_free_non_null (basic_authorization);
1604 handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1605 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1606 return GNUNET_SYSERR;
1609 //check client password
1611 == GNUNET_CONFIGURATION_get_value_string (cfg,
1612 "reclaim-rest-plugin",
1616 if (0 != strcmp (expected_pass, pass))
1618 GNUNET_free_non_null (basic_authorization);
1619 GNUNET_free (expected_pass);
1620 handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1621 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1622 return GNUNET_SYSERR;
1624 GNUNET_free (expected_pass);
1628 GNUNET_free_non_null (basic_authorization);
1629 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
1630 handle->edesc = GNUNET_strdup ("gnunet configuration failed");
1631 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1632 return GNUNET_SYSERR;
1636 for (handle->ego_entry = handle->ego_head; NULL != handle->ego_entry; )
1638 if (0 == strcmp (handle->ego_entry->keystring, client_id))
1640 client_exists = GNUNET_YES;
1643 handle->ego_entry = handle->ego_entry->next;
1645 if (GNUNET_NO == client_exists)
1647 GNUNET_free_non_null (basic_authorization);
1648 handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1649 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1650 return GNUNET_SYSERR;
1652 GNUNET_STRINGS_string_to_data (client_id,
1655 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
1657 GNUNET_free (basic_authorization);
1662 ego_exists (struct RequestHandle *handle,
1663 struct GNUNET_CRYPTO_EcdsaPublicKey *test_key)
1665 struct EgoEntry *ego_entry;
1666 struct GNUNET_CRYPTO_EcdsaPublicKey pub_key;
1668 for (ego_entry = handle->ego_head; NULL != ego_entry; ego_entry = ego_entry->next)
1670 GNUNET_IDENTITY_ego_get_public_key (ego_entry->ego, &pub_key);
1671 if (0 == memcmp (&pub_key,
1673 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)))
1678 if (NULL == ego_entry)
1684 store_ticket_reference (const struct RequestHandle *handle,
1685 const char* access_token,
1686 const struct GNUNET_RECLAIM_Ticket *ticket,
1687 const struct GNUNET_CRYPTO_EcdsaPublicKey *cid)
1689 struct GNUNET_HashCode cache_key;
1690 char *id_ticket_combination;
1691 char *ticket_string;
1694 GNUNET_CRYPTO_hash (access_token,
1695 strlen (access_token),
1697 client_id = GNUNET_STRINGS_data_to_string_alloc (cid,
1698 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
1699 ticket_string = GNUNET_STRINGS_data_to_string_alloc (ticket,
1700 sizeof (struct GNUNET_RECLAIM_Ticket));
1701 GNUNET_asprintf (&id_ticket_combination,
1705 GNUNET_CONTAINER_multihashmap_put (OIDC_access_token_map,
1707 id_ticket_combination,
1708 GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
1710 GNUNET_free (client_id);
1711 GNUNET_free (ticket_string);
1715 * Responds to token url-encoded POST request
1717 * @param con_handle the connection handle
1718 * @param url the url
1719 * @param cls the RequestHandle
1722 token_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
1726 struct RequestHandle *handle = cls;
1727 struct GNUNET_TIME_Relative expiration_time;
1728 struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *cl;
1729 struct GNUNET_RECLAIM_Ticket *ticket;
1730 struct GNUNET_CRYPTO_EcdsaPublicKey cid;
1731 struct GNUNET_HashCode cache_key;
1732 struct MHD_Response *resp;
1735 char *json_response;
1743 * Check Authorization
1745 if (GNUNET_SYSERR == check_authorization (handle,
1748 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1749 "OIDC authorization for token endpoint failed\n");
1750 GNUNET_SCHEDULER_add_now (&do_error, handle);
1758 //TODO Do not allow multiple equal parameter names
1759 //REQUIRED grant_type
1760 GNUNET_CRYPTO_hash (OIDC_GRANT_TYPE_KEY, strlen (OIDC_GRANT_TYPE_KEY), &cache_key);
1762 GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1765 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1766 handle->edesc = GNUNET_strdup ("missing parameter grant_type");
1767 handle->response_code = MHD_HTTP_BAD_REQUEST;
1768 GNUNET_SCHEDULER_add_now (&do_error, handle);
1771 grant_type = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map,
1775 GNUNET_CRYPTO_hash (OIDC_CODE_KEY, strlen (OIDC_CODE_KEY), &cache_key);
1777 GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1780 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1781 handle->edesc = GNUNET_strdup ("missing parameter code");
1782 handle->response_code = MHD_HTTP_BAD_REQUEST;
1783 GNUNET_SCHEDULER_add_now (&do_error, handle);
1786 code = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map,
1789 //REQUIRED redirect_uri
1790 GNUNET_CRYPTO_hash (OIDC_REDIRECT_URI_KEY, strlen (OIDC_REDIRECT_URI_KEY),
1793 GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1796 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1797 handle->edesc = GNUNET_strdup ("missing parameter redirect_uri");
1798 handle->response_code = MHD_HTTP_BAD_REQUEST;
1799 GNUNET_SCHEDULER_add_now (&do_error, handle);
1803 //Check parameter grant_type == "authorization_code"
1804 if (0 != strcmp (OIDC_GRANT_TYPE_VALUE, grant_type))
1806 handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_UNSUPPORTED_GRANT_TYPE);
1807 handle->response_code = MHD_HTTP_BAD_REQUEST;
1808 GNUNET_SCHEDULER_add_now (&do_error, handle);
1811 GNUNET_CRYPTO_hash (code, strlen (code), &cache_key);
1812 if (GNUNET_SYSERR ==
1813 GNUNET_CONTAINER_multihashmap_put (OIDC_used_ticket_map,
1816 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY) )
1818 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1819 handle->edesc = GNUNET_strdup ("Cannot use the same code more than once");
1820 handle->response_code = MHD_HTTP_BAD_REQUEST;
1821 GNUNET_SCHEDULER_add_now (&do_error, handle);
1826 if (GNUNET_OK != OIDC_parse_authz_code (&cid,
1831 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1832 handle->edesc = GNUNET_strdup ("invalid code");
1833 handle->response_code = MHD_HTTP_BAD_REQUEST;
1834 GNUNET_SCHEDULER_add_now (&do_error, handle);
1840 GNUNET_CONFIGURATION_get_value_time (cfg,
1841 "reclaim-rest-plugin",
1845 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
1846 handle->edesc = GNUNET_strdup ("gnunet configuration failed");
1847 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1848 GNUNET_SCHEDULER_add_now (&do_error, handle);
1849 GNUNET_free (ticket);
1854 //TODO OPTIONAL acr,amr,azp
1855 if (GNUNET_NO == ego_exists (handle,
1858 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1859 handle->edesc = GNUNET_strdup ("invalid code...");
1860 handle->response_code = MHD_HTTP_BAD_REQUEST;
1861 GNUNET_SCHEDULER_add_now (&do_error, handle);
1862 GNUNET_free (ticket);
1865 != GNUNET_CONFIGURATION_get_value_string (cfg,
1866 "reclaim-rest-plugin",
1870 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1871 handle->edesc = GNUNET_strdup ("No signing secret configured!");
1872 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1873 GNUNET_SCHEDULER_add_now (&do_error, handle);
1874 GNUNET_free (ticket);
1877 //TODO We should collect the attributes here. cl always empty
1878 cl = GNUNET_new (struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList);
1879 id_token = OIDC_id_token_new (&ticket->audience,
1883 (NULL != nonce) ? nonce : NULL,
1885 access_token = OIDC_access_token_new ();
1886 OIDC_build_token_response (access_token,
1891 store_ticket_reference (handle,
1895 resp = GNUNET_REST_create_response (json_response);
1896 MHD_add_response_header (resp, "Cache-Control", "no-store");
1897 MHD_add_response_header (resp, "Pragma", "no-cache");
1898 MHD_add_response_header (resp, "Content-Type", "application/json");
1899 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
1900 GNUNET_RECLAIM_ATTRIBUTE_list_destroy (cl);
1901 GNUNET_free (access_token);
1902 GNUNET_free (json_response);
1903 GNUNET_free (ticket);
1904 GNUNET_free (id_token);
1905 GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
1909 * Collects claims and stores them in handle
1912 consume_ticket (void *cls,
1913 const struct GNUNET_CRYPTO_EcdsaPublicKey *identity,
1914 const struct GNUNET_RECLAIM_ATTRIBUTE_Claim *attr)
1916 struct RequestHandle *handle = cls;
1920 if (NULL == identity)
1922 GNUNET_SCHEDULER_add_now (&return_userinfo_response, handle);
1925 tmp_value = GNUNET_RECLAIM_ATTRIBUTE_value_to_string (attr->type,
1928 value = json_string (tmp_value);
1929 json_object_set_new (handle->oidc->response,
1932 GNUNET_free (tmp_value);
1936 * Responds to userinfo GET and url-encoded POST request
1938 * @param con_handle the connection handle
1939 * @param url the url
1940 * @param cls the RequestHandle
1943 userinfo_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
1944 const char* url, void *cls)
1946 //TODO expiration time
1947 struct RequestHandle *handle = cls;
1948 char delimiter[] = " ";
1949 char delimiter_db[] = ";";
1950 struct GNUNET_HashCode cache_key;
1951 char *authorization, *authorization_type, *authorization_access_token;
1952 char *client_ticket, *client, *ticket_str;
1953 struct GNUNET_RECLAIM_Ticket *ticket;
1955 GNUNET_CRYPTO_hash (OIDC_AUTHORIZATION_HEADER_KEY,
1956 strlen (OIDC_AUTHORIZATION_HEADER_KEY),
1959 == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->header_param_map,
1962 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
1963 handle->edesc = GNUNET_strdup ("No Access Token");
1964 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1965 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
1968 authorization = GNUNET_CONTAINER_multihashmap_get (
1969 handle->rest_handle->header_param_map, &cache_key);
1971 //split header in "Bearer" and access_token
1972 authorization = GNUNET_strdup (authorization);
1973 authorization_type = strtok (authorization, delimiter);
1974 if ( 0 != strcmp ("Bearer", authorization_type) )
1976 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
1977 handle->edesc = GNUNET_strdup ("No Access Token");
1978 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1979 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
1980 GNUNET_free (authorization);
1983 authorization_access_token = strtok (NULL, delimiter);
1984 if ( NULL == authorization_access_token )
1986 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
1987 handle->edesc = GNUNET_strdup ("No Access Token");
1988 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1989 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
1990 GNUNET_free (authorization);
1994 GNUNET_CRYPTO_hash (authorization_access_token,
1995 strlen (authorization_access_token),
1997 if ( GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (OIDC_access_token_map,
2000 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
2001 handle->edesc = GNUNET_strdup ("The Access Token expired");
2002 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2003 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2004 GNUNET_free (authorization);
2008 client_ticket = GNUNET_CONTAINER_multihashmap_get (OIDC_access_token_map,
2010 client_ticket = GNUNET_strdup (client_ticket);
2011 client = strtok (client_ticket,delimiter_db);
2014 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
2015 handle->edesc = GNUNET_strdup ("The Access Token expired");
2016 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2017 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2018 GNUNET_free (authorization);
2019 GNUNET_free (client_ticket);
2022 handle->ego_entry = handle->ego_head;
2023 for (; NULL != handle->ego_entry; handle->ego_entry = handle->ego_entry->next)
2025 if (0 == strcmp (handle->ego_entry->keystring,client))
2028 if (NULL == handle->ego_entry)
2030 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
2031 handle->edesc = GNUNET_strdup ("The Access Token expired");
2032 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2033 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2034 GNUNET_free (authorization);
2035 GNUNET_free (client_ticket);
2038 ticket_str = strtok (NULL, delimiter_db);
2039 if (NULL == ticket_str)
2041 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
2042 handle->edesc = GNUNET_strdup ("The Access Token expired");
2043 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2044 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2045 GNUNET_free (authorization);
2046 GNUNET_free (client_ticket);
2049 ticket = GNUNET_new (struct GNUNET_RECLAIM_Ticket);
2051 != GNUNET_STRINGS_string_to_data (ticket_str,
2052 strlen (ticket_str),
2054 sizeof (struct GNUNET_RECLAIM_Ticket)))
2056 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
2057 handle->edesc = GNUNET_strdup ("The Access Token expired");
2058 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2059 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2060 GNUNET_free (ticket);
2061 GNUNET_free (authorization);
2062 GNUNET_free (client_ticket);
2066 handle->idp = GNUNET_RECLAIM_connect (cfg);
2067 handle->oidc->response = json_object ();
2068 json_object_set_new (handle->oidc->response,
2070 json_string (handle->ego_entry->keystring));
2071 handle->idp_op = GNUNET_RECLAIM_ticket_consume (handle->idp,
2072 GNUNET_IDENTITY_ego_get_private_key (handle->ego_entry->ego),
2076 GNUNET_free (ticket);
2077 GNUNET_free (authorization);
2078 GNUNET_free (client_ticket);
2084 * Handle rest request
2086 * @param handle the request handle
2089 init_cont (struct RequestHandle *handle)
2091 struct GNUNET_REST_RequestHandlerError err;
2092 static const struct GNUNET_REST_RequestHandler handlers[] = {
2093 {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_AUTHORIZE, &authorize_endpoint},
2094 {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_AUTHORIZE, &authorize_endpoint}, //url-encoded
2095 {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_LOGIN, &login_cont},
2096 {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_TOKEN, &token_endpoint },
2097 {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_USERINFO, &userinfo_endpoint },
2098 {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_USERINFO, &userinfo_endpoint },
2099 {MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_OIDC,
2101 GNUNET_REST_HANDLER_END
2104 if (GNUNET_NO == GNUNET_REST_handle_request (handle->rest_handle,
2109 handle->response_code = err.error_code;
2110 GNUNET_SCHEDULER_add_now (&do_error, handle);
2115 * If listing is enabled, prints information about the egos.
2117 * This function is initially called for all egos and then again
2118 * whenever a ego's identifier changes or if it is deleted. At the
2119 * end of the initial pass over all egos, the function is once called
2120 * with 'NULL' for 'ego'. That does NOT mean that the callback won't
2121 * be invoked in the future or that there was an error.
2123 * When used with 'GNUNET_IDENTITY_create' or 'GNUNET_IDENTITY_get',
2124 * this function is only called ONCE, and 'NULL' being passed in
2125 * 'ego' does indicate an error (i.e. name is taken or no default
2126 * value is known). If 'ego' is non-NULL and if '*ctx'
2127 * is set in those callbacks, the value WILL be passed to a subsequent
2128 * call to the identity callback of 'GNUNET_IDENTITY_connect' (if
2129 * that one was not NULL).
2131 * When an identity is renamed, this function is called with the
2132 * (known) ego but the NEW identifier.
2134 * When an identity is deleted, this function is called with the
2135 * (known) ego and "NULL" for the 'identifier'. In this case,
2136 * the 'ego' is henceforth invalid (and the 'ctx' should also be
2139 * @param cls closure
2140 * @param ego ego handle
2141 * @param ctx context for application to store data for this ego
2142 * (during the lifetime of this process, initially NULL)
2143 * @param identifier identifier assigned by the user for this ego,
2144 * NULL if the user just deleted the ego and it
2145 * must thus no longer be used
2148 list_ego (void *cls,
2149 struct GNUNET_IDENTITY_Ego *ego,
2151 const char *identifier)
2153 struct RequestHandle *handle = cls;
2154 struct EgoEntry *ego_entry;
2155 struct GNUNET_CRYPTO_EcdsaPublicKey pk;
2157 if ((NULL == ego) && (ID_REST_STATE_INIT == handle->state))
2159 handle->state = ID_REST_STATE_POST_INIT;
2163 if (ID_REST_STATE_INIT == handle->state) {
2164 ego_entry = GNUNET_new (struct EgoEntry);
2165 GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
2166 ego_entry->keystring =
2167 GNUNET_CRYPTO_ecdsa_public_key_to_string (&pk);
2168 ego_entry->ego = ego;
2169 ego_entry->identifier = GNUNET_strdup (identifier);
2170 GNUNET_CONTAINER_DLL_insert_tail (handle->ego_head,
2175 /* Ego renamed or added */
2176 if (identifier != NULL) {
2177 for (ego_entry = handle->ego_head; NULL != ego_entry; ego_entry = ego_entry->next) {
2178 if (ego_entry->ego == ego) {
2180 GNUNET_free (ego_entry->identifier);
2181 ego_entry->identifier = GNUNET_strdup (identifier);
2185 if (NULL == ego_entry) {
2187 ego_entry = GNUNET_new (struct EgoEntry);
2188 GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
2189 ego_entry->keystring =
2190 GNUNET_CRYPTO_ecdsa_public_key_to_string (&pk);
2191 ego_entry->ego = ego;
2192 ego_entry->identifier = GNUNET_strdup (identifier);
2193 GNUNET_CONTAINER_DLL_insert_tail (handle->ego_head,
2199 for (ego_entry = handle->ego_head; NULL != ego_entry; ego_entry = ego_entry->next) {
2200 if (ego_entry->ego == ego)
2203 if (NULL != ego_entry)
2204 GNUNET_CONTAINER_DLL_remove (handle->ego_head,handle->ego_tail, ego_entry);
2210 rest_identity_process_request (struct GNUNET_REST_RequestHandle *rest_handle,
2211 GNUNET_REST_ResultProcessor proc,
2214 struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
2215 handle->oidc = GNUNET_new (struct OIDC_Variables);
2216 if (NULL == OIDC_cookie_jar_map)
2217 OIDC_cookie_jar_map = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
2218 if (NULL == OIDC_identity_grants)
2219 OIDC_identity_grants = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
2220 if (NULL == OIDC_used_ticket_map)
2221 OIDC_used_ticket_map = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
2222 if (NULL == OIDC_access_token_map)
2223 OIDC_access_token_map = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
2224 handle->response_code = 0;
2225 handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
2226 handle->proc_cls = proc_cls;
2227 handle->proc = proc;
2228 handle->state = ID_REST_STATE_INIT;
2229 handle->rest_handle = rest_handle;
2231 handle->url = GNUNET_strdup (rest_handle->url);
2232 if (handle->url[strlen (handle->url)-1] == '/')
2233 handle->url[strlen (handle->url)-1] = '\0';
2234 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2236 handle->identity_handle = GNUNET_IDENTITY_connect (cfg,
2239 handle->gns_handle = GNUNET_GNS_connect (cfg);
2240 handle->namestore_handle = GNUNET_NAMESTORE_connect (cfg);
2241 handle->timeout_task =
2242 GNUNET_SCHEDULER_add_delayed (handle->timeout,
2245 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2250 * Entry point for the plugin.
2252 * @param cls Config info
2253 * @return NULL on error, otherwise the plugin context
2256 libgnunet_plugin_rest_openid_connect_init (void *cls)
2258 static struct Plugin plugin;
2259 struct GNUNET_REST_Plugin *api;
2262 if (NULL != plugin.cfg)
2263 return NULL; /* can only initialize once! */
2264 memset (&plugin, 0, sizeof (struct Plugin));
2266 api = GNUNET_new (struct GNUNET_REST_Plugin);
2268 api->name = GNUNET_REST_API_NS_OIDC;
2269 api->process_request = &rest_identity_process_request;
2270 GNUNET_asprintf (&allow_methods,
2271 "%s, %s, %s, %s, %s",
2272 MHD_HTTP_METHOD_GET,
2273 MHD_HTTP_METHOD_POST,
2274 MHD_HTTP_METHOD_PUT,
2275 MHD_HTTP_METHOD_DELETE,
2276 MHD_HTTP_METHOD_OPTIONS);
2278 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2279 _("Identity Provider REST API initialized\n"));
2285 * Exit point from the plugin.
2287 * @param cls the plugin context (as returned by "init")
2288 * @return always NULL
2291 libgnunet_plugin_rest_openid_connect_done (void *cls)
2293 struct GNUNET_REST_Plugin *api = cls;
2294 struct Plugin *plugin = api->cls;
2297 struct GNUNET_CONTAINER_MultiHashMapIterator *hashmap_it;
2299 hashmap_it = GNUNET_CONTAINER_multihashmap_iterator_create (OIDC_cookie_jar_map);
2300 while (GNUNET_YES ==
2301 GNUNET_CONTAINER_multihashmap_iterator_next (hashmap_it, NULL, value))
2302 GNUNET_free_non_null (value);
2303 GNUNET_CONTAINER_multihashmap_destroy (OIDC_cookie_jar_map);
2305 hashmap_it = GNUNET_CONTAINER_multihashmap_iterator_create (OIDC_identity_grants);
2306 while (GNUNET_YES ==
2307 GNUNET_CONTAINER_multihashmap_iterator_next (hashmap_it, NULL, value))
2308 GNUNET_free_non_null (value);
2309 GNUNET_CONTAINER_multihashmap_destroy (OIDC_identity_grants);
2311 hashmap_it = GNUNET_CONTAINER_multihashmap_iterator_create (OIDC_used_ticket_map);
2312 while (GNUNET_YES ==
2313 GNUNET_CONTAINER_multihashmap_iterator_next (hashmap_it, NULL, value))
2314 GNUNET_free_non_null (value);
2315 GNUNET_CONTAINER_multihashmap_destroy (OIDC_used_ticket_map);
2317 hashmap_it = GNUNET_CONTAINER_multihashmap_iterator_create (OIDC_access_token_map);
2318 while (GNUNET_YES ==
2319 GNUNET_CONTAINER_multihashmap_iterator_next (hashmap_it, NULL, value))
2320 GNUNET_free_non_null (value);
2321 GNUNET_CONTAINER_multihashmap_destroy (OIDC_access_token_map);
2322 GNUNET_CONTAINER_multihashmap_iterator_destroy (hashmap_it);
2323 GNUNET_free_non_null (allow_methods);
2325 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2326 "Identity Provider REST plugin is finished\n");
2330 /* end of plugin_rest_identity_provider.c */