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;
1169 cookie_identity_interpretation(handle);
1171 //RECOMMENDED value: state - REQUIRED for answers
1172 GNUNET_CRYPTO_hash (OIDC_STATE_KEY, strlen (OIDC_STATE_KEY), &cache_key);
1173 if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1176 handle->oidc->state = GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
1178 handle->oidc->state = GNUNET_strdup (handle->oidc->state);
1181 // REQUIRED value: client_id
1182 GNUNET_CRYPTO_hash (OIDC_CLIENT_ID_KEY, strlen (OIDC_CLIENT_ID_KEY),
1184 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1187 handle->emsg=GNUNET_strdup("invalid_request");
1188 handle->edesc=GNUNET_strdup("missing parameter client_id");
1189 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1190 GNUNET_SCHEDULER_add_now (&do_error, handle);
1193 handle->oidc->client_id = GNUNET_strdup (GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
1197 != GNUNET_CRYPTO_ecdsa_public_key_from_string (handle->oidc->client_id,
1198 strlen (handle->oidc->client_id),
1199 &handle->oidc->client_pkey) )
1201 handle->emsg = GNUNET_strdup("unauthorized_client");
1202 handle->edesc = GNUNET_strdup("The client is not authorized to request an "
1203 "authorization code using this method.");
1204 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1205 GNUNET_SCHEDULER_add_now (&do_error, handle);
1210 if ( NULL == handle->ego_head )
1212 handle->emsg = GNUNET_strdup("server_error");
1213 handle->edesc = GNUNET_strdup ("Egos are missing");
1214 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1215 GNUNET_SCHEDULER_add_now (&do_error, handle);
1219 handle->ego_entry = handle->ego_head;
1220 handle->priv_key = *GNUNET_IDENTITY_ego_get_private_key (handle->ego_head->ego);
1222 GNUNET_SCHEDULER_add_now (&build_authz_response, handle);
1226 * Combines an identity with a login time and responds OK to login request
1228 * @param con_handle the connection handle
1229 * @param url the url
1230 * @param cls the RequestHandle
1233 login_cont (struct GNUNET_REST_RequestHandle *con_handle,
1237 struct MHD_Response *resp = GNUNET_REST_create_response ("");
1238 struct RequestHandle *handle = cls;
1239 struct GNUNET_HashCode cache_key;
1240 struct GNUNET_TIME_Absolute *current_time;
1241 struct GNUNET_TIME_Absolute *last_time;
1246 char term_data[handle->rest_handle->data_size+1];
1247 term_data[handle->rest_handle->data_size] = '\0';
1248 GNUNET_memcpy (term_data, handle->rest_handle->data, handle->rest_handle->data_size);
1249 root = json_loads (term_data, JSON_DECODE_ANY, &error);
1250 identity = json_object_get (root, "identity");
1251 if ( json_is_string(identity) )
1253 GNUNET_asprintf (&cookie, "Identity=%s", json_string_value (identity));
1254 MHD_add_response_header (resp, "Set-Cookie", cookie);
1255 MHD_add_response_header (resp, "Access-Control-Allow-Methods", "POST");
1256 GNUNET_CRYPTO_hash (cookie, strlen (cookie), &cache_key);
1258 current_time = GNUNET_new(struct GNUNET_TIME_Absolute);
1259 *current_time = GNUNET_TIME_relative_to_absolute (
1260 GNUNET_TIME_relative_multiply (GNUNET_TIME_relative_get_second_ (),
1262 last_time = GNUNET_CONTAINER_multihashmap_get(OIDC_identity_login_time, &cache_key);
1263 if (NULL != last_time)
1265 GNUNET_free(last_time);
1267 GNUNET_CONTAINER_multihashmap_put (
1268 OIDC_identity_login_time, &cache_key, current_time,
1269 GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
1271 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
1272 GNUNET_free(cookie);
1276 handle->proc (handle->proc_cls, resp, MHD_HTTP_BAD_REQUEST);
1279 GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
1284 * Responds to token url-encoded POST request
1286 * @param con_handle the connection handle
1287 * @param url the url
1288 * @param cls the RequestHandle
1291 token_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
1295 //TODO static strings
1296 struct RequestHandle *handle = cls;
1297 struct GNUNET_HashCode cache_key;
1298 char *authorization, *credentials;
1299 char delimiter[]=" ";
1300 char delimiter_user_psw[]=":";
1301 char *grant_type, *code;
1302 char *user_psw = NULL, *client_id, *psw;
1304 int client_exists = GNUNET_NO;
1305 struct MHD_Response *resp;
1307 json_t *root, *ticket_string, *nonce, *max_age;
1309 char *json_response;
1313 * Check Authorization
1315 GNUNET_CRYPTO_hash (OIDC_AUTHORIZATION_HEADER_KEY,
1316 strlen (OIDC_AUTHORIZATION_HEADER_KEY),
1318 if ( GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->header_param_map,
1321 handle->emsg=GNUNET_strdup("invalid_client");
1322 handle->edesc=GNUNET_strdup("missing authorization");
1323 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1324 GNUNET_SCHEDULER_add_now (&do_error, handle);
1327 authorization = GNUNET_CONTAINER_multihashmap_get ( handle->rest_handle->header_param_map, &cache_key);
1329 //split header in "Basic" and [content]
1330 credentials = strtok (authorization, delimiter);
1331 if (0 != strcmp ("Basic",credentials))
1333 handle->emsg=GNUNET_strdup("invalid_client");
1334 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1335 GNUNET_SCHEDULER_add_now (&do_error, handle);
1338 credentials = strtok(NULL, delimiter);
1339 if (NULL == credentials)
1341 handle->emsg=GNUNET_strdup("invalid_client");
1342 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1343 GNUNET_SCHEDULER_add_now (&do_error, handle);
1346 GNUNET_STRINGS_base64_decode (credentials, strlen (credentials), (void**)&user_psw);
1348 if ( NULL == user_psw )
1350 handle->emsg=GNUNET_strdup("invalid_client");
1351 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1352 GNUNET_SCHEDULER_add_now (&do_error, handle);
1355 client_id = strtok (user_psw, delimiter_user_psw);
1356 if ( NULL == client_id )
1358 GNUNET_free_non_null(user_psw);
1359 handle->emsg=GNUNET_strdup("invalid_client");
1360 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1361 GNUNET_SCHEDULER_add_now (&do_error, handle);
1364 psw = strtok (NULL, delimiter_user_psw);
1367 GNUNET_free_non_null(user_psw);
1368 handle->emsg=GNUNET_strdup("invalid_client");
1369 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1370 GNUNET_SCHEDULER_add_now (&do_error, handle);
1374 //check client password
1376 == GNUNET_CONFIGURATION_get_value_string (cfg, "reclaim-rest-plugin",
1377 "psw", &expected_psw) )
1379 if (0 != strcmp (expected_psw, psw))
1381 GNUNET_free_non_null(user_psw);
1382 GNUNET_free(expected_psw);
1383 handle->emsg=GNUNET_strdup("invalid_client");
1384 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1385 GNUNET_SCHEDULER_add_now (&do_error, handle);
1388 GNUNET_free(expected_psw);
1392 GNUNET_free_non_null(user_psw);
1393 handle->emsg = GNUNET_strdup("server_error");
1394 handle->edesc = GNUNET_strdup ("gnunet configuration failed");
1395 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1396 GNUNET_SCHEDULER_add_now (&do_error, handle);
1401 for (handle->ego_entry = handle->ego_head; NULL != handle->ego_entry->next; )
1403 if ( 0 == strcmp(handle->ego_entry->keystring, client_id))
1405 client_exists = GNUNET_YES;
1408 handle->ego_entry = handle->ego_entry->next;
1410 if (GNUNET_NO == client_exists)
1412 GNUNET_free_non_null(user_psw);
1413 handle->emsg=GNUNET_strdup("invalid_client");
1414 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1415 GNUNET_SCHEDULER_add_now (&do_error, handle);
1423 //TODO Do not allow multiple equal parameter names
1424 //REQUIRED grant_type
1425 GNUNET_CRYPTO_hash (OIDC_GRANT_TYPE_KEY, strlen (OIDC_GRANT_TYPE_KEY), &cache_key);
1427 == GNUNET_CONTAINER_multihashmap_contains (
1428 handle->rest_handle->url_param_map, &cache_key) )
1430 GNUNET_free_non_null(user_psw);
1431 handle->emsg = GNUNET_strdup("invalid_request");
1432 handle->edesc = GNUNET_strdup("missing parameter grant_type");
1433 handle->response_code = MHD_HTTP_BAD_REQUEST;
1434 GNUNET_SCHEDULER_add_now (&do_error, handle);
1437 grant_type = GNUNET_CONTAINER_multihashmap_get (
1438 handle->rest_handle->url_param_map, &cache_key);
1441 GNUNET_CRYPTO_hash (OIDC_CODE_KEY, strlen (OIDC_CODE_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 code");
1449 handle->response_code = MHD_HTTP_BAD_REQUEST;
1450 GNUNET_SCHEDULER_add_now (&do_error, handle);
1453 code = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map,
1456 //REQUIRED redirect_uri
1457 GNUNET_CRYPTO_hash (OIDC_REDIRECT_URI_KEY, strlen (OIDC_REDIRECT_URI_KEY),
1460 == GNUNET_CONTAINER_multihashmap_contains (
1461 handle->rest_handle->url_param_map, &cache_key) )
1463 GNUNET_free_non_null(user_psw);
1464 handle->emsg = GNUNET_strdup("invalid_request");
1465 handle->edesc = GNUNET_strdup("missing parameter redirect_uri");
1466 handle->response_code = MHD_HTTP_BAD_REQUEST;
1467 GNUNET_SCHEDULER_add_now (&do_error, handle);
1471 //Check parameter grant_type == "authorization_code"
1472 if (0 != strcmp(OIDC_GRANT_TYPE_VALUE, grant_type))
1474 GNUNET_free_non_null(user_psw);
1475 handle->emsg=GNUNET_strdup("unsupported_grant_type");
1476 handle->response_code = MHD_HTTP_BAD_REQUEST;
1477 GNUNET_SCHEDULER_add_now (&do_error, handle);
1480 GNUNET_CRYPTO_hash (code, strlen (code), &cache_key);
1483 == GNUNET_CONTAINER_multihashmap_put (OIDC_ticket_once,
1486 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY) )
1488 GNUNET_free_non_null(user_psw);
1489 handle->emsg = GNUNET_strdup("invalid_request");
1490 handle->edesc = GNUNET_strdup("Cannot use the same code more than once");
1491 handle->response_code = MHD_HTTP_BAD_REQUEST;
1492 GNUNET_SCHEDULER_add_now (&do_error, handle);
1497 GNUNET_STRINGS_base64_decode(code,strlen(code), (void**)&code_output);
1498 root = json_loads (code_output, 0, &error);
1499 GNUNET_free(code_output);
1500 ticket_string = json_object_get (root, "ticket");
1501 nonce = json_object_get (root, "nonce");
1502 max_age = json_object_get (root, "max_age");
1504 if(ticket_string == NULL && !json_is_string(ticket_string))
1506 GNUNET_free_non_null(user_psw);
1507 handle->emsg = GNUNET_strdup("invalid_request");
1508 handle->edesc = GNUNET_strdup("invalid code");
1509 handle->response_code = MHD_HTTP_BAD_REQUEST;
1510 GNUNET_SCHEDULER_add_now (&do_error, handle);
1514 struct GNUNET_RECLAIM_Ticket *ticket = GNUNET_new(struct GNUNET_RECLAIM_Ticket);
1516 != GNUNET_STRINGS_string_to_data (json_string_value(ticket_string),
1517 strlen (json_string_value(ticket_string)),
1519 sizeof(struct GNUNET_RECLAIM_Ticket)))
1521 GNUNET_free_non_null(user_psw);
1522 handle->emsg = GNUNET_strdup("invalid_request");
1523 handle->edesc = GNUNET_strdup("invalid code");
1524 handle->response_code = MHD_HTTP_BAD_REQUEST;
1525 GNUNET_SCHEDULER_add_now (&do_error, handle);
1526 GNUNET_free(ticket);
1529 // this is the current client (relying party)
1530 struct GNUNET_CRYPTO_EcdsaPublicKey pub_key;
1531 GNUNET_IDENTITY_ego_get_public_key(handle->ego_entry->ego,&pub_key);
1532 if (0 != memcmp(&pub_key,&ticket->audience,sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey)))
1534 GNUNET_free_non_null(user_psw);
1535 handle->emsg = GNUNET_strdup("invalid_request");
1536 handle->edesc = GNUNET_strdup("invalid code");
1537 handle->response_code = MHD_HTTP_BAD_REQUEST;
1538 GNUNET_SCHEDULER_add_now (&do_error, handle);
1539 GNUNET_free(ticket);
1544 unsigned long long int expiration_time;
1546 != GNUNET_CONFIGURATION_get_value_number(cfg, "reclaim-rest-plugin",
1547 "expiration_time", &expiration_time) )
1549 GNUNET_free_non_null(user_psw);
1550 handle->emsg = GNUNET_strdup("server_error");
1551 handle->edesc = GNUNET_strdup ("gnunet configuration failed");
1552 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1553 GNUNET_SCHEDULER_add_now (&do_error, handle);
1554 GNUNET_free(ticket);
1558 struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *cl = GNUNET_new (struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList);
1559 //aud REQUIRED public key client_id must be there
1560 GNUNET_RECLAIM_ATTRIBUTE_list_add(cl,
1562 GNUNET_RECLAIM_ATTRIBUTE_TYPE_STRING,
1565 //exp REQUIRED time expired from config
1566 struct GNUNET_TIME_Absolute exp_time = GNUNET_TIME_relative_to_absolute (
1567 GNUNET_TIME_relative_multiply (GNUNET_TIME_relative_get_second_ (),
1569 const char* exp_time_string = GNUNET_STRINGS_absolute_time_to_string(exp_time);
1570 GNUNET_RECLAIM_ATTRIBUTE_list_add (cl,
1572 GNUNET_RECLAIM_ATTRIBUTE_TYPE_STRING,
1574 strlen(exp_time_string));
1575 //iat REQUIRED time now
1576 struct GNUNET_TIME_Absolute time_now = GNUNET_TIME_absolute_get();
1577 const char* time_now_string = GNUNET_STRINGS_absolute_time_to_string(time_now);
1578 GNUNET_RECLAIM_ATTRIBUTE_list_add (cl,
1580 GNUNET_RECLAIM_ATTRIBUTE_TYPE_STRING,
1582 strlen(time_now_string));
1583 //nonce only if nonce is provided
1584 if ( NULL != nonce && json_is_string(nonce) )
1586 GNUNET_RECLAIM_ATTRIBUTE_list_add (cl,
1588 GNUNET_RECLAIM_ATTRIBUTE_TYPE_STRING,
1589 json_string_value(nonce),
1590 strlen(json_string_value(nonce)));
1592 //auth_time only if max_age is provided
1593 if ( NULL != max_age && json_is_string(max_age) )
1595 GNUNET_RECLAIM_ATTRIBUTE_list_add (cl,
1597 GNUNET_RECLAIM_ATTRIBUTE_TYPE_STRING,
1598 json_string_value(max_age),
1599 strlen(json_string_value(max_age)));
1601 //TODO OPTIONAL acr,amr,azp
1603 struct EgoEntry *ego_entry;
1604 for (ego_entry = handle->ego_head; NULL != ego_entry; ego_entry = ego_entry->next)
1606 GNUNET_IDENTITY_ego_get_public_key (ego_entry->ego, &pub_key);
1607 if (0 == memcmp (&pub_key, &ticket->audience, sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey)))
1612 if ( NULL == ego_entry )
1614 GNUNET_free_non_null(user_psw);
1615 handle->emsg = GNUNET_strdup("invalid_request");
1616 handle->edesc = GNUNET_strdup("invalid code...");
1617 handle->response_code = MHD_HTTP_BAD_REQUEST;
1618 GNUNET_SCHEDULER_add_now (&do_error, handle);
1619 GNUNET_free(ticket);
1623 != GNUNET_CONFIGURATION_get_value_string (cfg, "reclaim-rest-plugin",
1624 "jwt_secret", &jwt_secret) )
1626 GNUNET_free_non_null(user_psw);
1627 handle->emsg = GNUNET_strdup("invalid_request");
1628 handle->edesc = GNUNET_strdup("No signing secret configured!");
1629 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1630 GNUNET_SCHEDULER_add_now (&do_error, handle);
1631 GNUNET_free(ticket);
1634 struct GNUNET_CRYPTO_AuthKey jwt_sign_key;
1635 struct GNUNET_CRYPTO_EcdsaPublicKey pk;
1636 GNUNET_IDENTITY_ego_get_public_key (ego_entry->ego, &pk);
1637 GNUNET_CRYPTO_hash (jwt_secret, strlen (jwt_secret), (struct GNUNET_HashCode*)jwt_sign_key.key);
1638 char *id_token = jwt_create_from_list(&ticket->audience,
1643 //Create random access_token
1644 char* access_token_number;
1646 uint64_t random_number;
1647 random_number = GNUNET_CRYPTO_random_u64(GNUNET_CRYPTO_QUALITY_NONCE, UINT64_MAX);
1648 GNUNET_asprintf(&access_token_number, "%" PRIu64, random_number);
1649 GNUNET_STRINGS_base64_encode(access_token_number,strlen(access_token_number),&access_token);
1653 //TODO OPTIONAL add refresh_token and scope
1654 GNUNET_asprintf (&json_response,
1655 "{ \"access_token\" : \"%s\", "
1656 "\"token_type\" : \"Bearer\", "
1657 "\"expires_in\" : %d, "
1658 "\"id_token\" : \"%s\"}",
1662 GNUNET_CRYPTO_hash(access_token, strlen(access_token), &cache_key);
1663 char *id_ticket_combination;
1664 GNUNET_asprintf(&id_ticket_combination,
1667 json_string_value(ticket_string));
1668 GNUNET_CONTAINER_multihashmap_put(OIDC_interpret_access_token,
1670 id_ticket_combination,
1671 GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
1673 resp = GNUNET_REST_create_response (json_response);
1674 MHD_add_response_header (resp, "Cache-Control", "no-store");
1675 MHD_add_response_header (resp, "Pragma", "no-cache");
1676 MHD_add_response_header (resp, "Content-Type", "application/json");
1677 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
1679 GNUNET_RECLAIM_ATTRIBUTE_list_destroy(cl);
1680 GNUNET_free(access_token_number);
1681 GNUNET_free(access_token);
1682 GNUNET_free(user_psw);
1683 GNUNET_free(json_response);
1684 GNUNET_free(ticket);
1685 GNUNET_free(id_token);
1687 GNUNET_SCHEDULER_add_now(&cleanup_handle_delayed, handle);
1691 * Collects claims and stores them in handle
1694 consume_ticket (void *cls,
1695 const struct GNUNET_CRYPTO_EcdsaPublicKey *identity,
1696 const struct GNUNET_RECLAIM_ATTRIBUTE_Claim *attr)
1698 struct RequestHandle *handle = cls;
1702 if (NULL == identity)
1704 GNUNET_SCHEDULER_add_now (&return_userinfo_response, handle);
1708 tmp_value = GNUNET_RECLAIM_ATTRIBUTE_value_to_string (attr->type,
1712 value = json_string (tmp_value);
1715 json_object_set_new (handle->oidc->response,
1718 GNUNET_free (tmp_value);
1722 * Responds to userinfo GET and url-encoded POST request
1724 * @param con_handle the connection handle
1725 * @param url the url
1726 * @param cls the RequestHandle
1729 userinfo_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
1730 const char* url, void *cls)
1732 //TODO expiration time
1733 struct RequestHandle *handle = cls;
1734 char delimiter[] = " ";
1735 char delimiter_db[] = ";";
1736 struct GNUNET_HashCode cache_key;
1737 char *authorization, *authorization_type, *authorization_access_token;
1738 char *client_ticket, *client, *ticket_str;
1739 struct GNUNET_RECLAIM_Ticket *ticket;
1741 GNUNET_CRYPTO_hash (OIDC_AUTHORIZATION_HEADER_KEY,
1742 strlen (OIDC_AUTHORIZATION_HEADER_KEY),
1745 == GNUNET_CONTAINER_multihashmap_contains (
1746 handle->rest_handle->header_param_map, &cache_key) )
1748 handle->emsg = GNUNET_strdup("invalid_token");
1749 handle->edesc = GNUNET_strdup("No Access Token");
1750 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1751 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
1754 authorization = GNUNET_CONTAINER_multihashmap_get (
1755 handle->rest_handle->header_param_map, &cache_key);
1757 //split header in "Bearer" and access_token
1758 authorization = GNUNET_strdup(authorization);
1759 authorization_type = strtok (authorization, delimiter);
1760 if ( 0 != strcmp ("Bearer", authorization_type) )
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);
1766 GNUNET_free(authorization);
1769 authorization_access_token = strtok (NULL, delimiter);
1770 if ( NULL == authorization_access_token )
1772 handle->emsg = GNUNET_strdup("invalid_token");
1773 handle->edesc = GNUNET_strdup("No Access Token");
1774 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1775 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
1776 GNUNET_free(authorization);
1780 GNUNET_CRYPTO_hash (authorization_access_token,
1781 strlen (authorization_access_token),
1783 if ( GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (OIDC_interpret_access_token,
1786 handle->emsg = GNUNET_strdup("invalid_token");
1787 handle->edesc = GNUNET_strdup("The Access Token expired");
1788 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1789 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
1790 GNUNET_free(authorization);
1794 client_ticket = GNUNET_CONTAINER_multihashmap_get(OIDC_interpret_access_token,
1796 client_ticket = GNUNET_strdup(client_ticket);
1797 client = strtok(client_ticket,delimiter_db);
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);
1805 GNUNET_free(client_ticket);
1808 handle->ego_entry = handle->ego_head;
1809 for(; NULL != handle->ego_entry; handle->ego_entry = handle->ego_entry->next)
1811 if (0 == strcmp(handle->ego_entry->keystring,client))
1816 if (NULL == handle->ego_entry)
1818 handle->emsg = GNUNET_strdup("invalid_token");
1819 handle->edesc = GNUNET_strdup("The Access Token expired");
1820 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1821 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
1822 GNUNET_free(authorization);
1823 GNUNET_free(client_ticket);
1826 ticket_str = strtok(NULL, delimiter_db);
1827 if (NULL == ticket_str)
1829 handle->emsg = GNUNET_strdup("invalid_token");
1830 handle->edesc = GNUNET_strdup("The Access Token expired");
1831 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1832 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
1833 GNUNET_free(authorization);
1834 GNUNET_free(client_ticket);
1837 ticket = GNUNET_new(struct GNUNET_RECLAIM_Ticket);
1839 != GNUNET_STRINGS_string_to_data (ticket_str,
1840 strlen (ticket_str),
1842 sizeof(struct GNUNET_RECLAIM_Ticket)))
1844 handle->emsg = GNUNET_strdup("invalid_token");
1845 handle->edesc = GNUNET_strdup("The Access Token expired");
1846 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1847 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
1848 GNUNET_free(ticket);
1849 GNUNET_free(authorization);
1850 GNUNET_free(client_ticket);
1854 handle->idp = GNUNET_RECLAIM_connect (cfg);
1855 handle->oidc->response = json_object();
1856 json_object_set_new( handle->oidc->response, "sub", json_string( handle->ego_entry->keystring));
1857 handle->idp_op = GNUNET_RECLAIM_ticket_consume (
1859 GNUNET_IDENTITY_ego_get_private_key (handle->ego_entry->ego),
1863 GNUNET_free(ticket);
1864 GNUNET_free(authorization);
1865 GNUNET_free(client_ticket);
1871 * Handle rest request
1873 * @param handle the request handle
1876 init_cont (struct RequestHandle *handle)
1878 struct GNUNET_REST_RequestHandlerError err;
1879 static const struct GNUNET_REST_RequestHandler handlers[] = {
1880 {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_AUTHORIZE, &authorize_endpoint},
1881 {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_AUTHORIZE, &authorize_endpoint}, //url-encoded
1882 {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_LOGIN, &login_cont},
1883 {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_TOKEN, &token_endpoint },
1884 {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_USERINFO, &userinfo_endpoint },
1885 {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_USERINFO, &userinfo_endpoint },
1886 {MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_OIDC,
1888 GNUNET_REST_HANDLER_END
1891 if (GNUNET_NO == GNUNET_REST_handle_request (handle->rest_handle,
1896 handle->response_code = err.error_code;
1897 GNUNET_SCHEDULER_add_now (&do_error, handle);
1902 * If listing is enabled, prints information about the egos.
1904 * This function is initially called for all egos and then again
1905 * whenever a ego's identifier changes or if it is deleted. At the
1906 * end of the initial pass over all egos, the function is once called
1907 * with 'NULL' for 'ego'. That does NOT mean that the callback won't
1908 * be invoked in the future or that there was an error.
1910 * When used with 'GNUNET_IDENTITY_create' or 'GNUNET_IDENTITY_get',
1911 * this function is only called ONCE, and 'NULL' being passed in
1912 * 'ego' does indicate an error (i.e. name is taken or no default
1913 * value is known). If 'ego' is non-NULL and if '*ctx'
1914 * is set in those callbacks, the value WILL be passed to a subsequent
1915 * call to the identity callback of 'GNUNET_IDENTITY_connect' (if
1916 * that one was not NULL).
1918 * When an identity is renamed, this function is called with the
1919 * (known) ego but the NEW identifier.
1921 * When an identity is deleted, this function is called with the
1922 * (known) ego and "NULL" for the 'identifier'. In this case,
1923 * the 'ego' is henceforth invalid (and the 'ctx' should also be
1926 * @param cls closure
1927 * @param ego ego handle
1928 * @param ctx context for application to store data for this ego
1929 * (during the lifetime of this process, initially NULL)
1930 * @param identifier identifier assigned by the user for this ego,
1931 * NULL if the user just deleted the ego and it
1932 * must thus no longer be used
1935 list_ego (void *cls,
1936 struct GNUNET_IDENTITY_Ego *ego,
1938 const char *identifier)
1940 struct RequestHandle *handle = cls;
1941 struct EgoEntry *ego_entry;
1942 struct GNUNET_CRYPTO_EcdsaPublicKey pk;
1944 if ((NULL == ego) && (ID_REST_STATE_INIT == handle->state))
1946 handle->state = ID_REST_STATE_POST_INIT;
1950 if (ID_REST_STATE_INIT == handle->state) {
1951 ego_entry = GNUNET_new (struct EgoEntry);
1952 GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
1953 ego_entry->keystring =
1954 GNUNET_CRYPTO_ecdsa_public_key_to_string (&pk);
1955 ego_entry->ego = ego;
1956 ego_entry->identifier = GNUNET_strdup (identifier);
1957 GNUNET_CONTAINER_DLL_insert_tail(handle->ego_head,handle->ego_tail, ego_entry);
1960 /* Ego renamed or added */
1961 if (identifier != NULL) {
1962 for (ego_entry = handle->ego_head; NULL != ego_entry; ego_entry = ego_entry->next) {
1963 if (ego_entry->ego == ego) {
1965 GNUNET_free (ego_entry->identifier);
1966 ego_entry->identifier = GNUNET_strdup (identifier);
1970 if (NULL == ego_entry) {
1972 ego_entry = GNUNET_new (struct EgoEntry);
1973 GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
1974 ego_entry->keystring =
1975 GNUNET_CRYPTO_ecdsa_public_key_to_string (&pk);
1976 ego_entry->ego = ego;
1977 ego_entry->identifier = GNUNET_strdup (identifier);
1978 GNUNET_CONTAINER_DLL_insert_tail(handle->ego_head,handle->ego_tail, ego_entry);
1982 for (ego_entry = handle->ego_head; NULL != ego_entry; ego_entry = ego_entry->next) {
1983 if (ego_entry->ego == ego)
1986 if (NULL != ego_entry)
1987 GNUNET_CONTAINER_DLL_remove(handle->ego_head,handle->ego_tail, ego_entry);
1993 rest_identity_process_request(struct GNUNET_REST_RequestHandle *rest_handle,
1994 GNUNET_REST_ResultProcessor proc,
1997 struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
1998 handle->oidc = GNUNET_new (struct OIDC_Variables);
1999 if ( NULL == OIDC_identity_login_time )
2000 OIDC_identity_login_time = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
2001 if ( NULL == OIDC_identity_grants )
2002 OIDC_identity_grants = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
2003 if ( NULL == OIDC_ticket_once )
2004 OIDC_ticket_once = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
2005 if ( NULL == OIDC_interpret_access_token )
2006 OIDC_interpret_access_token = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
2007 handle->response_code = 0;
2008 handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
2009 handle->proc_cls = proc_cls;
2010 handle->proc = proc;
2011 handle->state = ID_REST_STATE_INIT;
2012 handle->rest_handle = rest_handle;
2014 handle->url = GNUNET_strdup (rest_handle->url);
2015 if (handle->url[strlen (handle->url)-1] == '/')
2016 handle->url[strlen (handle->url)-1] = '\0';
2017 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2019 handle->identity_handle = GNUNET_IDENTITY_connect (cfg,
2022 handle->namestore_handle = GNUNET_NAMESTORE_connect (cfg);
2023 handle->timeout_task =
2024 GNUNET_SCHEDULER_add_delayed (handle->timeout,
2027 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2032 * Entry point for the plugin.
2034 * @param cls Config info
2035 * @return NULL on error, otherwise the plugin context
2038 libgnunet_plugin_rest_openid_connect_init (void *cls)
2040 static struct Plugin plugin;
2041 struct GNUNET_REST_Plugin *api;
2044 if (NULL != plugin.cfg)
2045 return NULL; /* can only initialize once! */
2046 memset (&plugin, 0, sizeof (struct Plugin));
2048 api = GNUNET_new (struct GNUNET_REST_Plugin);
2050 api->name = GNUNET_REST_API_NS_OIDC;
2051 api->process_request = &rest_identity_process_request;
2052 GNUNET_asprintf (&allow_methods,
2053 "%s, %s, %s, %s, %s",
2054 MHD_HTTP_METHOD_GET,
2055 MHD_HTTP_METHOD_POST,
2056 MHD_HTTP_METHOD_PUT,
2057 MHD_HTTP_METHOD_DELETE,
2058 MHD_HTTP_METHOD_OPTIONS);
2060 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2061 _("Identity Provider REST API initialized\n"));
2067 * Exit point from the plugin.
2069 * @param cls the plugin context (as returned by "init")
2070 * @return always NULL
2073 libgnunet_plugin_rest_openid_connect_done (void *cls)
2075 struct GNUNET_REST_Plugin *api = cls;
2076 struct Plugin *plugin = api->cls;
2079 struct GNUNET_CONTAINER_MultiHashMapIterator *hashmap_it;
2081 hashmap_it = GNUNET_CONTAINER_multihashmap_iterator_create (
2082 OIDC_identity_login_time);
2083 while (GNUNET_YES ==
2084 GNUNET_CONTAINER_multihashmap_iterator_next (hashmap_it, NULL, value))
2089 GNUNET_CONTAINER_multihashmap_destroy(OIDC_identity_login_time);
2090 hashmap_it = GNUNET_CONTAINER_multihashmap_iterator_create (OIDC_identity_grants);
2091 while (GNUNET_YES ==
2092 GNUNET_CONTAINER_multihashmap_iterator_next (hashmap_it, NULL, value))
2097 GNUNET_CONTAINER_multihashmap_destroy(OIDC_identity_grants);
2098 hashmap_it = GNUNET_CONTAINER_multihashmap_iterator_create (OIDC_ticket_once);
2099 while (GNUNET_YES ==
2100 GNUNET_CONTAINER_multihashmap_iterator_next (hashmap_it, NULL, value))
2105 GNUNET_CONTAINER_multihashmap_destroy(OIDC_ticket_once);
2106 hashmap_it = GNUNET_CONTAINER_multihashmap_iterator_create (OIDC_interpret_access_token);
2107 while (GNUNET_YES ==
2108 GNUNET_CONTAINER_multihashmap_iterator_next (hashmap_it, NULL, value))
2113 GNUNET_CONTAINER_multihashmap_destroy(OIDC_interpret_access_token);
2114 GNUNET_CONTAINER_multihashmap_iterator_destroy(hashmap_it);
2115 GNUNET_free_non_null (allow_methods);
2117 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2118 "Identity Provider REST plugin is finished\n");
2122 /* end of plugin_rest_identity_provider.c */