2 This file is part of GNUnet.
3 Copyright (C) 2012-2015 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/>.
19 * @author Martin Schanzenbach
20 * @author Philippe Buschmann
21 * @file identity/plugin_rest_openid_connect.c
22 * @brief GNUnet Namestore REST plugin
27 #include "gnunet_rest_plugin.h"
28 #include "gnunet_identity_service.h"
29 #include "gnunet_gns_service.h"
30 #include "gnunet_gnsrecord_lib.h"
31 #include "gnunet_namestore_service.h"
32 #include "gnunet_rest_lib.h"
33 #include "gnunet_jsonapi_lib.h"
34 #include "gnunet_jsonapi_util.h"
35 #include "microhttpd.h"
38 #include "gnunet_signatures.h"
39 #include "gnunet_reclaim_attribute_lib.h"
40 #include "gnunet_reclaim_service.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"
71 #define GNUNET_REST_JSONAPI_RECLAIM_ATTRIBUTE "attribute"
76 #define GNUNET_REST_JSONAPI_IDENTITY_TICKET "ticket"
82 #define GNUNET_REST_JSONAPI_RECLAIM_ATTRIBUTE_VALUE "value"
85 * State while collecting all egos
87 #define ID_REST_STATE_INIT 0
90 * Done collecting egos
92 #define ID_REST_STATE_POST_INIT 1
97 #define OIDC_GRANT_TYPE_KEY "grant_type"
100 * OIDC grant_type key
102 #define OIDC_GRANT_TYPE_VALUE "authorization_code"
107 #define OIDC_CODE_KEY "code"
110 * OIDC response_type key
112 #define OIDC_RESPONSE_TYPE_KEY "response_type"
117 #define OIDC_CLIENT_ID_KEY "client_id"
122 #define OIDC_SCOPE_KEY "scope"
125 * OIDC redirect_uri key
127 #define OIDC_REDIRECT_URI_KEY "redirect_uri"
132 #define OIDC_STATE_KEY "state"
137 #define OIDC_NONCE_KEY "nonce"
140 * OIDC cookie header key
142 #define OIDC_COOKIE_HEADER_KEY "cookie"
145 * OIDC cookie header information key
147 #define OIDC_AUTHORIZATION_HEADER_KEY "authorization"
150 * OIDC cookie header information key
152 #define OIDC_COOKIE_HEADER_INFORMATION_KEY "Identity="
155 * OIDC expected response_type while authorizing
157 #define OIDC_EXPECTED_AUTHORIZATION_RESPONSE_TYPE "code"
160 * OIDC expected scope part while authorizing
162 #define OIDC_EXPECTED_AUTHORIZATION_SCOPE "openid"
165 * OIDC ignored parameter array
167 static char* OIDC_ignored_parameter_array [] =
180 * OIDC authorized identities and times hashmap
182 struct GNUNET_CONTAINER_MultiHashMap *OIDC_identity_login_time;
185 * OIDC authorized identities and times hashmap
187 struct GNUNET_CONTAINER_MultiHashMap *OIDC_identity_grants;
190 * OIDC ticket/code use only once
192 struct GNUNET_CONTAINER_MultiHashMap *OIDC_ticket_once;
195 * OIDC access_token to ticket and ego
197 struct GNUNET_CONTAINER_MultiHashMap *OIDC_interpret_access_token;
200 * The configuration handle
202 const struct GNUNET_CONFIGURATION_Handle *cfg;
205 * HTTP methods allows for this plugin
207 static char* allow_methods;
210 * @brief struct returned by the initialization function of the plugin
214 const struct GNUNET_CONFIGURATION_Handle *cfg;
218 * OIDC needed variables
220 struct OIDC_Variables
223 * The RP client public key
225 struct GNUNET_CRYPTO_EcdsaPublicKey client_pkey;
228 * The OIDC client id of the RP
233 * The OIDC redirect uri
238 * The list of oidc scopes
253 * The OIDC response type
258 * The identity chosen by the user to login
260 char *login_identity;
277 struct EgoEntry *next;
282 struct EgoEntry *prev;
297 struct GNUNET_IDENTITY_Ego *ego;
306 struct EgoEntry *ego_head;
311 struct EgoEntry *ego_tail;
316 struct EgoEntry *ego_entry;
319 * Pointer to ego private key
321 struct GNUNET_CRYPTO_EcdsaPrivateKey priv_key;
326 struct OIDC_Variables *oidc;
329 * The processing state
334 * Handle to Identity service.
336 struct GNUNET_IDENTITY_Handle *identity_handle;
341 struct GNUNET_REST_RequestHandle *rest_handle;
344 * Handle to NAMESTORE
346 struct GNUNET_NAMESTORE_Handle *namestore_handle;
349 * Iterator for NAMESTORE
351 struct GNUNET_NAMESTORE_ZoneIterator *namestore_handle_it;
354 * Attribute claim list
356 struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attr_list;
361 struct GNUNET_IDENTITY_Operation *op;
366 struct GNUNET_RECLAIM_Handle *idp;
371 struct GNUNET_RECLAIM_Operation *idp_op;
376 struct GNUNET_RECLAIM_AttributeIterator *attr_it;
381 struct GNUNET_RECLAIM_TicketIterator *ticket_it;
386 struct GNUNET_RECLAIM_Ticket ticket;
389 * Desired timeout for the lookup (default is no timeout).
391 struct GNUNET_TIME_Relative timeout;
394 * ID of a task associated with the resolution process.
396 struct GNUNET_SCHEDULER_Task *timeout_task;
399 * The plugin result processor
401 GNUNET_REST_ResultProcessor proc;
404 * The closure of the result processor
414 * The tld for redirect
419 * Error response message
424 * Error response description
436 struct GNUNET_JSONAPI_Document *resp_object;
441 * Cleanup lookup handle
442 * @param handle Handle to clean up
445 cleanup_handle (struct RequestHandle *handle)
447 struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *claim_entry;
448 struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *claim_tmp;
449 struct EgoEntry *ego_entry;
450 struct EgoEntry *ego_tmp;
451 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
453 if (NULL != handle->resp_object)
454 GNUNET_JSONAPI_document_delete (handle->resp_object);
455 if (NULL != handle->timeout_task)
456 GNUNET_SCHEDULER_cancel (handle->timeout_task);
457 if (NULL != handle->identity_handle)
458 GNUNET_IDENTITY_disconnect (handle->identity_handle);
459 if (NULL != handle->attr_it)
460 GNUNET_RECLAIM_get_attributes_stop (handle->attr_it);
461 if (NULL != handle->ticket_it)
462 GNUNET_RECLAIM_ticket_iteration_stop (handle->ticket_it);
463 if (NULL != handle->idp)
464 GNUNET_RECLAIM_disconnect (handle->idp);
465 if (NULL != handle->url)
466 GNUNET_free (handle->url);
467 if (NULL != handle->tld)
468 GNUNET_free (handle->tld);
469 if (NULL != handle->emsg)
470 GNUNET_free (handle->emsg);
471 if (NULL != handle->edesc)
472 GNUNET_free (handle->edesc);
473 if (NULL != handle->namestore_handle)
474 GNUNET_NAMESTORE_disconnect (handle->namestore_handle);
475 if (NULL != handle->oidc)
477 if (NULL != handle->oidc->client_id)
478 GNUNET_free(handle->oidc->client_id);
479 if (NULL != handle->oidc->login_identity)
480 GNUNET_free(handle->oidc->login_identity);
481 if (NULL != handle->oidc->nonce)
482 GNUNET_free(handle->oidc->nonce);
483 if (NULL != handle->oidc->redirect_uri)
484 GNUNET_free(handle->oidc->redirect_uri);
485 if (NULL != handle->oidc->response_type)
486 GNUNET_free(handle->oidc->response_type);
487 if (NULL != handle->oidc->scope)
488 GNUNET_free(handle->oidc->scope);
489 if (NULL != handle->oidc->state)
490 GNUNET_free(handle->oidc->state);
491 if (NULL != handle->oidc->response)
492 json_decref(handle->oidc->response);
493 GNUNET_free(handle->oidc);
495 if ( NULL != handle->attr_list )
497 for (claim_entry = handle->attr_list->list_head;
498 NULL != claim_entry;)
500 claim_tmp = claim_entry;
501 claim_entry = claim_entry->next;
502 GNUNET_free(claim_tmp->claim);
503 GNUNET_free(claim_tmp);
505 GNUNET_free (handle->attr_list);
507 for (ego_entry = handle->ego_head;
511 ego_entry = ego_entry->next;
512 GNUNET_free (ego_tmp->identifier);
513 GNUNET_free (ego_tmp->keystring);
514 GNUNET_free (ego_tmp);
516 if (NULL != handle->attr_it)
518 GNUNET_free(handle->attr_it);
520 GNUNET_free (handle);
524 cleanup_handle_delayed (void *cls)
526 cleanup_handle (cls);
531 * Task run on error, sends error message. Cleans up everything.
533 * @param cls the `struct RequestHandle`
538 struct RequestHandle *handle = cls;
539 struct MHD_Response *resp;
542 GNUNET_asprintf (&json_error, "{ \"error\" : \"%s\", \"error_description\" : \"%s\"%s%s%s}",
544 (NULL != handle->edesc) ? handle->edesc : "",
545 (NULL != handle->oidc->state) ? ", \"state\":\"" : "",
546 (NULL != handle->oidc->state) ? handle->oidc->state : "",
547 (NULL != handle->oidc->state) ? "\"" : "");
548 if ( 0 == handle->response_code )
550 handle->response_code = MHD_HTTP_BAD_REQUEST;
552 resp = GNUNET_REST_create_response (json_error);
553 if (MHD_HTTP_UNAUTHORIZED == handle->response_code)
555 MHD_add_response_header(resp, "WWW-Authenticate", "Basic");
557 MHD_add_response_header (resp, "Content-Type", "application/json");
558 handle->proc (handle->proc_cls, resp, handle->response_code);
559 GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
560 GNUNET_free (json_error);
565 * Task run on error in userinfo endpoint, sends error header. Cleans up
568 * @param cls the `struct RequestHandle`
571 do_userinfo_error (void *cls)
573 struct RequestHandle *handle = cls;
574 struct MHD_Response *resp;
577 GNUNET_asprintf (&error, "error=\"%s\", error_description=\"%s\"",
579 (NULL != handle->edesc) ? handle->edesc : "");
580 resp = GNUNET_REST_create_response ("");
581 MHD_add_response_header(resp, "WWW-Authenticate", error);
582 handle->proc (handle->proc_cls, resp, handle->response_code);
583 GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
589 * Task run on error, sends error message and redirects. Cleans up everything.
591 * @param cls the `struct RequestHandle`
594 do_redirect_error (void *cls)
596 struct RequestHandle *handle = cls;
597 struct MHD_Response *resp;
599 GNUNET_asprintf (&redirect,
600 "%s?error=%s&error_description=%s%s%s",
601 handle->oidc->redirect_uri, handle->emsg, handle->edesc,
602 (NULL != handle->oidc->state) ? "&state=" : "",
603 (NULL != handle->oidc->state) ? handle->oidc->state : "");
604 resp = GNUNET_REST_create_response ("");
605 MHD_add_response_header (resp, "Location", redirect);
606 handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
607 GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
608 GNUNET_free (redirect);
612 * Task run on timeout, sends error message. Cleans up everything.
614 * @param cls the `struct RequestHandle`
617 do_timeout (void *cls)
619 struct RequestHandle *handle = cls;
621 handle->timeout_task = NULL;
626 * Return attributes for claim
628 * @param cls the request handle
631 return_userinfo_response (void *cls)
634 struct RequestHandle *handle = cls;
635 struct MHD_Response *resp;
637 result_str = json_dumps (handle->oidc->response, 0);
639 resp = GNUNET_REST_create_response (result_str);
640 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
641 GNUNET_free (result_str);
642 cleanup_handle (handle);
646 * Returns base64 encoded string without padding
648 * @param string the string to encode
649 * @return base64 encoded string
652 base_64_encode(const char *s)
657 GNUNET_STRINGS_base64_encode(s, strlen(s), &enc);
658 tmp = strrchr (enc, '=');
664 * Respond to OPTIONS request
666 * @param con_handle the connection handle
668 * @param cls the RequestHandle
671 options_cont (struct GNUNET_REST_RequestHandle *con_handle,
675 struct MHD_Response *resp;
676 struct RequestHandle *handle = cls;
678 //For now, independent of path return all options
679 resp = GNUNET_REST_create_response (NULL);
680 MHD_add_response_header (resp,
681 "Access-Control-Allow-Methods",
683 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
684 cleanup_handle (handle);
689 * Interprets cookie header and pass its identity keystring to handle
692 cookie_identity_interpretation (struct RequestHandle *handle)
694 struct GNUNET_HashCode cache_key;
696 struct GNUNET_TIME_Absolute current_time, *relog_time;
697 char delimiter[] = "; ";
699 //gets identity of login try with cookie
700 GNUNET_CRYPTO_hash (OIDC_COOKIE_HEADER_KEY, strlen (OIDC_COOKIE_HEADER_KEY),
702 if ( GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->header_param_map,
705 //splits cookies and find 'Identity' cookie
706 cookies = GNUNET_CONTAINER_multihashmap_get ( handle->rest_handle->header_param_map, &cache_key);
707 handle->oidc->login_identity = strtok(cookies, delimiter);
709 while ( NULL != handle->oidc->login_identity )
711 if ( NULL != strstr (handle->oidc->login_identity, OIDC_COOKIE_HEADER_INFORMATION_KEY) )
715 handle->oidc->login_identity = strtok (NULL, delimiter);
717 GNUNET_CRYPTO_hash (handle->oidc->login_identity, strlen (handle->oidc->login_identity),
719 if ( GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (OIDC_identity_login_time, &cache_key) )
721 relog_time = GNUNET_CONTAINER_multihashmap_get (OIDC_identity_login_time,
723 current_time = GNUNET_TIME_absolute_get ();
724 // 30 min after old login -> redirect to login
725 if ( current_time.abs_value_us <= relog_time->abs_value_us )
727 handle->oidc->login_identity = strtok(handle->oidc->login_identity, OIDC_COOKIE_HEADER_INFORMATION_KEY);
728 handle->oidc->login_identity = GNUNET_strdup(handle->oidc->login_identity);
730 handle->oidc->login_identity = NULL;
735 handle->oidc->login_identity = NULL;
741 * Redirects to login page stored in configuration file
744 login_redirection(void *cls)
746 char *login_base_url;
748 struct MHD_Response *resp;
749 struct RequestHandle *handle = cls;
752 == GNUNET_CONFIGURATION_get_value_string (cfg, "reclaim-rest-plugin",
753 "address", &login_base_url) )
755 GNUNET_asprintf (&new_redirect, "%s?%s=%s&%s=%s&%s=%s&%s=%s&%s=%s&%s=%s",
757 OIDC_RESPONSE_TYPE_KEY,
758 handle->oidc->response_type,
760 handle->oidc->client_id,
761 OIDC_REDIRECT_URI_KEY,
762 handle->oidc->redirect_uri,
766 (NULL != handle->oidc->state) ? handle->oidc->state : "",
768 (NULL != handle->oidc->nonce) ? handle->oidc->nonce : "");
769 resp = GNUNET_REST_create_response ("");
770 MHD_add_response_header (resp, "Location", new_redirect);
771 GNUNET_free(login_base_url);
775 handle->emsg = GNUNET_strdup("server_error");
776 handle->edesc = GNUNET_strdup ("gnunet configuration failed");
777 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
778 GNUNET_SCHEDULER_add_now (&do_error, handle);
781 handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
782 GNUNET_free(new_redirect);
783 GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
787 * Does internal server error when iteration failed.
790 oidc_iteration_error (void *cls)
792 struct RequestHandle *handle = cls;
793 handle->emsg = GNUNET_strdup("INTERNAL_SERVER_ERROR");
794 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
795 GNUNET_SCHEDULER_add_now (&do_error, handle);
798 static void get_client_name_result (void *cls,
799 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
801 unsigned int rd_count,
802 const struct GNUNET_GNSRECORD_Data *rd)
804 struct RequestHandle *handle = cls;
805 struct MHD_Response *resp;
808 char *code_json_string;
809 char *code_base64_final_string;
814 ticket_str = GNUNET_STRINGS_data_to_string_alloc (&handle->ticket,
815 sizeof (struct GNUNET_RECLAIM_Ticket));
816 //TODO change if more attributes are needed (see max_age)
817 GNUNET_asprintf (&code_json_string, "{\"ticket\":\"%s\"%s%s%s}",
819 (NULL != handle->oidc->nonce) ? ", \"nonce\":\"" : "",
820 (NULL != handle->oidc->nonce) ? handle->oidc->nonce : "",
821 (NULL != handle->oidc->nonce) ? "\"" : "");
822 code_base64_final_string = base_64_encode(code_json_string);
823 tmp = GNUNET_strdup (handle->oidc->redirect_uri);
824 redirect_path = strtok (tmp, "/");
825 redirect_path = strtok (NULL, "/");
826 redirect_path = strtok (NULL, "/");
827 tmp_prefix = GNUNET_strdup (handle->oidc->redirect_uri);
828 prefix = strrchr (tmp_prefix,
829 (unsigned char) '.');
831 GNUNET_asprintf (&redirect_uri, "%s.%s/%s?%s=%s&state=%s",
835 handle->oidc->response_type,
836 code_base64_final_string, handle->oidc->state);
837 resp = GNUNET_REST_create_response ("");
838 MHD_add_response_header (resp, "Location", redirect_uri);
839 handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
840 GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
842 GNUNET_free (tmp_prefix);
843 GNUNET_free (redirect_uri);
844 GNUNET_free (ticket_str);
845 GNUNET_free (code_json_string);
846 GNUNET_free (code_base64_final_string);
851 get_client_name_error (void *cls)
853 struct RequestHandle *handle = cls;
855 handle->emsg = GNUNET_strdup("server_error");
856 handle->edesc = GNUNET_strdup("Server cannot generate ticket, no name found for client.");
857 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
861 * Issues ticket and redirects to relying party with the authorization code as
862 * parameter. Otherwise redirects with error
865 oidc_ticket_issue_cb (void* cls,
866 const struct GNUNET_RECLAIM_Ticket *ticket)
868 struct RequestHandle *handle = cls;
869 handle->idp_op = NULL;
870 handle->ticket = *ticket;
871 if (NULL != ticket) {
872 GNUNET_NAMESTORE_zone_to_name (handle->namestore_handle,
874 &handle->oidc->client_pkey,
875 &get_client_name_error,
877 &get_client_name_result,
881 handle->emsg = GNUNET_strdup("server_error");
882 handle->edesc = GNUNET_strdup("Server cannot generate ticket.");
883 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
887 oidc_collect_finished_cb (void *cls)
889 struct RequestHandle *handle = cls;
890 handle->attr_it = NULL;
891 handle->ticket_it = NULL;
892 if (NULL == handle->attr_list->list_head)
894 handle->emsg = GNUNET_strdup("invalid_scope");
895 handle->edesc = GNUNET_strdup("The requested scope is not available.");
896 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
899 handle->idp_op = GNUNET_RECLAIM_ticket_issue (handle->idp,
901 &handle->oidc->client_pkey,
903 &oidc_ticket_issue_cb,
909 * Collects all attributes for an ego if in scope parameter
912 oidc_attr_collect (void *cls,
913 const struct GNUNET_CRYPTO_EcdsaPublicKey *identity,
914 const struct GNUNET_RECLAIM_ATTRIBUTE_Claim *attr)
916 struct RequestHandle *handle = cls;
917 struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *le;
918 char* scope_variables;
919 char* scope_variable;
920 char delimiter[]=" ";
922 if ( (NULL == attr->name) || (NULL == attr->data) )
924 GNUNET_RECLAIM_get_attributes_next (handle->attr_it);
928 scope_variables = GNUNET_strdup(handle->oidc->scope);
929 scope_variable = strtok (scope_variables, delimiter);
930 while (NULL != scope_variable)
932 if ( 0 == strcmp (attr->name, scope_variable) )
936 scope_variable = strtok (NULL, delimiter);
938 if ( NULL == scope_variable )
940 GNUNET_RECLAIM_get_attributes_next (handle->attr_it);
941 GNUNET_free(scope_variables);
944 GNUNET_free(scope_variables);
946 le = GNUNET_new(struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry);
947 le->claim = GNUNET_RECLAIM_ATTRIBUTE_claim_new (attr->name, attr->type,
948 attr->data, attr->data_size);
949 GNUNET_CONTAINER_DLL_insert(handle->attr_list->list_head,
950 handle->attr_list->list_tail, le);
951 GNUNET_RECLAIM_get_attributes_next (handle->attr_it);
956 * Checks time and cookie and redirects accordingly
959 login_check (void *cls)
961 struct RequestHandle *handle = cls;
962 struct GNUNET_TIME_Absolute current_time, *relog_time;
963 struct GNUNET_CRYPTO_EcdsaPublicKey pubkey, ego_pkey;
964 struct GNUNET_HashCode cache_key;
965 char *identity_cookie;
967 GNUNET_asprintf (&identity_cookie, "Identity=%s", handle->oidc->login_identity);
968 GNUNET_CRYPTO_hash (identity_cookie, strlen (identity_cookie), &cache_key);
969 GNUNET_free(identity_cookie);
970 //No login time for identity -> redirect to login
972 == GNUNET_CONTAINER_multihashmap_contains (OIDC_identity_login_time,
975 relog_time = GNUNET_CONTAINER_multihashmap_get (OIDC_identity_login_time,
977 current_time = GNUNET_TIME_absolute_get ();
978 // 30 min after old login -> redirect to login
979 if ( current_time.abs_value_us <= relog_time->abs_value_us )
982 != GNUNET_CRYPTO_ecdsa_public_key_from_string (
983 handle->oidc->login_identity,
984 strlen (handle->oidc->login_identity), &pubkey) )
986 handle->emsg = GNUNET_strdup("invalid_cookie");
987 handle->edesc = GNUNET_strdup(
988 "The cookie of a login identity is not valid");
989 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
992 // iterate over egos and compare their public key
993 for (handle->ego_entry = handle->ego_head;
994 NULL != handle->ego_entry; handle->ego_entry = handle->ego_entry->next)
996 GNUNET_IDENTITY_ego_get_public_key (handle->ego_entry->ego, &ego_pkey);
998 == memcmp (&ego_pkey, &pubkey,
999 sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey)) )
1001 handle->priv_key = *GNUNET_IDENTITY_ego_get_private_key (
1002 handle->ego_entry->ego);
1003 handle->resp_object = GNUNET_JSONAPI_document_new ();
1004 handle->idp = GNUNET_RECLAIM_connect (cfg);
1005 handle->attr_list = GNUNET_new(
1006 struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList);
1007 handle->attr_it = GNUNET_RECLAIM_get_attributes_start (
1008 handle->idp, &handle->priv_key, &oidc_iteration_error, handle,
1009 &oidc_attr_collect, handle, &oidc_collect_finished_cb, handle);
1013 //handle->emsg = GNUNET_strdup("invalid_cookie");
1014 //handle->edesc = GNUNET_strdup(
1015 // "The cookie of the login identity is not valid");
1016 //GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1017 GNUNET_SCHEDULER_add_now (&login_redirection,handle);
1024 * Iteration over all results finished, build final
1027 * @param cls the `struct RequestHandle`
1030 build_authz_response (void *cls)
1032 struct RequestHandle *handle = cls;
1033 struct GNUNET_HashCode cache_key;
1035 char *expected_scope;
1036 char delimiter[]=" ";
1037 int number_of_ignored_parameter, iterator;
1040 // REQUIRED value: redirect_uri
1041 GNUNET_CRYPTO_hash (OIDC_REDIRECT_URI_KEY, strlen (OIDC_REDIRECT_URI_KEY),
1043 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1046 handle->emsg=GNUNET_strdup("invalid_request");
1047 handle->edesc=GNUNET_strdup("missing parameter redirect_uri");
1048 GNUNET_SCHEDULER_add_now (&do_error, handle);
1051 handle->oidc->redirect_uri = GNUNET_strdup (GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
1054 // REQUIRED value: response_type
1055 GNUNET_CRYPTO_hash (OIDC_RESPONSE_TYPE_KEY, strlen (OIDC_RESPONSE_TYPE_KEY),
1057 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1060 handle->emsg=GNUNET_strdup("invalid_request");
1061 handle->edesc=GNUNET_strdup("missing parameter response_type");
1062 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1065 handle->oidc->response_type = GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
1067 handle->oidc->response_type = GNUNET_strdup (handle->oidc->response_type);
1069 // REQUIRED value: scope
1070 GNUNET_CRYPTO_hash (OIDC_SCOPE_KEY, strlen (OIDC_SCOPE_KEY), &cache_key);
1071 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1074 handle->emsg=GNUNET_strdup("invalid_request");
1075 handle->edesc=GNUNET_strdup("missing parameter scope");
1076 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1079 handle->oidc->scope = GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
1081 handle->oidc->scope = GNUNET_strdup(handle->oidc->scope);
1083 //OPTIONAL value: nonce
1084 GNUNET_CRYPTO_hash (OIDC_NONCE_KEY, strlen (OIDC_NONCE_KEY), &cache_key);
1085 if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1088 handle->oidc->nonce = GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
1090 handle->oidc->nonce = GNUNET_strdup (handle->oidc->nonce);
1093 //TODO check other values if needed
1094 number_of_ignored_parameter = sizeof(OIDC_ignored_parameter_array) / sizeof(char *);
1095 for( iterator = 0; iterator < number_of_ignored_parameter; iterator++ )
1097 GNUNET_CRYPTO_hash (OIDC_ignored_parameter_array[iterator],
1098 strlen(OIDC_ignored_parameter_array[iterator]),
1100 if(GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains(handle->rest_handle->url_param_map,
1103 handle->emsg=GNUNET_strdup("access_denied");
1104 GNUNET_asprintf (&handle->edesc, "Server will not handle parameter: %s",
1105 OIDC_ignored_parameter_array[iterator]);
1106 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1111 // Checks if response_type is 'code'
1112 if( 0 != strcmp( handle->oidc->response_type, OIDC_EXPECTED_AUTHORIZATION_RESPONSE_TYPE ) )
1114 handle->emsg=GNUNET_strdup("unsupported_response_type");
1115 handle->edesc=GNUNET_strdup("The authorization server does not support "
1116 "obtaining this authorization code.");
1117 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1121 // Checks if scope contains 'openid'
1122 expected_scope = GNUNET_strdup(handle->oidc->scope);
1124 test = strtok (expected_scope, delimiter);
1125 while (NULL != test)
1127 if ( 0 == strcmp (OIDC_EXPECTED_AUTHORIZATION_SCOPE, expected_scope) )
1131 test = strtok (NULL, delimiter);
1135 handle->emsg = GNUNET_strdup("invalid_scope");
1136 handle->edesc=GNUNET_strdup("The requested scope is invalid, unknown, or "
1138 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1139 GNUNET_free(expected_scope);
1143 GNUNET_free(expected_scope);
1145 if( NULL != handle->oidc->login_identity )
1147 GNUNET_SCHEDULER_add_now(&login_check,handle);
1151 GNUNET_SCHEDULER_add_now(&login_redirection,handle);
1155 * Responds to authorization GET and url-encoded POST request
1157 * @param con_handle the connection handle
1158 * @param url the url
1159 * @param cls the RequestHandle
1162 authorize_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
1166 struct RequestHandle *handle = cls;
1167 struct GNUNET_HashCode cache_key;
1168 struct EgoEntry *tmp_ego;
1169 const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key;
1170 struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
1172 cookie_identity_interpretation(handle);
1174 //RECOMMENDED value: state - REQUIRED for answers
1175 GNUNET_CRYPTO_hash (OIDC_STATE_KEY, strlen (OIDC_STATE_KEY), &cache_key);
1176 if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1179 handle->oidc->state = GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
1181 handle->oidc->state = GNUNET_strdup (handle->oidc->state);
1184 // REQUIRED value: client_id
1185 GNUNET_CRYPTO_hash (OIDC_CLIENT_ID_KEY, strlen (OIDC_CLIENT_ID_KEY),
1187 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1190 handle->emsg=GNUNET_strdup("invalid_request");
1191 handle->edesc=GNUNET_strdup("missing parameter client_id");
1192 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1193 GNUNET_SCHEDULER_add_now (&do_error, handle);
1196 handle->oidc->client_id = GNUNET_strdup (GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
1200 != GNUNET_CRYPTO_ecdsa_public_key_from_string (handle->oidc->client_id,
1201 strlen (handle->oidc->client_id),
1202 &handle->oidc->client_pkey) )
1204 handle->emsg = GNUNET_strdup("unauthorized_client");
1205 handle->edesc = GNUNET_strdup("The client is not authorized to request an "
1206 "authorization code using this method.");
1207 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1208 GNUNET_SCHEDULER_add_now (&do_error, handle);
1213 if ( NULL == handle->ego_head )
1215 handle->emsg = GNUNET_strdup("server_error");
1216 handle->edesc = GNUNET_strdup ("Egos are missing");
1217 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1218 GNUNET_SCHEDULER_add_now (&do_error, handle);
1222 handle->ego_entry = handle->ego_head;
1223 handle->priv_key = *GNUNET_IDENTITY_ego_get_private_key (handle->ego_head->ego);
1224 //If we know this identity, translated the corresponding TLD
1225 //TODO: We might want to have a reverse lookup functionality for TLDs?
1226 for (tmp_ego = handle->ego_head; NULL != tmp_ego; tmp_ego = tmp_ego->next)
1228 priv_key = GNUNET_IDENTITY_ego_get_private_key (tmp_ego->ego);
1229 GNUNET_CRYPTO_ecdsa_key_get_public (priv_key,
1231 if ( 0 == memcmp (&pkey, &handle->oidc->client_pkey,
1232 sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey)) )
1234 handle->tld = GNUNET_strdup (tmp_ego->identifier);
1235 handle->ego_entry = handle->ego_tail;
1238 GNUNET_SCHEDULER_add_now (&build_authz_response, handle);
1242 * Combines an identity with a login time and responds OK to login request
1244 * @param con_handle the connection handle
1245 * @param url the url
1246 * @param cls the RequestHandle
1249 login_cont (struct GNUNET_REST_RequestHandle *con_handle,
1253 struct MHD_Response *resp = GNUNET_REST_create_response ("");
1254 struct RequestHandle *handle = cls;
1255 struct GNUNET_HashCode cache_key;
1256 struct GNUNET_TIME_Absolute *current_time;
1257 struct GNUNET_TIME_Absolute *last_time;
1262 char term_data[handle->rest_handle->data_size+1];
1263 term_data[handle->rest_handle->data_size] = '\0';
1264 GNUNET_memcpy (term_data, handle->rest_handle->data, handle->rest_handle->data_size);
1265 root = json_loads (term_data, JSON_DECODE_ANY, &error);
1266 identity = json_object_get (root, "identity");
1267 if ( json_is_string(identity) )
1269 GNUNET_asprintf (&cookie, "Identity=%s", json_string_value (identity));
1270 MHD_add_response_header (resp, "Set-Cookie", cookie);
1271 MHD_add_response_header (resp, "Access-Control-Allow-Methods", "POST");
1272 GNUNET_CRYPTO_hash (cookie, strlen (cookie), &cache_key);
1274 current_time = GNUNET_new(struct GNUNET_TIME_Absolute);
1275 *current_time = GNUNET_TIME_relative_to_absolute (
1276 GNUNET_TIME_relative_multiply (GNUNET_TIME_relative_get_second_ (),
1278 last_time = GNUNET_CONTAINER_multihashmap_get(OIDC_identity_login_time, &cache_key);
1279 if (NULL != last_time)
1281 GNUNET_free(last_time);
1283 GNUNET_CONTAINER_multihashmap_put (
1284 OIDC_identity_login_time, &cache_key, current_time,
1285 GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
1287 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
1288 GNUNET_free(cookie);
1292 handle->proc (handle->proc_cls, resp, MHD_HTTP_BAD_REQUEST);
1295 GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
1300 * Responds to token url-encoded POST request
1302 * @param con_handle the connection handle
1303 * @param url the url
1304 * @param cls the RequestHandle
1307 token_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
1311 //TODO static strings
1312 struct RequestHandle *handle = cls;
1313 struct GNUNET_HashCode cache_key;
1314 char *authorization, *credentials;
1315 char delimiter[]=" ";
1316 char delimiter_user_psw[]=":";
1317 char *grant_type, *code;
1318 char *user_psw = NULL, *client_id, *psw;
1320 int client_exists = GNUNET_NO;
1321 struct MHD_Response *resp;
1323 json_t *root, *ticket_string, *nonce, *max_age;
1325 char *json_response;
1329 * Check Authorization
1331 GNUNET_CRYPTO_hash (OIDC_AUTHORIZATION_HEADER_KEY,
1332 strlen (OIDC_AUTHORIZATION_HEADER_KEY),
1334 if ( GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->header_param_map,
1337 handle->emsg=GNUNET_strdup("invalid_client");
1338 handle->edesc=GNUNET_strdup("missing authorization");
1339 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1340 GNUNET_SCHEDULER_add_now (&do_error, handle);
1343 authorization = GNUNET_CONTAINER_multihashmap_get ( handle->rest_handle->header_param_map, &cache_key);
1345 //split header in "Basic" and [content]
1346 credentials = strtok (authorization, delimiter);
1347 if (0 != strcmp ("Basic",credentials))
1349 handle->emsg=GNUNET_strdup("invalid_client");
1350 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1351 GNUNET_SCHEDULER_add_now (&do_error, handle);
1354 credentials = strtok(NULL, delimiter);
1355 if (NULL == credentials)
1357 handle->emsg=GNUNET_strdup("invalid_client");
1358 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1359 GNUNET_SCHEDULER_add_now (&do_error, handle);
1362 GNUNET_STRINGS_base64_decode (credentials, strlen (credentials), (void**)&user_psw);
1364 if ( NULL == user_psw )
1366 handle->emsg=GNUNET_strdup("invalid_client");
1367 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1368 GNUNET_SCHEDULER_add_now (&do_error, handle);
1371 client_id = strtok (user_psw, delimiter_user_psw);
1372 if ( NULL == client_id )
1374 GNUNET_free_non_null(user_psw);
1375 handle->emsg=GNUNET_strdup("invalid_client");
1376 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1377 GNUNET_SCHEDULER_add_now (&do_error, handle);
1380 psw = strtok (NULL, delimiter_user_psw);
1383 GNUNET_free_non_null(user_psw);
1384 handle->emsg=GNUNET_strdup("invalid_client");
1385 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1386 GNUNET_SCHEDULER_add_now (&do_error, handle);
1390 //check client password
1392 == GNUNET_CONFIGURATION_get_value_string (cfg, "reclaim-rest-plugin",
1393 "psw", &expected_psw) )
1395 if (0 != strcmp (expected_psw, psw))
1397 GNUNET_free_non_null(user_psw);
1398 GNUNET_free(expected_psw);
1399 handle->emsg=GNUNET_strdup("invalid_client");
1400 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1401 GNUNET_SCHEDULER_add_now (&do_error, handle);
1404 GNUNET_free(expected_psw);
1408 GNUNET_free_non_null(user_psw);
1409 handle->emsg = GNUNET_strdup("server_error");
1410 handle->edesc = GNUNET_strdup ("gnunet configuration failed");
1411 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1412 GNUNET_SCHEDULER_add_now (&do_error, handle);
1417 for (handle->ego_entry = handle->ego_head; NULL != handle->ego_entry->next; )
1419 if ( 0 == strcmp(handle->ego_entry->keystring, client_id))
1421 client_exists = GNUNET_YES;
1424 handle->ego_entry = handle->ego_entry->next;
1426 if (GNUNET_NO == client_exists)
1428 GNUNET_free_non_null(user_psw);
1429 handle->emsg=GNUNET_strdup("invalid_client");
1430 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1431 GNUNET_SCHEDULER_add_now (&do_error, handle);
1439 //TODO Do not allow multiple equal parameter names
1440 //REQUIRED grant_type
1441 GNUNET_CRYPTO_hash (OIDC_GRANT_TYPE_KEY, strlen (OIDC_GRANT_TYPE_KEY), &cache_key);
1443 == GNUNET_CONTAINER_multihashmap_contains (
1444 handle->rest_handle->url_param_map, &cache_key) )
1446 GNUNET_free_non_null(user_psw);
1447 handle->emsg = GNUNET_strdup("invalid_request");
1448 handle->edesc = GNUNET_strdup("missing parameter grant_type");
1449 handle->response_code = MHD_HTTP_BAD_REQUEST;
1450 GNUNET_SCHEDULER_add_now (&do_error, handle);
1453 grant_type = GNUNET_CONTAINER_multihashmap_get (
1454 handle->rest_handle->url_param_map, &cache_key);
1457 GNUNET_CRYPTO_hash (OIDC_CODE_KEY, strlen (OIDC_CODE_KEY), &cache_key);
1459 == GNUNET_CONTAINER_multihashmap_contains (
1460 handle->rest_handle->url_param_map, &cache_key) )
1462 GNUNET_free_non_null(user_psw);
1463 handle->emsg = GNUNET_strdup("invalid_request");
1464 handle->edesc = GNUNET_strdup("missing parameter code");
1465 handle->response_code = MHD_HTTP_BAD_REQUEST;
1466 GNUNET_SCHEDULER_add_now (&do_error, handle);
1469 code = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map,
1472 //REQUIRED redirect_uri
1473 GNUNET_CRYPTO_hash (OIDC_REDIRECT_URI_KEY, strlen (OIDC_REDIRECT_URI_KEY),
1476 == GNUNET_CONTAINER_multihashmap_contains (
1477 handle->rest_handle->url_param_map, &cache_key) )
1479 GNUNET_free_non_null(user_psw);
1480 handle->emsg = GNUNET_strdup("invalid_request");
1481 handle->edesc = GNUNET_strdup("missing parameter redirect_uri");
1482 handle->response_code = MHD_HTTP_BAD_REQUEST;
1483 GNUNET_SCHEDULER_add_now (&do_error, handle);
1487 //Check parameter grant_type == "authorization_code"
1488 if (0 != strcmp(OIDC_GRANT_TYPE_VALUE, grant_type))
1490 GNUNET_free_non_null(user_psw);
1491 handle->emsg=GNUNET_strdup("unsupported_grant_type");
1492 handle->response_code = MHD_HTTP_BAD_REQUEST;
1493 GNUNET_SCHEDULER_add_now (&do_error, handle);
1496 GNUNET_CRYPTO_hash (code, strlen (code), &cache_key);
1499 == GNUNET_CONTAINER_multihashmap_put (OIDC_ticket_once,
1502 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY) )
1504 GNUNET_free_non_null(user_psw);
1505 handle->emsg = GNUNET_strdup("invalid_request");
1506 handle->edesc = GNUNET_strdup("Cannot use the same code more than once");
1507 handle->response_code = MHD_HTTP_BAD_REQUEST;
1508 GNUNET_SCHEDULER_add_now (&do_error, handle);
1513 GNUNET_STRINGS_base64_decode(code,strlen(code), (void**)&code_output);
1514 root = json_loads (code_output, 0, &error);
1515 GNUNET_free(code_output);
1516 ticket_string = json_object_get (root, "ticket");
1517 nonce = json_object_get (root, "nonce");
1518 max_age = json_object_get (root, "max_age");
1520 if(ticket_string == NULL && !json_is_string(ticket_string))
1522 GNUNET_free_non_null(user_psw);
1523 handle->emsg = GNUNET_strdup("invalid_request");
1524 handle->edesc = GNUNET_strdup("invalid code");
1525 handle->response_code = MHD_HTTP_BAD_REQUEST;
1526 GNUNET_SCHEDULER_add_now (&do_error, handle);
1530 struct GNUNET_RECLAIM_Ticket *ticket = GNUNET_new(struct GNUNET_RECLAIM_Ticket);
1532 != GNUNET_STRINGS_string_to_data (json_string_value(ticket_string),
1533 strlen (json_string_value(ticket_string)),
1535 sizeof(struct GNUNET_RECLAIM_Ticket)))
1537 GNUNET_free_non_null(user_psw);
1538 handle->emsg = GNUNET_strdup("invalid_request");
1539 handle->edesc = GNUNET_strdup("invalid code");
1540 handle->response_code = MHD_HTTP_BAD_REQUEST;
1541 GNUNET_SCHEDULER_add_now (&do_error, handle);
1542 GNUNET_free(ticket);
1545 // this is the current client (relying party)
1546 struct GNUNET_CRYPTO_EcdsaPublicKey pub_key;
1547 GNUNET_IDENTITY_ego_get_public_key(handle->ego_entry->ego,&pub_key);
1548 if (0 != memcmp(&pub_key,&ticket->audience,sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey)))
1550 GNUNET_free_non_null(user_psw);
1551 handle->emsg = GNUNET_strdup("invalid_request");
1552 handle->edesc = GNUNET_strdup("invalid code");
1553 handle->response_code = MHD_HTTP_BAD_REQUEST;
1554 GNUNET_SCHEDULER_add_now (&do_error, handle);
1555 GNUNET_free(ticket);
1560 unsigned long long int expiration_time;
1562 != GNUNET_CONFIGURATION_get_value_number(cfg, "reclaim-rest-plugin",
1563 "expiration_time", &expiration_time) )
1565 GNUNET_free_non_null(user_psw);
1566 handle->emsg = GNUNET_strdup("server_error");
1567 handle->edesc = GNUNET_strdup ("gnunet configuration failed");
1568 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1569 GNUNET_SCHEDULER_add_now (&do_error, handle);
1570 GNUNET_free(ticket);
1574 struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *cl = GNUNET_new (struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList);
1575 //aud REQUIRED public key client_id must be there
1576 GNUNET_RECLAIM_ATTRIBUTE_list_add(cl,
1578 GNUNET_RECLAIM_ATTRIBUTE_TYPE_STRING,
1581 //exp REQUIRED time expired from config
1582 struct GNUNET_TIME_Absolute exp_time = GNUNET_TIME_relative_to_absolute (
1583 GNUNET_TIME_relative_multiply (GNUNET_TIME_relative_get_second_ (),
1585 const char* exp_time_string = GNUNET_STRINGS_absolute_time_to_string(exp_time);
1586 GNUNET_RECLAIM_ATTRIBUTE_list_add (cl,
1588 GNUNET_RECLAIM_ATTRIBUTE_TYPE_STRING,
1590 strlen(exp_time_string));
1591 //iat REQUIRED time now
1592 struct GNUNET_TIME_Absolute time_now = GNUNET_TIME_absolute_get();
1593 const char* time_now_string = GNUNET_STRINGS_absolute_time_to_string(time_now);
1594 GNUNET_RECLAIM_ATTRIBUTE_list_add (cl,
1596 GNUNET_RECLAIM_ATTRIBUTE_TYPE_STRING,
1598 strlen(time_now_string));
1599 //nonce only if nonce is provided
1600 if ( NULL != nonce && json_is_string(nonce) )
1602 GNUNET_RECLAIM_ATTRIBUTE_list_add (cl,
1604 GNUNET_RECLAIM_ATTRIBUTE_TYPE_STRING,
1605 json_string_value(nonce),
1606 strlen(json_string_value(nonce)));
1608 //auth_time only if max_age is provided
1609 if ( NULL != max_age && json_is_string(max_age) )
1611 GNUNET_RECLAIM_ATTRIBUTE_list_add (cl,
1613 GNUNET_RECLAIM_ATTRIBUTE_TYPE_STRING,
1614 json_string_value(max_age),
1615 strlen(json_string_value(max_age)));
1617 //TODO OPTIONAL acr,amr,azp
1619 struct EgoEntry *ego_entry;
1620 for (ego_entry = handle->ego_head; NULL != ego_entry; ego_entry = ego_entry->next)
1622 GNUNET_IDENTITY_ego_get_public_key (ego_entry->ego, &pub_key);
1623 if (0 == memcmp (&pub_key, &ticket->audience, sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey)))
1628 if ( NULL == ego_entry )
1630 GNUNET_free_non_null(user_psw);
1631 handle->emsg = GNUNET_strdup("invalid_request");
1632 handle->edesc = GNUNET_strdup("invalid code...");
1633 handle->response_code = MHD_HTTP_BAD_REQUEST;
1634 GNUNET_SCHEDULER_add_now (&do_error, handle);
1635 GNUNET_free(ticket);
1639 != GNUNET_CONFIGURATION_get_value_string (cfg, "reclaim-rest-plugin",
1640 "jwt_secret", &jwt_secret) )
1642 GNUNET_free_non_null(user_psw);
1643 handle->emsg = GNUNET_strdup("invalid_request");
1644 handle->edesc = GNUNET_strdup("No signing secret configured!");
1645 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1646 GNUNET_SCHEDULER_add_now (&do_error, handle);
1647 GNUNET_free(ticket);
1650 struct GNUNET_CRYPTO_EcdsaPublicKey pk;
1651 GNUNET_IDENTITY_ego_get_public_key (ego_entry->ego, &pk);
1652 char *id_token = jwt_create_from_list(&ticket->audience,
1657 //Create random access_token
1658 char* access_token_number;
1660 uint64_t random_number;
1661 random_number = GNUNET_CRYPTO_random_u64(GNUNET_CRYPTO_QUALITY_NONCE, UINT64_MAX);
1662 GNUNET_asprintf(&access_token_number, "%" PRIu64, random_number);
1663 GNUNET_STRINGS_base64_encode(access_token_number,strlen(access_token_number),&access_token);
1667 //TODO OPTIONAL add refresh_token and scope
1668 GNUNET_asprintf (&json_response,
1669 "{ \"access_token\" : \"%s\", "
1670 "\"token_type\" : \"Bearer\", "
1671 "\"expires_in\" : %d, "
1672 "\"id_token\" : \"%s\"}",
1676 GNUNET_CRYPTO_hash(access_token, strlen(access_token), &cache_key);
1677 char *id_ticket_combination;
1678 GNUNET_asprintf(&id_ticket_combination,
1681 json_string_value(ticket_string));
1682 GNUNET_CONTAINER_multihashmap_put(OIDC_interpret_access_token,
1684 id_ticket_combination,
1685 GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
1687 resp = GNUNET_REST_create_response (json_response);
1688 MHD_add_response_header (resp, "Cache-Control", "no-store");
1689 MHD_add_response_header (resp, "Pragma", "no-cache");
1690 MHD_add_response_header (resp, "Content-Type", "application/json");
1691 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
1693 GNUNET_RECLAIM_ATTRIBUTE_list_destroy(cl);
1694 GNUNET_free(access_token_number);
1695 GNUNET_free(access_token);
1696 GNUNET_free(user_psw);
1697 GNUNET_free(json_response);
1698 GNUNET_free(ticket);
1699 GNUNET_free(id_token);
1701 GNUNET_SCHEDULER_add_now(&cleanup_handle_delayed, handle);
1705 * Collects claims and stores them in handle
1708 consume_ticket (void *cls,
1709 const struct GNUNET_CRYPTO_EcdsaPublicKey *identity,
1710 const struct GNUNET_RECLAIM_ATTRIBUTE_Claim *attr)
1712 struct RequestHandle *handle = cls;
1716 if (NULL == identity)
1718 GNUNET_SCHEDULER_add_now (&return_userinfo_response, handle);
1722 tmp_value = GNUNET_RECLAIM_ATTRIBUTE_value_to_string (attr->type,
1726 value = json_string (tmp_value);
1729 json_object_set_new (handle->oidc->response,
1732 GNUNET_free (tmp_value);
1736 * Responds to userinfo GET and url-encoded POST request
1738 * @param con_handle the connection handle
1739 * @param url the url
1740 * @param cls the RequestHandle
1743 userinfo_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
1744 const char* url, void *cls)
1746 //TODO expiration time
1747 struct RequestHandle *handle = cls;
1748 char delimiter[] = " ";
1749 char delimiter_db[] = ";";
1750 struct GNUNET_HashCode cache_key;
1751 char *authorization, *authorization_type, *authorization_access_token;
1752 char *client_ticket, *client, *ticket_str;
1753 struct GNUNET_RECLAIM_Ticket *ticket;
1755 GNUNET_CRYPTO_hash (OIDC_AUTHORIZATION_HEADER_KEY,
1756 strlen (OIDC_AUTHORIZATION_HEADER_KEY),
1759 == GNUNET_CONTAINER_multihashmap_contains (
1760 handle->rest_handle->header_param_map, &cache_key) )
1762 handle->emsg = GNUNET_strdup("invalid_token");
1763 handle->edesc = GNUNET_strdup("No Access Token");
1764 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1765 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
1768 authorization = GNUNET_CONTAINER_multihashmap_get (
1769 handle->rest_handle->header_param_map, &cache_key);
1771 //split header in "Bearer" and access_token
1772 authorization = GNUNET_strdup(authorization);
1773 authorization_type = strtok (authorization, delimiter);
1774 if ( 0 != strcmp ("Bearer", authorization_type) )
1776 handle->emsg = GNUNET_strdup("invalid_token");
1777 handle->edesc = GNUNET_strdup("No Access Token");
1778 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1779 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
1780 GNUNET_free(authorization);
1783 authorization_access_token = strtok (NULL, delimiter);
1784 if ( NULL == authorization_access_token )
1786 handle->emsg = GNUNET_strdup("invalid_token");
1787 handle->edesc = GNUNET_strdup("No Access Token");
1788 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1789 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
1790 GNUNET_free(authorization);
1794 GNUNET_CRYPTO_hash (authorization_access_token,
1795 strlen (authorization_access_token),
1797 if ( GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (OIDC_interpret_access_token,
1800 handle->emsg = GNUNET_strdup("invalid_token");
1801 handle->edesc = GNUNET_strdup("The Access Token expired");
1802 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1803 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
1804 GNUNET_free(authorization);
1808 client_ticket = GNUNET_CONTAINER_multihashmap_get(OIDC_interpret_access_token,
1810 client_ticket = GNUNET_strdup(client_ticket);
1811 client = strtok(client_ticket,delimiter_db);
1814 handle->emsg = GNUNET_strdup("invalid_token");
1815 handle->edesc = GNUNET_strdup("The Access Token expired");
1816 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1817 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
1818 GNUNET_free(authorization);
1819 GNUNET_free(client_ticket);
1822 handle->ego_entry = handle->ego_head;
1823 for(; NULL != handle->ego_entry; handle->ego_entry = handle->ego_entry->next)
1825 if (0 == strcmp(handle->ego_entry->keystring,client))
1830 if (NULL == handle->ego_entry)
1832 handle->emsg = GNUNET_strdup("invalid_token");
1833 handle->edesc = GNUNET_strdup("The Access Token expired");
1834 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1835 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
1836 GNUNET_free(authorization);
1837 GNUNET_free(client_ticket);
1840 ticket_str = strtok(NULL, delimiter_db);
1841 if (NULL == ticket_str)
1843 handle->emsg = GNUNET_strdup("invalid_token");
1844 handle->edesc = GNUNET_strdup("The Access Token expired");
1845 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1846 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
1847 GNUNET_free(authorization);
1848 GNUNET_free(client_ticket);
1851 ticket = GNUNET_new(struct GNUNET_RECLAIM_Ticket);
1853 != GNUNET_STRINGS_string_to_data (ticket_str,
1854 strlen (ticket_str),
1856 sizeof(struct GNUNET_RECLAIM_Ticket)))
1858 handle->emsg = GNUNET_strdup("invalid_token");
1859 handle->edesc = GNUNET_strdup("The Access Token expired");
1860 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1861 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
1862 GNUNET_free(ticket);
1863 GNUNET_free(authorization);
1864 GNUNET_free(client_ticket);
1868 handle->idp = GNUNET_RECLAIM_connect (cfg);
1869 handle->oidc->response = json_object();
1870 json_object_set_new( handle->oidc->response, "sub", json_string( handle->ego_entry->keystring));
1871 handle->idp_op = GNUNET_RECLAIM_ticket_consume (
1873 GNUNET_IDENTITY_ego_get_private_key (handle->ego_entry->ego),
1877 GNUNET_free(ticket);
1878 GNUNET_free(authorization);
1879 GNUNET_free(client_ticket);
1885 * Handle rest request
1887 * @param handle the request handle
1890 init_cont (struct RequestHandle *handle)
1892 struct GNUNET_REST_RequestHandlerError err;
1893 static const struct GNUNET_REST_RequestHandler handlers[] = {
1894 {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_AUTHORIZE, &authorize_endpoint},
1895 {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_AUTHORIZE, &authorize_endpoint}, //url-encoded
1896 {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_LOGIN, &login_cont},
1897 {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_TOKEN, &token_endpoint },
1898 {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_USERINFO, &userinfo_endpoint },
1899 {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_USERINFO, &userinfo_endpoint },
1900 {MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_OIDC,
1902 GNUNET_REST_HANDLER_END
1905 if (GNUNET_NO == GNUNET_REST_handle_request (handle->rest_handle,
1910 handle->response_code = err.error_code;
1911 GNUNET_SCHEDULER_add_now (&do_error, handle);
1916 * If listing is enabled, prints information about the egos.
1918 * This function is initially called for all egos and then again
1919 * whenever a ego's identifier changes or if it is deleted. At the
1920 * end of the initial pass over all egos, the function is once called
1921 * with 'NULL' for 'ego'. That does NOT mean that the callback won't
1922 * be invoked in the future or that there was an error.
1924 * When used with 'GNUNET_IDENTITY_create' or 'GNUNET_IDENTITY_get',
1925 * this function is only called ONCE, and 'NULL' being passed in
1926 * 'ego' does indicate an error (i.e. name is taken or no default
1927 * value is known). If 'ego' is non-NULL and if '*ctx'
1928 * is set in those callbacks, the value WILL be passed to a subsequent
1929 * call to the identity callback of 'GNUNET_IDENTITY_connect' (if
1930 * that one was not NULL).
1932 * When an identity is renamed, this function is called with the
1933 * (known) ego but the NEW identifier.
1935 * When an identity is deleted, this function is called with the
1936 * (known) ego and "NULL" for the 'identifier'. In this case,
1937 * the 'ego' is henceforth invalid (and the 'ctx' should also be
1940 * @param cls closure
1941 * @param ego ego handle
1942 * @param ctx context for application to store data for this ego
1943 * (during the lifetime of this process, initially NULL)
1944 * @param identifier identifier assigned by the user for this ego,
1945 * NULL if the user just deleted the ego and it
1946 * must thus no longer be used
1949 list_ego (void *cls,
1950 struct GNUNET_IDENTITY_Ego *ego,
1952 const char *identifier)
1954 struct RequestHandle *handle = cls;
1955 struct EgoEntry *ego_entry;
1956 struct GNUNET_CRYPTO_EcdsaPublicKey pk;
1958 if ((NULL == ego) && (ID_REST_STATE_INIT == handle->state))
1960 handle->state = ID_REST_STATE_POST_INIT;
1964 if (ID_REST_STATE_INIT == handle->state) {
1965 ego_entry = GNUNET_new (struct EgoEntry);
1966 GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
1967 ego_entry->keystring =
1968 GNUNET_CRYPTO_ecdsa_public_key_to_string (&pk);
1969 ego_entry->ego = ego;
1970 ego_entry->identifier = GNUNET_strdup (identifier);
1971 GNUNET_CONTAINER_DLL_insert_tail(handle->ego_head,handle->ego_tail, ego_entry);
1974 /* Ego renamed or added */
1975 if (identifier != NULL) {
1976 for (ego_entry = handle->ego_head; NULL != ego_entry; ego_entry = ego_entry->next) {
1977 if (ego_entry->ego == ego) {
1979 GNUNET_free (ego_entry->identifier);
1980 ego_entry->identifier = GNUNET_strdup (identifier);
1984 if (NULL == ego_entry) {
1986 ego_entry = GNUNET_new (struct EgoEntry);
1987 GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
1988 ego_entry->keystring =
1989 GNUNET_CRYPTO_ecdsa_public_key_to_string (&pk);
1990 ego_entry->ego = ego;
1991 ego_entry->identifier = GNUNET_strdup (identifier);
1992 GNUNET_CONTAINER_DLL_insert_tail(handle->ego_head,handle->ego_tail, ego_entry);
1996 for (ego_entry = handle->ego_head; NULL != ego_entry; ego_entry = ego_entry->next) {
1997 if (ego_entry->ego == ego)
2000 if (NULL != ego_entry)
2001 GNUNET_CONTAINER_DLL_remove(handle->ego_head,handle->ego_tail, ego_entry);
2007 rest_identity_process_request(struct GNUNET_REST_RequestHandle *rest_handle,
2008 GNUNET_REST_ResultProcessor proc,
2011 struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
2012 handle->oidc = GNUNET_new (struct OIDC_Variables);
2013 if ( NULL == OIDC_identity_login_time )
2014 OIDC_identity_login_time = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
2015 if ( NULL == OIDC_identity_grants )
2016 OIDC_identity_grants = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
2017 if ( NULL == OIDC_ticket_once )
2018 OIDC_ticket_once = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
2019 if ( NULL == OIDC_interpret_access_token )
2020 OIDC_interpret_access_token = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
2021 handle->response_code = 0;
2022 handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
2023 handle->proc_cls = proc_cls;
2024 handle->proc = proc;
2025 handle->state = ID_REST_STATE_INIT;
2026 handle->rest_handle = rest_handle;
2028 handle->url = GNUNET_strdup (rest_handle->url);
2029 if (handle->url[strlen (handle->url)-1] == '/')
2030 handle->url[strlen (handle->url)-1] = '\0';
2031 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2033 handle->identity_handle = GNUNET_IDENTITY_connect (cfg,
2036 handle->namestore_handle = GNUNET_NAMESTORE_connect (cfg);
2037 handle->timeout_task =
2038 GNUNET_SCHEDULER_add_delayed (handle->timeout,
2041 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2046 * Entry point for the plugin.
2048 * @param cls Config info
2049 * @return NULL on error, otherwise the plugin context
2052 libgnunet_plugin_rest_openid_connect_init (void *cls)
2054 static struct Plugin plugin;
2055 struct GNUNET_REST_Plugin *api;
2058 if (NULL != plugin.cfg)
2059 return NULL; /* can only initialize once! */
2060 memset (&plugin, 0, sizeof (struct Plugin));
2062 api = GNUNET_new (struct GNUNET_REST_Plugin);
2064 api->name = GNUNET_REST_API_NS_OIDC;
2065 api->process_request = &rest_identity_process_request;
2066 GNUNET_asprintf (&allow_methods,
2067 "%s, %s, %s, %s, %s",
2068 MHD_HTTP_METHOD_GET,
2069 MHD_HTTP_METHOD_POST,
2070 MHD_HTTP_METHOD_PUT,
2071 MHD_HTTP_METHOD_DELETE,
2072 MHD_HTTP_METHOD_OPTIONS);
2074 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2075 _("Identity Provider REST API initialized\n"));
2081 * Exit point from the plugin.
2083 * @param cls the plugin context (as returned by "init")
2084 * @return always NULL
2087 libgnunet_plugin_rest_openid_connect_done (void *cls)
2089 struct GNUNET_REST_Plugin *api = cls;
2090 struct Plugin *plugin = api->cls;
2093 struct GNUNET_CONTAINER_MultiHashMapIterator *hashmap_it;
2095 hashmap_it = GNUNET_CONTAINER_multihashmap_iterator_create (
2096 OIDC_identity_login_time);
2097 while (GNUNET_YES ==
2098 GNUNET_CONTAINER_multihashmap_iterator_next (hashmap_it, NULL, value))
2103 GNUNET_CONTAINER_multihashmap_destroy(OIDC_identity_login_time);
2104 hashmap_it = GNUNET_CONTAINER_multihashmap_iterator_create (OIDC_identity_grants);
2105 while (GNUNET_YES ==
2106 GNUNET_CONTAINER_multihashmap_iterator_next (hashmap_it, NULL, value))
2111 GNUNET_CONTAINER_multihashmap_destroy(OIDC_identity_grants);
2112 hashmap_it = GNUNET_CONTAINER_multihashmap_iterator_create (OIDC_ticket_once);
2113 while (GNUNET_YES ==
2114 GNUNET_CONTAINER_multihashmap_iterator_next (hashmap_it, NULL, value))
2119 GNUNET_CONTAINER_multihashmap_destroy(OIDC_ticket_once);
2120 hashmap_it = GNUNET_CONTAINER_multihashmap_iterator_create (OIDC_interpret_access_token);
2121 while (GNUNET_YES ==
2122 GNUNET_CONTAINER_multihashmap_iterator_next (hashmap_it, NULL, value))
2127 GNUNET_CONTAINER_multihashmap_destroy(OIDC_interpret_access_token);
2128 GNUNET_CONTAINER_multihashmap_iterator_destroy(hashmap_it);
2129 GNUNET_free_non_null (allow_methods);
2131 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2132 "Identity Provider REST plugin is finished\n");
2136 /* end of plugin_rest_identity_provider.c */