-Merge branch 'master' into gsoc2018/rest_api
[oweals/gnunet.git] / src / reclaim / plugin_rest_openid_connect.c
1 /*
2    This file is part of GNUnet.
3    Copyright (C) 2012-2015 GNUnet e.V.
4
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.
9
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.
14   
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/>.
17    */
18 /**
19  * @author Martin Schanzenbach
20  * @author Philippe Buschmann
21  * @file identity/plugin_rest_openid_connect.c
22  * @brief GNUnet Namestore REST plugin
23  *
24  */
25
26 #include "platform.h"
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"
36 #include <jansson.h>
37 #include <inttypes.h>
38 #include "gnunet_signatures.h"
39 #include "gnunet_reclaim_attribute_lib.h"
40 #include "gnunet_reclaim_service.h"
41 #include "oidc_helper.h"
42
43 /**
44  * REST root namespace
45  */
46 #define GNUNET_REST_API_NS_OIDC "/openid"
47
48 /**
49  * Authorize endpoint
50  */
51 #define GNUNET_REST_API_NS_AUTHORIZE "/openid/authorize"
52
53 /**
54  * Token endpoint
55  */
56 #define GNUNET_REST_API_NS_TOKEN "/openid/token"
57
58 /**
59  * UserInfo endpoint
60  */
61 #define GNUNET_REST_API_NS_USERINFO "/openid/userinfo"
62
63 /**
64  * Login namespace
65  */
66 #define GNUNET_REST_API_NS_LOGIN "/openid/login"
67
68 /**
69  * Attribute key
70  */
71 #define GNUNET_REST_JSONAPI_RECLAIM_ATTRIBUTE "attribute"
72
73 /**
74  * Ticket key
75  */
76 #define GNUNET_REST_JSONAPI_IDENTITY_TICKET "ticket"
77
78
79 /**
80  * Value key
81  */
82 #define GNUNET_REST_JSONAPI_RECLAIM_ATTRIBUTE_VALUE "value"
83
84 /**
85  * State while collecting all egos
86  */
87 #define ID_REST_STATE_INIT 0
88
89 /**
90  * Done collecting egos
91  */
92 #define ID_REST_STATE_POST_INIT 1
93
94 /**
95  * OIDC grant_type key
96  */
97 #define OIDC_GRANT_TYPE_KEY "grant_type"
98
99 /**
100  * OIDC grant_type key
101  */
102 #define OIDC_GRANT_TYPE_VALUE "authorization_code"
103
104 /**
105  * OIDC code key
106  */
107 #define OIDC_CODE_KEY "code"
108
109 /**
110  * OIDC response_type key
111  */
112 #define OIDC_RESPONSE_TYPE_KEY "response_type"
113
114 /**
115  * OIDC client_id key
116  */
117 #define OIDC_CLIENT_ID_KEY "client_id"
118
119 /**
120  * OIDC scope key
121  */
122 #define OIDC_SCOPE_KEY "scope"
123
124 /**
125  * OIDC redirect_uri key
126  */
127 #define OIDC_REDIRECT_URI_KEY "redirect_uri"
128
129 /**
130  * OIDC state key
131  */
132 #define OIDC_STATE_KEY "state"
133
134 /**
135  * OIDC nonce key
136  */
137 #define OIDC_NONCE_KEY "nonce"
138
139 /**
140  * OIDC cookie header key
141  */
142 #define OIDC_COOKIE_HEADER_KEY "cookie"
143
144 /**
145  * OIDC cookie header information key
146  */
147 #define OIDC_AUTHORIZATION_HEADER_KEY "authorization"
148
149 /**
150  * OIDC cookie header information key
151  */
152 #define OIDC_COOKIE_HEADER_INFORMATION_KEY "Identity="
153
154 /**
155  * OIDC expected response_type while authorizing
156  */
157 #define OIDC_EXPECTED_AUTHORIZATION_RESPONSE_TYPE "code"
158
159 /**
160  * OIDC expected scope part while authorizing
161  */
162 #define OIDC_EXPECTED_AUTHORIZATION_SCOPE "openid"
163
164 /**
165  * OIDC ignored parameter array
166  */
167 static char* OIDC_ignored_parameter_array [] =
168 {
169   "display",
170   "prompt",
171   "ui_locales", 
172   "response_mode",
173   "id_token_hint",
174   "login_hint", 
175   "acr_values"
176 };
177
178 /**
179  * OIDC authorized identities and times hashmap
180  */
181 struct GNUNET_CONTAINER_MultiHashMap *OIDC_identity_login_time;
182
183 /**
184  * OIDC authorized identities and times hashmap
185  */
186 struct GNUNET_CONTAINER_MultiHashMap *OIDC_identity_grants;
187
188 /**
189  * OIDC ticket/code use only once
190  */
191 struct GNUNET_CONTAINER_MultiHashMap *OIDC_ticket_once;
192
193 /**
194  * OIDC access_token to ticket and ego
195  */
196 struct GNUNET_CONTAINER_MultiHashMap *OIDC_interpret_access_token;
197
198 /**
199  * The configuration handle
200  */
201 const struct GNUNET_CONFIGURATION_Handle *cfg;
202
203 /**
204  * HTTP methods allows for this plugin
205  */
206 static char* allow_methods;
207
208 /**
209  * @brief struct returned by the initialization function of the plugin
210  */
211 struct Plugin
212 {
213   const struct GNUNET_CONFIGURATION_Handle *cfg;
214 };
215
216 /**
217  * OIDC needed variables
218  */
219 struct OIDC_Variables
220 {
221   /**
222    * The RP client public key
223    */
224   struct GNUNET_CRYPTO_EcdsaPublicKey client_pkey;
225
226   /**
227    * The OIDC client id of the RP
228    */
229   char *client_id;
230
231   /**
232    * The OIDC redirect uri
233    */
234   char *redirect_uri;
235
236   /**
237    * The list of oidc scopes
238    */
239   char *scope;
240
241   /**
242    * The OIDC state
243    */
244   char *state;
245
246   /**
247    * The OIDC nonce
248    */
249   char *nonce;
250
251   /**
252    * The OIDC response type
253    */
254   char *response_type;
255
256   /**
257    * The identity chosen by the user to login
258    */
259   char *login_identity;
260
261   /**
262    * The response JSON
263    */
264   json_t *response;
265
266 };
267
268 /**
269  * The ego list
270  */
271 struct EgoEntry
272 {
273   /**
274    * DLL
275    */
276   struct EgoEntry *next;
277
278   /**
279    * DLL
280    */
281   struct EgoEntry *prev;
282
283   /**
284    * Ego Identifier
285    */
286   char *identifier;
287
288   /**
289    * Public key string
290    */
291   char *keystring;
292
293   /**
294    * The Ego
295    */
296   struct GNUNET_IDENTITY_Ego *ego;
297 };
298
299
300 struct RequestHandle
301 {
302   /**
303    * Ego list
304    */
305   struct EgoEntry *ego_head;
306
307   /**
308    * Ego list
309    */
310   struct EgoEntry *ego_tail;
311
312   /**
313    * Selected ego
314    */
315   struct EgoEntry *ego_entry;
316
317   /**
318    * Pointer to ego private key
319    */
320   struct GNUNET_CRYPTO_EcdsaPrivateKey priv_key;
321
322   /**
323    * OIDC variables
324    */
325   struct OIDC_Variables *oidc;
326
327   /**
328    * The processing state
329    */
330   int state;
331
332   /**
333    * Handle to Identity service.
334    */
335   struct GNUNET_IDENTITY_Handle *identity_handle;
336
337   /**
338    * Rest connection
339    */
340   struct GNUNET_REST_RequestHandle *rest_handle;
341
342   /**
343    * GNS handle
344    */
345   struct GNUNET_GNS_Handle *gns_handle;
346
347   /**
348    * GNS lookup op
349    */
350   struct GNUNET_GNS_LookupRequest *gns_op;
351
352   /**
353    * Handle to NAMESTORE
354    */
355   struct GNUNET_NAMESTORE_Handle *namestore_handle;
356
357   /**
358    * Iterator for NAMESTORE
359    */
360   struct GNUNET_NAMESTORE_ZoneIterator *namestore_handle_it;
361
362   /**
363    * Attribute claim list
364    */
365   struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attr_list;
366
367   /**
368    * IDENTITY Operation
369    */
370   struct GNUNET_IDENTITY_Operation *op;
371
372   /**
373    * Identity Provider
374    */
375   struct GNUNET_RECLAIM_Handle *idp;
376
377   /**
378    * Idp Operation
379    */
380   struct GNUNET_RECLAIM_Operation *idp_op;
381
382   /**
383    * Attribute iterator
384    */
385   struct GNUNET_RECLAIM_AttributeIterator *attr_it;
386
387   /**
388    * Ticket iterator
389    */
390   struct GNUNET_RECLAIM_TicketIterator *ticket_it;
391
392   /**
393    * A ticket
394    */
395   struct GNUNET_RECLAIM_Ticket ticket;
396
397   /**
398    * Desired timeout for the lookup (default is no timeout).
399    */
400   struct GNUNET_TIME_Relative timeout;
401
402   /**
403    * ID of a task associated with the resolution process.
404    */
405   struct GNUNET_SCHEDULER_Task *timeout_task;
406
407   /**
408    * The plugin result processor
409    */
410   GNUNET_REST_ResultProcessor proc;
411
412   /**
413    * The closure of the result processor
414    */
415   void *proc_cls;
416
417   /**
418    * The url
419    */
420   char *url;
421
422   /**
423    * The tld for redirect
424    */
425   char *tld;
426
427   /**
428    * The redirect prefix
429    */
430   char *redirect_prefix;
431
432   /**
433    * The redirect suffix
434    */
435   char *redirect_suffix;
436
437   /**
438    * Error response message
439    */
440   char *emsg;
441
442   /**
443    * Error response description
444    */
445   char *edesc;
446
447   /**
448    * Reponse code
449    */
450   int response_code;
451
452   /**
453    * Response object
454    */
455   struct GNUNET_JSONAPI_Document *resp_object;
456
457 };
458
459 /**
460  * Cleanup lookup handle
461  * @param handle Handle to clean up
462  */
463 static void
464 cleanup_handle (struct RequestHandle *handle)
465 {
466   struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *claim_entry;
467   struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *claim_tmp;
468   struct EgoEntry *ego_entry;
469   struct EgoEntry *ego_tmp;
470   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
471               "Cleaning up\n");
472   if (NULL != handle->resp_object)
473     GNUNET_JSONAPI_document_delete (handle->resp_object);
474   if (NULL != handle->timeout_task)
475     GNUNET_SCHEDULER_cancel (handle->timeout_task);
476   if (NULL != handle->identity_handle)
477     GNUNET_IDENTITY_disconnect (handle->identity_handle);
478   if (NULL != handle->attr_it)
479     GNUNET_RECLAIM_get_attributes_stop (handle->attr_it);
480   if (NULL != handle->ticket_it)
481     GNUNET_RECLAIM_ticket_iteration_stop (handle->ticket_it);
482   if (NULL != handle->idp)
483     GNUNET_RECLAIM_disconnect (handle->idp);
484   if (NULL != handle->url)
485     GNUNET_free (handle->url);
486   if (NULL != handle->tld)
487     GNUNET_free (handle->tld);
488   if (NULL != handle->redirect_prefix)
489     GNUNET_free (handle->redirect_prefix);
490   if (NULL != handle->redirect_suffix)
491     GNUNET_free (handle->redirect_suffix);
492   if (NULL != handle->emsg)
493     GNUNET_free (handle->emsg);
494   if (NULL != handle->edesc)
495     GNUNET_free (handle->edesc);
496   if (NULL != handle->gns_op)
497     GNUNET_GNS_lookup_cancel (handle->gns_op);
498   if (NULL != handle->gns_handle)
499     GNUNET_GNS_disconnect (handle->gns_handle);
500
501   if (NULL != handle->namestore_handle)
502     GNUNET_NAMESTORE_disconnect (handle->namestore_handle);
503   if (NULL != handle->oidc)
504   {
505     if (NULL != handle->oidc->client_id)
506       GNUNET_free(handle->oidc->client_id);
507     if (NULL != handle->oidc->login_identity)
508       GNUNET_free(handle->oidc->login_identity);
509     if (NULL != handle->oidc->nonce)
510       GNUNET_free(handle->oidc->nonce);
511     if (NULL != handle->oidc->redirect_uri)
512       GNUNET_free(handle->oidc->redirect_uri);
513     if (NULL != handle->oidc->response_type)
514       GNUNET_free(handle->oidc->response_type);
515     if (NULL != handle->oidc->scope)
516       GNUNET_free(handle->oidc->scope);
517     if (NULL != handle->oidc->state)
518       GNUNET_free(handle->oidc->state);
519     if (NULL != handle->oidc->response)
520       json_decref(handle->oidc->response);
521     GNUNET_free(handle->oidc);
522   }
523   if ( NULL != handle->attr_list )
524   {
525     for (claim_entry = handle->attr_list->list_head;
526          NULL != claim_entry;)
527     {
528       claim_tmp = claim_entry;
529       claim_entry = claim_entry->next;
530       GNUNET_free(claim_tmp->claim);
531       GNUNET_free(claim_tmp);
532     }
533     GNUNET_free (handle->attr_list);
534   }
535   for (ego_entry = handle->ego_head;
536        NULL != ego_entry;)
537   {
538     ego_tmp = ego_entry;
539     ego_entry = ego_entry->next;
540     GNUNET_free (ego_tmp->identifier);
541     GNUNET_free (ego_tmp->keystring);
542     GNUNET_free (ego_tmp);
543   }
544   if (NULL != handle->attr_it)
545   {
546     GNUNET_free(handle->attr_it);
547   }
548   GNUNET_free (handle);
549 }
550
551 static void
552 cleanup_handle_delayed (void *cls)
553 {
554   cleanup_handle (cls);
555 }
556
557
558 /**
559  * Task run on error, sends error message.  Cleans up everything.
560  *
561  * @param cls the `struct RequestHandle`
562  */
563 static void
564 do_error (void *cls)
565 {
566   struct RequestHandle *handle = cls;
567   struct MHD_Response *resp;
568   char *json_error;
569
570   GNUNET_asprintf (&json_error, "{ \"error\" : \"%s\", \"error_description\" : \"%s\"%s%s%s}",
571                    handle->emsg,
572                    (NULL != handle->edesc) ? handle->edesc : "",
573                    (NULL != handle->oidc->state) ? ", \"state\":\"" : "",
574                    (NULL != handle->oidc->state) ? handle->oidc->state : "",
575                    (NULL != handle->oidc->state) ? "\"" : "");
576   if ( 0 == handle->response_code )
577   {
578     handle->response_code = MHD_HTTP_BAD_REQUEST;
579   }
580   resp = GNUNET_REST_create_response (json_error);
581   if (MHD_HTTP_UNAUTHORIZED == handle->response_code)
582   {
583     MHD_add_response_header(resp, "WWW-Authenticate", "Basic");
584   }
585   MHD_add_response_header (resp, "Content-Type", "application/json");
586   handle->proc (handle->proc_cls, resp, handle->response_code);
587   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
588   GNUNET_free (json_error);
589 }
590
591
592 /**
593  * Task run on error in userinfo endpoint, sends error header. Cleans up
594  * everything
595  *
596  * @param cls the `struct RequestHandle`
597  */
598 static void
599 do_userinfo_error (void *cls)
600 {
601   struct RequestHandle *handle = cls;
602   struct MHD_Response *resp;
603   char *error;
604
605   GNUNET_asprintf (&error, "error=\"%s\", error_description=\"%s\"",
606                    handle->emsg,
607                    (NULL != handle->edesc) ? handle->edesc : "");
608   resp = GNUNET_REST_create_response ("");
609   MHD_add_response_header(resp, "WWW-Authenticate", error);
610   handle->proc (handle->proc_cls, resp, handle->response_code);
611   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
612   GNUNET_free (error);
613 }
614
615
616 /**
617  * Task run on error, sends error message and redirects. Cleans up everything.
618  *
619  * @param cls the `struct RequestHandle`
620  */
621 static void
622 do_redirect_error (void *cls)
623 {
624   struct RequestHandle *handle = cls;
625   struct MHD_Response *resp;
626   char* redirect;
627   GNUNET_asprintf (&redirect,
628                    "%s?error=%s&error_description=%s%s%s",
629                    handle->oidc->redirect_uri, handle->emsg, handle->edesc,
630                    (NULL != handle->oidc->state) ? "&state=" : "",
631                    (NULL != handle->oidc->state) ? handle->oidc->state : "");
632   resp = GNUNET_REST_create_response ("");
633   MHD_add_response_header (resp, "Location", redirect);
634   handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
635   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
636   GNUNET_free (redirect);
637 }
638
639 /**
640  * Task run on timeout, sends error message.  Cleans up everything.
641  *
642  * @param cls the `struct RequestHandle`
643  */
644 static void
645 do_timeout (void *cls)
646 {
647   struct RequestHandle *handle = cls;
648
649   handle->timeout_task = NULL;
650   do_error (handle);
651 }
652
653 /**
654  * Return attributes for claim
655  *
656  * @param cls the request handle
657  */
658 static void
659 return_userinfo_response (void *cls)
660 {
661   char* result_str;
662   struct RequestHandle *handle = cls;
663   struct MHD_Response *resp;
664
665   result_str = json_dumps (handle->oidc->response, 0);
666
667   resp = GNUNET_REST_create_response (result_str);
668   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
669   GNUNET_free (result_str);
670   cleanup_handle (handle);
671 }
672
673 /**
674  * Returns base64 encoded string without padding
675  *
676  * @param string the string to encode
677  * @return base64 encoded string
678  */
679 static char*
680 base_64_encode(const char *s)
681 {
682   char *enc;
683   char *tmp;
684
685   GNUNET_STRINGS_base64_encode(s, strlen(s), &enc);
686   tmp = strrchr (enc, '=');
687   *tmp = '\0';
688   return enc;
689 }
690
691 /**
692  * Respond to OPTIONS request
693  *
694  * @param con_handle the connection handle
695  * @param url the url
696  * @param cls the RequestHandle
697  */
698 static void
699 options_cont (struct GNUNET_REST_RequestHandle *con_handle,
700               const char* url,
701               void *cls)
702 {
703   struct MHD_Response *resp;
704   struct RequestHandle *handle = cls;
705
706   //For now, independent of path return all options
707   resp = GNUNET_REST_create_response (NULL);
708   MHD_add_response_header (resp,
709                            "Access-Control-Allow-Methods",
710                            allow_methods);
711   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
712   cleanup_handle (handle);
713   return;
714 }
715
716 /**
717  * Interprets cookie header and pass its identity keystring to handle
718  */
719 static void
720 cookie_identity_interpretation (struct RequestHandle *handle)
721 {
722   struct GNUNET_HashCode cache_key;
723   char *cookies;
724   struct GNUNET_TIME_Absolute current_time, *relog_time;
725   char delimiter[] = "; ";
726
727   //gets identity of login try with cookie
728   GNUNET_CRYPTO_hash (OIDC_COOKIE_HEADER_KEY, strlen (OIDC_COOKIE_HEADER_KEY),
729                       &cache_key);
730   if ( GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->header_param_map,
731                                                              &cache_key) )
732   {
733     //splits cookies and find 'Identity' cookie
734     cookies = GNUNET_CONTAINER_multihashmap_get ( handle->rest_handle->header_param_map, &cache_key);
735     handle->oidc->login_identity = strtok(cookies, delimiter);
736
737     while ( NULL != handle->oidc->login_identity )
738     {
739       if ( NULL != strstr (handle->oidc->login_identity, OIDC_COOKIE_HEADER_INFORMATION_KEY) )
740       {
741         break;
742       }
743       handle->oidc->login_identity = strtok (NULL, delimiter);
744     }
745     GNUNET_CRYPTO_hash (handle->oidc->login_identity, strlen (handle->oidc->login_identity),
746                         &cache_key);
747     if ( GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (OIDC_identity_login_time, &cache_key) )
748     {
749       relog_time = GNUNET_CONTAINER_multihashmap_get (OIDC_identity_login_time,
750                                                       &cache_key);
751       current_time = GNUNET_TIME_absolute_get ();
752       // 30 min after old login -> redirect to login
753       if ( current_time.abs_value_us <= relog_time->abs_value_us )
754       {
755         handle->oidc->login_identity = strtok(handle->oidc->login_identity, OIDC_COOKIE_HEADER_INFORMATION_KEY);
756         handle->oidc->login_identity = GNUNET_strdup(handle->oidc->login_identity);
757       } else {
758         handle->oidc->login_identity = NULL;
759       }
760     }
761     else
762     {
763       handle->oidc->login_identity = NULL;
764     }
765   }
766 }
767
768 /**
769  * Redirects to login page stored in configuration file
770  */
771 static void
772 login_redirection(void *cls)
773 {
774   char *login_base_url;
775   char *new_redirect;
776   struct MHD_Response *resp;
777   struct RequestHandle *handle = cls;
778
779   if ( GNUNET_OK
780        == GNUNET_CONFIGURATION_get_value_string (cfg, "reclaim-rest-plugin",
781                                                  "address", &login_base_url) )
782   {
783     GNUNET_asprintf (&new_redirect, "%s?%s=%s&%s=%s&%s=%s&%s=%s&%s=%s&%s=%s",
784                      login_base_url,
785                      OIDC_RESPONSE_TYPE_KEY,
786                      handle->oidc->response_type,
787                      OIDC_CLIENT_ID_KEY,
788                      handle->oidc->client_id,
789                      OIDC_REDIRECT_URI_KEY,
790                      handle->oidc->redirect_uri,
791                      OIDC_SCOPE_KEY,
792                      handle->oidc->scope,
793                      OIDC_STATE_KEY,
794                      (NULL != handle->oidc->state) ? handle->oidc->state : "",
795                      OIDC_NONCE_KEY,
796                      (NULL != handle->oidc->nonce) ? handle->oidc->nonce : "");
797     resp = GNUNET_REST_create_response ("");
798     MHD_add_response_header (resp, "Location", new_redirect);
799     GNUNET_free(login_base_url);
800   }
801   else
802   {
803     handle->emsg = GNUNET_strdup("server_error");
804     handle->edesc = GNUNET_strdup ("gnunet configuration failed");
805     handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
806     GNUNET_SCHEDULER_add_now (&do_error, handle);
807     return;
808   }
809   handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
810   GNUNET_free(new_redirect);
811   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
812 }
813
814 /**
815  * Does internal server error when iteration failed.
816  */
817 static void
818 oidc_iteration_error (void *cls)
819 {
820   struct RequestHandle *handle = cls;
821   handle->emsg = GNUNET_strdup("INTERNAL_SERVER_ERROR");
822   handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
823   GNUNET_SCHEDULER_add_now (&do_error, handle);
824 }
825
826 static void
827 get_client_name_result (void *cls,
828                         const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
829                         const char *label,
830                         unsigned int rd_count,
831                         const struct GNUNET_GNSRECORD_Data *rd)
832 {
833   struct RequestHandle *handle = cls;
834   struct MHD_Response *resp;
835   char *ticket_str;
836   char *redirect_uri;
837   char *code_json_string;
838   char *code_base64_final_string;
839
840   ticket_str = GNUNET_STRINGS_data_to_string_alloc (&handle->ticket,
841                                                     sizeof (struct GNUNET_RECLAIM_Ticket));
842   //TODO change if more attributes are needed (see max_age)
843   code_json_string = OIDC_build_authz_code (&handle->priv_key,
844                                             &handle->ticket,
845                                             handle->oidc->nonce);
846   code_base64_final_string = base_64_encode(code_json_string);
847   GNUNET_asprintf (&redirect_uri, "%s.%s/%s?%s=%s&state=%s",
848                    handle->redirect_prefix,
849                    handle->tld,
850                    handle->redirect_suffix,
851                    handle->oidc->response_type,
852                    code_base64_final_string, handle->oidc->state);
853   resp = GNUNET_REST_create_response ("");
854   MHD_add_response_header (resp, "Location", redirect_uri);
855   handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
856   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
857   GNUNET_free (redirect_uri);
858   GNUNET_free (ticket_str);
859   GNUNET_free (code_json_string);
860   GNUNET_free (code_base64_final_string);
861   return;
862
863 }
864
865
866 static void
867 get_client_name_error (void *cls)
868 {
869   struct RequestHandle *handle = cls;
870
871   handle->emsg = GNUNET_strdup("server_error");
872   handle->edesc = GNUNET_strdup("Server cannot generate ticket, no name found for client.");
873   GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
874 }
875
876
877 static void
878 lookup_redirect_uri_result (void *cls,
879                             uint32_t rd_count,
880                             const struct GNUNET_GNSRECORD_Data *rd)
881 {
882   struct RequestHandle *handle = cls;
883   char *tmp;
884   char *tmp_key_str;
885   char *pos;
886   struct GNUNET_CRYPTO_EcdsaPublicKey redirect_zone;
887
888   handle->gns_op = NULL;
889   if (0 == rd_count)
890   {
891     handle->emsg = GNUNET_strdup("server_error");
892     handle->edesc = GNUNET_strdup("Server cannot generate ticket, redirect uri not found.");
893     GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
894     return;
895   }
896   for (int i = 0; i < rd_count; i++)
897   {
898     if (GNUNET_GNSRECORD_TYPE_RECLAIM_OIDC_REDIRECT != rd[i].record_type)
899       continue;
900     if (0 != strcmp (rd[i].data,
901                      handle->oidc->redirect_uri))
902       continue;
903     tmp = GNUNET_strdup (rd[i].data);
904     pos = strrchr (tmp,
905                    (unsigned char) '.');
906     *pos = '\0';
907     handle->redirect_prefix = GNUNET_strdup (tmp);
908     tmp_key_str = pos + 1;
909     pos = strchr (tmp_key_str,
910                   (unsigned char) '/');
911     *pos = '\0';
912     handle->redirect_suffix = GNUNET_strdup (pos + 1);
913
914     GNUNET_STRINGS_string_to_data (tmp_key_str,
915                                    strlen (tmp_key_str),
916                                    &redirect_zone,
917                                    sizeof (redirect_zone));
918
919     GNUNET_NAMESTORE_zone_to_name (handle->namestore_handle,
920                                    &handle->priv_key,
921                                    &redirect_zone,
922                                    &get_client_name_error,
923                                    handle,
924                                    &get_client_name_result,
925                                    handle);
926     GNUNET_free (tmp);
927     return;
928   }
929   handle->emsg = GNUNET_strdup("server_error");
930   handle->edesc = GNUNET_strdup("Server cannot generate ticket, redirect uri not found.");
931   GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
932 }
933
934 /**
935  * Issues ticket and redirects to relying party with the authorization code as
936  * parameter. Otherwise redirects with error
937  */
938 static void
939 oidc_ticket_issue_cb (void* cls,
940                       const struct GNUNET_RECLAIM_Ticket *ticket)
941 {
942   struct RequestHandle *handle = cls;
943
944   handle->idp_op = NULL;
945   handle->ticket = *ticket;
946   if (NULL == ticket)
947   {
948     handle->emsg = GNUNET_strdup("server_error");
949     handle->edesc = GNUNET_strdup("Server cannot generate ticket.");
950     GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
951     return;
952   }
953   handle->gns_op = GNUNET_GNS_lookup (handle->gns_handle,
954                                       "+",
955                                       &handle->oidc->client_pkey,
956                                       GNUNET_GNSRECORD_TYPE_RECLAIM_OIDC_REDIRECT,
957                                       GNUNET_GNS_LO_DEFAULT,
958                                       &lookup_redirect_uri_result,
959                                       handle);
960
961 }
962
963 static void
964 oidc_collect_finished_cb (void *cls)
965 {
966   struct RequestHandle *handle = cls;
967   handle->attr_it = NULL;
968   handle->ticket_it = NULL;
969   if (NULL == handle->attr_list->list_head)
970   {
971     handle->emsg = GNUNET_strdup("invalid_scope");
972     handle->edesc = GNUNET_strdup("The requested scope is not available.");
973     GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
974     return;
975   }
976   handle->idp_op = GNUNET_RECLAIM_ticket_issue (handle->idp,
977                                                 &handle->priv_key,
978                                                 &handle->oidc->client_pkey,
979                                                 handle->attr_list,
980                                                 &oidc_ticket_issue_cb,
981                                                 handle);
982 }
983
984
985 /**
986  * Collects all attributes for an ego if in scope parameter
987  */
988 static void
989 oidc_attr_collect (void *cls,
990                    const struct GNUNET_CRYPTO_EcdsaPublicKey *identity,
991                    const struct GNUNET_RECLAIM_ATTRIBUTE_Claim *attr)
992 {
993   struct RequestHandle *handle = cls;
994   struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *le;
995   char* scope_variables;
996   char* scope_variable;
997   char delimiter[]=" ";
998
999   if ( (NULL == attr->name) || (NULL == attr->data) )
1000   {
1001     GNUNET_RECLAIM_get_attributes_next (handle->attr_it);
1002     return;
1003   }
1004
1005   scope_variables = GNUNET_strdup(handle->oidc->scope);
1006   scope_variable = strtok (scope_variables, delimiter);
1007   while (NULL != scope_variable)
1008   {
1009     if ( 0 == strcmp (attr->name, scope_variable) )
1010     {
1011       break;
1012     }
1013     scope_variable = strtok (NULL, delimiter);
1014   }
1015   if ( NULL == scope_variable )
1016   {
1017     GNUNET_RECLAIM_get_attributes_next (handle->attr_it);
1018     GNUNET_free(scope_variables);
1019     return;
1020   }
1021   GNUNET_free(scope_variables);
1022
1023   le = GNUNET_new(struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry);
1024   le->claim = GNUNET_RECLAIM_ATTRIBUTE_claim_new (attr->name, attr->type,
1025                                                   attr->data, attr->data_size);
1026   GNUNET_CONTAINER_DLL_insert(handle->attr_list->list_head,
1027                               handle->attr_list->list_tail, le);
1028   GNUNET_RECLAIM_get_attributes_next (handle->attr_it);
1029 }
1030
1031
1032 /**
1033  * Checks time and cookie and redirects accordingly
1034  */
1035 static void
1036 login_check (void *cls)
1037 {
1038   struct RequestHandle *handle = cls;
1039   struct GNUNET_TIME_Absolute current_time, *relog_time;
1040   struct GNUNET_CRYPTO_EcdsaPublicKey pubkey, ego_pkey;
1041   struct GNUNET_HashCode cache_key;
1042   char *identity_cookie;
1043
1044   GNUNET_asprintf (&identity_cookie, "Identity=%s", handle->oidc->login_identity);
1045   GNUNET_CRYPTO_hash (identity_cookie, strlen (identity_cookie), &cache_key);
1046   GNUNET_free(identity_cookie);
1047   //No login time for identity -> redirect to login
1048   if ( GNUNET_YES
1049        == GNUNET_CONTAINER_multihashmap_contains (OIDC_identity_login_time,
1050                                                   &cache_key) )
1051   {
1052     relog_time = GNUNET_CONTAINER_multihashmap_get (OIDC_identity_login_time,
1053                                                     &cache_key);
1054     current_time = GNUNET_TIME_absolute_get ();
1055     // 30 min after old login -> redirect to login
1056     if ( current_time.abs_value_us <= relog_time->abs_value_us )
1057     {
1058       if ( GNUNET_OK
1059            != GNUNET_CRYPTO_ecdsa_public_key_from_string (
1060                                                           handle->oidc->login_identity,
1061                                                           strlen (handle->oidc->login_identity), &pubkey) )
1062       {
1063         handle->emsg = GNUNET_strdup("invalid_cookie");
1064         handle->edesc = GNUNET_strdup(
1065                                       "The cookie of a login identity is not valid");
1066         GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1067         return;
1068       }
1069       // iterate over egos and compare their public key
1070       for (handle->ego_entry = handle->ego_head;
1071            NULL != handle->ego_entry; handle->ego_entry = handle->ego_entry->next)
1072       {
1073         GNUNET_IDENTITY_ego_get_public_key (handle->ego_entry->ego, &ego_pkey);
1074         if ( 0
1075              == memcmp (&ego_pkey, &pubkey,
1076                         sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey)) )
1077         {
1078           handle->priv_key = *GNUNET_IDENTITY_ego_get_private_key (
1079                                                                    handle->ego_entry->ego);
1080           handle->resp_object = GNUNET_JSONAPI_document_new ();
1081           handle->idp = GNUNET_RECLAIM_connect (cfg);
1082           handle->attr_list = GNUNET_new(
1083                                          struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList);
1084           handle->attr_it = GNUNET_RECLAIM_get_attributes_start (
1085                                                                  handle->idp, &handle->priv_key, &oidc_iteration_error, handle,
1086                                                                  &oidc_attr_collect, handle, &oidc_collect_finished_cb, handle);
1087           return;
1088         }
1089       }
1090       //handle->emsg = GNUNET_strdup("invalid_cookie");
1091       //handle->edesc = GNUNET_strdup(
1092       //                              "The cookie of the login identity is not valid");
1093       //GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1094       GNUNET_SCHEDULER_add_now (&login_redirection,handle);
1095       return;
1096     }
1097   }
1098 }
1099
1100 /**
1101  * Iteration over all results finished, build final
1102  * response.
1103  *
1104  * @param cls the `struct RequestHandle`
1105  */
1106 static void
1107 build_authz_response (void *cls)
1108 {
1109   struct RequestHandle *handle = cls;
1110   struct GNUNET_HashCode cache_key;
1111
1112   char *expected_scope;
1113   char delimiter[]=" ";
1114   int number_of_ignored_parameter, iterator;
1115
1116
1117   // REQUIRED value: redirect_uri
1118   GNUNET_CRYPTO_hash (OIDC_REDIRECT_URI_KEY, strlen (OIDC_REDIRECT_URI_KEY),
1119                       &cache_key);
1120   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1121                                                            &cache_key))
1122   {
1123     handle->emsg=GNUNET_strdup("invalid_request");
1124     handle->edesc=GNUNET_strdup("missing parameter redirect_uri");
1125     GNUNET_SCHEDULER_add_now (&do_error, handle);
1126     return;
1127   }
1128   handle->oidc->redirect_uri = GNUNET_strdup (GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
1129                                                                                 &cache_key));
1130
1131   // REQUIRED value: response_type
1132   GNUNET_CRYPTO_hash (OIDC_RESPONSE_TYPE_KEY, strlen (OIDC_RESPONSE_TYPE_KEY),
1133                       &cache_key);
1134   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1135                                                            &cache_key))
1136   {
1137     handle->emsg=GNUNET_strdup("invalid_request");
1138     handle->edesc=GNUNET_strdup("missing parameter response_type");
1139     GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1140     return;
1141   }
1142   handle->oidc->response_type = GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
1143                                                                   &cache_key);
1144   handle->oidc->response_type = GNUNET_strdup (handle->oidc->response_type);
1145
1146   // REQUIRED value: scope
1147   GNUNET_CRYPTO_hash (OIDC_SCOPE_KEY, strlen (OIDC_SCOPE_KEY), &cache_key);
1148   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1149                                                            &cache_key))
1150   {
1151     handle->emsg=GNUNET_strdup("invalid_request");
1152     handle->edesc=GNUNET_strdup("missing parameter scope");
1153     GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1154     return;
1155   }
1156   handle->oidc->scope = GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
1157                                                           &cache_key);
1158   handle->oidc->scope = GNUNET_strdup(handle->oidc->scope);
1159
1160   //OPTIONAL value: nonce
1161   GNUNET_CRYPTO_hash (OIDC_NONCE_KEY, strlen (OIDC_NONCE_KEY), &cache_key);
1162   if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1163                                                             &cache_key))
1164   {
1165     handle->oidc->nonce = GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
1166                                                             &cache_key);
1167     handle->oidc->nonce = GNUNET_strdup (handle->oidc->nonce);
1168   }
1169
1170   //TODO check other values if needed
1171   number_of_ignored_parameter = sizeof(OIDC_ignored_parameter_array) / sizeof(char *);
1172   for( iterator = 0; iterator < number_of_ignored_parameter; iterator++ )
1173   {
1174     GNUNET_CRYPTO_hash (OIDC_ignored_parameter_array[iterator],
1175                         strlen(OIDC_ignored_parameter_array[iterator]),
1176                         &cache_key);
1177     if(GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains(handle->rest_handle->url_param_map,
1178                                                             &cache_key))
1179     {
1180       handle->emsg=GNUNET_strdup("access_denied");
1181       GNUNET_asprintf (&handle->edesc, "Server will not handle parameter: %s",
1182                        OIDC_ignored_parameter_array[iterator]);
1183       GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1184       return;
1185     }
1186   }
1187
1188   // Checks if response_type is 'code'
1189   if( 0 != strcmp( handle->oidc->response_type, OIDC_EXPECTED_AUTHORIZATION_RESPONSE_TYPE ) )
1190   {
1191     handle->emsg=GNUNET_strdup("unsupported_response_type");
1192     handle->edesc=GNUNET_strdup("The authorization server does not support "
1193                                 "obtaining this authorization code.");
1194     GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1195     return;
1196   }
1197
1198   // Checks if scope contains 'openid'
1199   expected_scope = GNUNET_strdup(handle->oidc->scope);
1200   char* test;
1201   test = strtok (expected_scope, delimiter);
1202   while (NULL != test)
1203   {
1204     if ( 0 == strcmp (OIDC_EXPECTED_AUTHORIZATION_SCOPE, expected_scope) )
1205     {
1206       break;
1207     }
1208     test = strtok (NULL, delimiter);
1209   }
1210   if (NULL == test)
1211   {
1212     handle->emsg = GNUNET_strdup("invalid_scope");
1213     handle->edesc=GNUNET_strdup("The requested scope is invalid, unknown, or "
1214                                 "malformed.");
1215     GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1216     GNUNET_free(expected_scope);
1217     return;
1218   }
1219
1220   GNUNET_free(expected_scope);
1221
1222   if( NULL != handle->oidc->login_identity )
1223   {
1224     GNUNET_SCHEDULER_add_now(&login_check,handle);
1225     return;
1226   }
1227
1228   GNUNET_SCHEDULER_add_now(&login_redirection,handle);
1229 }
1230
1231 /**
1232  * Responds to authorization GET and url-encoded POST request
1233  *
1234  * @param con_handle the connection handle
1235  * @param url the url
1236  * @param cls the RequestHandle
1237  */
1238 static void
1239 authorize_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
1240                     const char* url,
1241                     void *cls)
1242 {
1243   struct RequestHandle *handle = cls;
1244   struct GNUNET_HashCode cache_key;
1245   struct EgoEntry *tmp_ego;
1246   const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key;
1247   struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
1248
1249   cookie_identity_interpretation(handle);
1250
1251   //RECOMMENDED value: state - REQUIRED for answers
1252   GNUNET_CRYPTO_hash (OIDC_STATE_KEY, strlen (OIDC_STATE_KEY), &cache_key);
1253   if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1254                                                             &cache_key))
1255   {
1256     handle->oidc->state = GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
1257                                                             &cache_key);
1258     handle->oidc->state = GNUNET_strdup (handle->oidc->state);
1259   }
1260
1261   // REQUIRED value: client_id
1262   GNUNET_CRYPTO_hash (OIDC_CLIENT_ID_KEY, strlen (OIDC_CLIENT_ID_KEY),
1263                       &cache_key);
1264   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1265                                                            &cache_key))
1266   {
1267     handle->emsg=GNUNET_strdup("invalid_request");
1268     handle->edesc=GNUNET_strdup("missing parameter client_id");
1269     handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1270     GNUNET_SCHEDULER_add_now (&do_error, handle);
1271     return;
1272   }
1273   handle->oidc->client_id = GNUNET_strdup (GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
1274                                                                              &cache_key));
1275
1276   if ( GNUNET_OK
1277        != GNUNET_CRYPTO_ecdsa_public_key_from_string (handle->oidc->client_id,
1278                                                       strlen (handle->oidc->client_id),
1279                                                       &handle->oidc->client_pkey) )
1280   {
1281     handle->emsg = GNUNET_strdup("unauthorized_client");
1282     handle->edesc = GNUNET_strdup("The client is not authorized to request an "
1283                                   "authorization code using this method.");
1284     handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1285     GNUNET_SCHEDULER_add_now (&do_error, handle);
1286     return;
1287   }
1288
1289
1290   if ( NULL == handle->ego_head )
1291   {
1292     handle->emsg = GNUNET_strdup("server_error");
1293     handle->edesc = GNUNET_strdup ("Egos are missing");
1294     handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1295     GNUNET_SCHEDULER_add_now (&do_error, handle);
1296     return;
1297   }
1298
1299   handle->ego_entry = handle->ego_head;
1300   handle->priv_key = *GNUNET_IDENTITY_ego_get_private_key (handle->ego_head->ego);
1301   //If we know this identity, translated the corresponding TLD
1302   //TODO: We might want to have a reverse lookup functionality for TLDs?
1303   for (tmp_ego = handle->ego_head; NULL != tmp_ego; tmp_ego = tmp_ego->next)
1304   {
1305     priv_key = GNUNET_IDENTITY_ego_get_private_key (tmp_ego->ego);
1306     GNUNET_CRYPTO_ecdsa_key_get_public (priv_key,
1307                                         &pkey);
1308     if ( 0 == memcmp (&pkey, &handle->oidc->client_pkey,
1309                       sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey)) )
1310     {
1311       handle->tld = GNUNET_strdup (tmp_ego->identifier);
1312       handle->ego_entry = handle->ego_tail;
1313     }
1314   } 
1315   GNUNET_SCHEDULER_add_now (&build_authz_response, handle);
1316 }
1317
1318 /**
1319  * Combines an identity with a login time and responds OK to login request
1320  *
1321  * @param con_handle the connection handle
1322  * @param url the url
1323  * @param cls the RequestHandle
1324  */
1325 static void
1326 login_cont (struct GNUNET_REST_RequestHandle *con_handle,
1327             const char* url,
1328             void *cls)
1329 {
1330   struct MHD_Response *resp = GNUNET_REST_create_response ("");
1331   struct RequestHandle *handle = cls;
1332   struct GNUNET_HashCode cache_key;
1333   struct GNUNET_TIME_Absolute *current_time;
1334   struct GNUNET_TIME_Absolute *last_time;
1335   char* cookie;
1336   json_t *root;
1337   json_error_t error;
1338   json_t *identity;
1339   char term_data[handle->rest_handle->data_size+1];
1340   term_data[handle->rest_handle->data_size] = '\0';
1341   GNUNET_memcpy (term_data, handle->rest_handle->data, handle->rest_handle->data_size);
1342   root = json_loads (term_data, JSON_DECODE_ANY, &error);
1343   identity = json_object_get (root, "identity");
1344   if ( json_is_string(identity) )
1345   {
1346     GNUNET_asprintf (&cookie, "Identity=%s", json_string_value (identity));
1347     MHD_add_response_header (resp, "Set-Cookie", cookie);
1348     MHD_add_response_header (resp, "Access-Control-Allow-Methods", "POST");
1349     GNUNET_CRYPTO_hash (cookie, strlen (cookie), &cache_key);
1350
1351     current_time = GNUNET_new(struct GNUNET_TIME_Absolute);
1352     *current_time = GNUNET_TIME_relative_to_absolute (
1353                                                       GNUNET_TIME_relative_multiply (GNUNET_TIME_relative_get_second_ (),
1354                                                                                      5));
1355     last_time = GNUNET_CONTAINER_multihashmap_get(OIDC_identity_login_time, &cache_key);
1356     if (NULL != last_time)
1357     {
1358       GNUNET_free(last_time);
1359     }
1360     GNUNET_CONTAINER_multihashmap_put (
1361                                        OIDC_identity_login_time, &cache_key, current_time,
1362                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
1363
1364     handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
1365     GNUNET_free(cookie);
1366   }
1367   else
1368   {
1369     handle->proc (handle->proc_cls, resp, MHD_HTTP_BAD_REQUEST);
1370   }
1371   json_decref (root);
1372   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
1373   return;
1374 }
1375
1376 static int 
1377 check_authorization (struct RequestHandle *handle,
1378                      struct GNUNET_CRYPTO_EcdsaPublicKey *cid)
1379 {
1380   struct GNUNET_HashCode cache_key;
1381   char *authorization;
1382   char *credentials;
1383   char *basic_authorization;
1384   char *client_id;
1385   char *pass;
1386   char *expected_pass;
1387   int client_exists = GNUNET_NO;
1388
1389   GNUNET_CRYPTO_hash (OIDC_AUTHORIZATION_HEADER_KEY,
1390                       strlen (OIDC_AUTHORIZATION_HEADER_KEY),
1391                       &cache_key);
1392   if ( GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->header_param_map,
1393                                                             &cache_key) )
1394   {
1395     handle->emsg=GNUNET_strdup("invalid_client");
1396     handle->edesc=GNUNET_strdup("missing authorization");
1397     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1398     return GNUNET_SYSERR;
1399   }
1400   authorization = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->header_param_map,
1401                                                      &cache_key);
1402
1403   //split header in "Basic" and [content]
1404   credentials = strtok (authorization, " ");
1405   if (0 != strcmp ("Basic", credentials))
1406   {
1407     handle->emsg=GNUNET_strdup("invalid_client");
1408     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1409     return GNUNET_SYSERR;
1410   }
1411   credentials = strtok(NULL, " ");
1412   if (NULL == credentials)
1413   {
1414     handle->emsg=GNUNET_strdup("invalid_client");
1415     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1416     return GNUNET_SYSERR;
1417   }
1418   GNUNET_STRINGS_base64_decode (credentials,
1419                                 strlen (credentials),
1420                                 (void**)&basic_authorization);
1421
1422   if ( NULL == basic_authorization )
1423   {
1424     handle->emsg=GNUNET_strdup("invalid_client");
1425     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1426     return GNUNET_SYSERR;
1427   }
1428   client_id = strtok (basic_authorization, ":");
1429   if ( NULL == client_id )
1430   {
1431     GNUNET_free_non_null(basic_authorization);
1432     handle->emsg=GNUNET_strdup("invalid_client");
1433     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1434     return GNUNET_SYSERR;
1435   }
1436   pass = strtok (NULL, ":");
1437   if (NULL == pass)
1438   {
1439     GNUNET_free_non_null(basic_authorization);
1440     handle->emsg=GNUNET_strdup("invalid_client");
1441     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1442     return GNUNET_SYSERR;
1443   }
1444
1445   //check client password
1446   if ( GNUNET_OK
1447        == GNUNET_CONFIGURATION_get_value_string (cfg, "reclaim-rest-plugin",
1448                                                  "psw", &expected_pass) )
1449   {
1450     if (0 != strcmp (expected_pass, pass))
1451     {
1452       GNUNET_free_non_null(basic_authorization);
1453       GNUNET_free(expected_pass);
1454       handle->emsg=GNUNET_strdup("invalid_client");
1455       handle->response_code = MHD_HTTP_UNAUTHORIZED;
1456       return GNUNET_SYSERR;
1457     }
1458     GNUNET_free(expected_pass);
1459   }
1460   else
1461   {
1462     GNUNET_free_non_null(basic_authorization);
1463     handle->emsg = GNUNET_strdup("server_error");
1464     handle->edesc = GNUNET_strdup ("gnunet configuration failed");
1465     handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1466     return GNUNET_SYSERR;
1467   }
1468
1469   //check client_id
1470   for (handle->ego_entry = handle->ego_head; NULL != handle->ego_entry->next; )
1471   {
1472     if ( 0 == strcmp(handle->ego_entry->keystring, client_id))
1473     {
1474       client_exists = GNUNET_YES;
1475       break;
1476     }
1477     handle->ego_entry = handle->ego_entry->next;
1478   }
1479   if (GNUNET_NO == client_exists)
1480   {
1481     GNUNET_free_non_null(basic_authorization);
1482     handle->emsg=GNUNET_strdup("invalid_client");
1483     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1484     return GNUNET_SYSERR;
1485   }
1486   GNUNET_STRINGS_string_to_data (client_id,
1487                                  strlen(client_id),
1488                                  cid,
1489                                  sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
1490
1491   GNUNET_free (basic_authorization);
1492   return GNUNET_OK;
1493 }
1494
1495 static int
1496 ego_exists (struct RequestHandle *handle,
1497             struct GNUNET_CRYPTO_EcdsaPublicKey *test_key)
1498 {
1499   struct EgoEntry *ego_entry;
1500   struct GNUNET_CRYPTO_EcdsaPublicKey pub_key;
1501
1502   for (ego_entry = handle->ego_head; NULL != ego_entry; ego_entry = ego_entry->next)
1503   {
1504     GNUNET_IDENTITY_ego_get_public_key (ego_entry->ego, &pub_key);
1505     if (0 == memcmp (&pub_key,
1506                      test_key,
1507                      sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey)))
1508     {
1509       break;
1510     }
1511   }
1512   if (NULL == ego_entry)
1513     return GNUNET_NO;
1514   return GNUNET_YES;
1515 }
1516
1517 static void
1518 store_ticket_reference (const struct RequestHandle *handle,
1519                         const char* access_token,
1520                         const struct GNUNET_RECLAIM_Ticket *ticket,
1521                         const struct GNUNET_CRYPTO_EcdsaPublicKey *cid)
1522 {
1523   struct GNUNET_HashCode cache_key;
1524   char *id_ticket_combination;
1525   char *ticket_string;
1526   char *client_id;
1527
1528   GNUNET_CRYPTO_hash(access_token, strlen(access_token), &cache_key);
1529   client_id = GNUNET_STRINGS_data_to_string_alloc (cid,
1530                                                    sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
1531   ticket_string = GNUNET_STRINGS_data_to_string_alloc (ticket,
1532                                                        sizeof (struct GNUNET_RECLAIM_Ticket));
1533   GNUNET_asprintf(&id_ticket_combination,
1534                   "%s;%s",
1535                   client_id,
1536                   ticket_string);
1537   GNUNET_CONTAINER_multihashmap_put(OIDC_interpret_access_token,
1538                                     &cache_key,
1539                                     id_ticket_combination,
1540                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
1541
1542   GNUNET_free (client_id);
1543   GNUNET_free (ticket_string);
1544 }
1545
1546 /**
1547  * Responds to token url-encoded POST request
1548  *
1549  * @param con_handle the connection handle
1550  * @param url the url
1551  * @param cls the RequestHandle
1552  */
1553 static void
1554 token_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
1555                 const char* url,
1556                 void *cls)
1557 {
1558   struct RequestHandle *handle = cls;
1559   struct GNUNET_TIME_Relative expiration_time;
1560   struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *cl; 
1561   struct GNUNET_RECLAIM_Ticket *ticket;
1562   struct GNUNET_CRYPTO_EcdsaPublicKey cid;
1563   struct GNUNET_HashCode cache_key;
1564   struct MHD_Response *resp;
1565   char *grant_type;
1566   char *code;
1567   char *json_response;
1568   char *id_token;
1569   char *access_token;
1570   char *jwt_secret;
1571   char *nonce;
1572   int i = 1;
1573
1574   /*
1575    * Check Authorization
1576    */
1577   if (GNUNET_SYSERR == check_authorization (handle,
1578                                             &cid))
1579   {
1580     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1581                 "OIDC authorization for token endpoint failed\n");
1582     GNUNET_SCHEDULER_add_now (&do_error, handle);
1583     return;
1584   }
1585
1586   /*
1587    * Check parameter
1588    */
1589
1590   //TODO Do not allow multiple equal parameter names
1591   //REQUIRED grant_type
1592   GNUNET_CRYPTO_hash (OIDC_GRANT_TYPE_KEY, strlen (OIDC_GRANT_TYPE_KEY), &cache_key);
1593   if (GNUNET_NO ==
1594       GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1595                                               &cache_key))
1596   {
1597     handle->emsg = GNUNET_strdup("invalid_request");
1598     handle->edesc = GNUNET_strdup("missing parameter grant_type");
1599     handle->response_code = MHD_HTTP_BAD_REQUEST;
1600     GNUNET_SCHEDULER_add_now (&do_error, handle);
1601     return;
1602   }
1603   grant_type = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map,
1604                                                   &cache_key);
1605
1606   //REQUIRED code
1607   GNUNET_CRYPTO_hash (OIDC_CODE_KEY, strlen (OIDC_CODE_KEY), &cache_key);
1608   if (GNUNET_NO ==
1609       GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1610                                               &cache_key))
1611   {
1612     handle->emsg = GNUNET_strdup("invalid_request");
1613     handle->edesc = GNUNET_strdup("missing parameter code");
1614     handle->response_code = MHD_HTTP_BAD_REQUEST;
1615     GNUNET_SCHEDULER_add_now (&do_error, handle);
1616     return;
1617   }
1618   code = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map,
1619                                             &cache_key);
1620
1621   //REQUIRED redirect_uri
1622   GNUNET_CRYPTO_hash (OIDC_REDIRECT_URI_KEY, strlen (OIDC_REDIRECT_URI_KEY),
1623                       &cache_key);
1624   if (GNUNET_NO ==
1625       GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1626                                               &cache_key) )
1627   {
1628     handle->emsg = GNUNET_strdup("invalid_request");
1629     handle->edesc = GNUNET_strdup("missing parameter redirect_uri");
1630     handle->response_code = MHD_HTTP_BAD_REQUEST;
1631     GNUNET_SCHEDULER_add_now (&do_error, handle);
1632     return;
1633   }
1634
1635   //Check parameter grant_type == "authorization_code"
1636   if (0 != strcmp(OIDC_GRANT_TYPE_VALUE, grant_type))
1637   {
1638     handle->emsg=GNUNET_strdup("unsupported_grant_type");
1639     handle->response_code = MHD_HTTP_BAD_REQUEST;
1640     GNUNET_SCHEDULER_add_now (&do_error, handle);
1641     return;
1642   }
1643   GNUNET_CRYPTO_hash (code, strlen (code), &cache_key);
1644   if (GNUNET_SYSERR ==
1645       GNUNET_CONTAINER_multihashmap_put (OIDC_ticket_once,
1646                                          &cache_key,
1647                                          &i,
1648                                          GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY) )
1649   {
1650     handle->emsg = GNUNET_strdup("invalid_request");
1651     handle->edesc = GNUNET_strdup("Cannot use the same code more than once");
1652     handle->response_code = MHD_HTTP_BAD_REQUEST;
1653     GNUNET_SCHEDULER_add_now (&do_error, handle);
1654     return;
1655   }
1656
1657   //decode code
1658   if(GNUNET_OK != OIDC_parse_authz_code (&cid,
1659                                          code,
1660                                          &ticket,
1661                                          &nonce))
1662   {
1663     handle->emsg = GNUNET_strdup("invalid_request");
1664     handle->edesc = GNUNET_strdup("invalid code");
1665     handle->response_code = MHD_HTTP_BAD_REQUEST;
1666     GNUNET_SCHEDULER_add_now (&do_error, handle);
1667     return;
1668   }
1669
1670   //create jwt
1671   if (GNUNET_OK !=
1672       GNUNET_CONFIGURATION_get_value_time(cfg,
1673                                           "reclaim-rest-plugin",
1674                                           "expiration_time",
1675                                           &expiration_time))
1676   {
1677     handle->emsg = GNUNET_strdup("server_error");
1678     handle->edesc = GNUNET_strdup ("gnunet configuration failed");
1679     handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1680     GNUNET_SCHEDULER_add_now (&do_error, handle);
1681     GNUNET_free(ticket);
1682     return;
1683   }
1684
1685
1686   //TODO OPTIONAL acr,amr,azp
1687   if (GNUNET_NO == ego_exists (handle,
1688                                &ticket->audience))
1689   {
1690     handle->emsg = GNUNET_strdup("invalid_request");
1691     handle->edesc = GNUNET_strdup("invalid code...");
1692     handle->response_code = MHD_HTTP_BAD_REQUEST;
1693     GNUNET_SCHEDULER_add_now (&do_error, handle);
1694     GNUNET_free(ticket);
1695   }
1696   if ( GNUNET_OK
1697        != GNUNET_CONFIGURATION_get_value_string (cfg, "reclaim-rest-plugin",
1698                                                  "jwt_secret", &jwt_secret) )
1699   {
1700     handle->emsg = GNUNET_strdup("invalid_request");
1701     handle->edesc = GNUNET_strdup("No signing secret configured!");
1702     handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1703     GNUNET_SCHEDULER_add_now (&do_error, handle);
1704     GNUNET_free(ticket);
1705     return;
1706   }
1707   //TODO We should collect the attributes here. cl always empty
1708   cl = GNUNET_new (struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList);
1709   id_token = OIDC_id_token_new (&ticket->audience,
1710                                 &ticket->identity,
1711                                 cl,
1712                                 &expiration_time,
1713                                 (NULL != nonce) ? nonce : NULL,
1714                                 jwt_secret);
1715   access_token = OIDC_access_token_new (); 
1716   OIDC_build_token_response (access_token,
1717                              id_token,
1718                              &expiration_time,
1719                              &json_response);
1720
1721   store_ticket_reference (handle,
1722                           access_token,
1723                           ticket,
1724                           &cid);
1725   resp = GNUNET_REST_create_response (json_response);
1726   MHD_add_response_header (resp, "Cache-Control", "no-store");
1727   MHD_add_response_header (resp, "Pragma", "no-cache");
1728   MHD_add_response_header (resp, "Content-Type", "application/json");
1729   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
1730   GNUNET_RECLAIM_ATTRIBUTE_list_destroy(cl);
1731   GNUNET_free(access_token);
1732   GNUNET_free(json_response);
1733   GNUNET_free(ticket);
1734   GNUNET_free(id_token);
1735   GNUNET_SCHEDULER_add_now(&cleanup_handle_delayed, handle);
1736 }
1737
1738 /**
1739  * Collects claims and stores them in handle
1740  */
1741 static void
1742 consume_ticket (void *cls,
1743                 const struct GNUNET_CRYPTO_EcdsaPublicKey *identity,
1744                 const struct GNUNET_RECLAIM_ATTRIBUTE_Claim *attr)
1745 {
1746   struct RequestHandle *handle = cls;
1747   char *tmp_value;
1748   json_t *value;
1749
1750   if (NULL == identity)
1751   {
1752     GNUNET_SCHEDULER_add_now (&return_userinfo_response, handle);
1753     return;
1754   }
1755
1756   tmp_value = GNUNET_RECLAIM_ATTRIBUTE_value_to_string (attr->type,
1757                                                         attr->data,
1758                                                         attr->data_size);
1759
1760   value = json_string (tmp_value);
1761
1762
1763   json_object_set_new (handle->oidc->response,
1764                        attr->name,
1765                        value);
1766   GNUNET_free (tmp_value);
1767 }
1768
1769 /**
1770  * Responds to userinfo GET and url-encoded POST request
1771  *
1772  * @param con_handle the connection handle
1773  * @param url the url
1774  * @param cls the RequestHandle
1775  */
1776 static void
1777 userinfo_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
1778                    const char* url, void *cls)
1779 {
1780   //TODO expiration time
1781   struct RequestHandle *handle = cls;
1782   char delimiter[] = " ";
1783   char delimiter_db[] = ";";
1784   struct GNUNET_HashCode cache_key;
1785   char *authorization, *authorization_type, *authorization_access_token;
1786   char *client_ticket, *client, *ticket_str;
1787   struct GNUNET_RECLAIM_Ticket *ticket;
1788
1789   GNUNET_CRYPTO_hash (OIDC_AUTHORIZATION_HEADER_KEY,
1790                       strlen (OIDC_AUTHORIZATION_HEADER_KEY),
1791                       &cache_key);
1792   if ( GNUNET_NO
1793        == GNUNET_CONTAINER_multihashmap_contains (
1794                                                   handle->rest_handle->header_param_map, &cache_key) )
1795   {
1796     handle->emsg = GNUNET_strdup("invalid_token");
1797     handle->edesc = GNUNET_strdup("No Access Token");
1798     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1799     GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
1800     return;
1801   }
1802   authorization = GNUNET_CONTAINER_multihashmap_get (
1803                                                      handle->rest_handle->header_param_map, &cache_key);
1804
1805   //split header in "Bearer" and access_token
1806   authorization = GNUNET_strdup(authorization);
1807   authorization_type = strtok (authorization, delimiter);
1808   if ( 0 != strcmp ("Bearer", authorization_type) )
1809   {
1810     handle->emsg = GNUNET_strdup("invalid_token");
1811     handle->edesc = GNUNET_strdup("No Access Token");
1812     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1813     GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
1814     GNUNET_free(authorization);
1815     return;
1816   }
1817   authorization_access_token = strtok (NULL, delimiter);
1818   if ( NULL == authorization_access_token )
1819   {
1820     handle->emsg = GNUNET_strdup("invalid_token");
1821     handle->edesc = GNUNET_strdup("No Access Token");
1822     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1823     GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
1824     GNUNET_free(authorization);
1825     return;
1826   }
1827
1828   GNUNET_CRYPTO_hash (authorization_access_token,
1829                       strlen (authorization_access_token),
1830                       &cache_key);
1831   if ( GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (OIDC_interpret_access_token,
1832                                                             &cache_key) )
1833   {
1834     handle->emsg = GNUNET_strdup("invalid_token");
1835     handle->edesc = GNUNET_strdup("The Access Token expired");
1836     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1837     GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
1838     GNUNET_free(authorization);
1839     return;
1840   }
1841
1842   client_ticket = GNUNET_CONTAINER_multihashmap_get(OIDC_interpret_access_token,
1843                                                     &cache_key);
1844   client_ticket = GNUNET_strdup(client_ticket);
1845   client = strtok(client_ticket,delimiter_db);
1846   if (NULL == client)
1847   {
1848     handle->emsg = GNUNET_strdup("invalid_token");
1849     handle->edesc = GNUNET_strdup("The Access Token expired");
1850     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1851     GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
1852     GNUNET_free(authorization);
1853     GNUNET_free(client_ticket);
1854     return;
1855   }
1856   handle->ego_entry = handle->ego_head;
1857   for(; NULL != handle->ego_entry; handle->ego_entry = handle->ego_entry->next)
1858   {
1859     if (0 == strcmp(handle->ego_entry->keystring,client))
1860     {
1861       break;
1862     }
1863   }
1864   if (NULL == handle->ego_entry)
1865   {
1866     handle->emsg = GNUNET_strdup("invalid_token");
1867     handle->edesc = GNUNET_strdup("The Access Token expired");
1868     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1869     GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
1870     GNUNET_free(authorization);
1871     GNUNET_free(client_ticket);
1872     return;
1873   }
1874   ticket_str = strtok(NULL, delimiter_db);
1875   if (NULL == ticket_str)
1876   {
1877     handle->emsg = GNUNET_strdup("invalid_token");
1878     handle->edesc = GNUNET_strdup("The Access Token expired");
1879     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1880     GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
1881     GNUNET_free(authorization);
1882     GNUNET_free(client_ticket);
1883     return;
1884   }
1885   ticket = GNUNET_new(struct GNUNET_RECLAIM_Ticket);
1886   if ( GNUNET_OK
1887        != GNUNET_STRINGS_string_to_data (ticket_str,
1888                                          strlen (ticket_str),
1889                                          ticket,
1890                                          sizeof(struct GNUNET_RECLAIM_Ticket)))
1891   {
1892     handle->emsg = GNUNET_strdup("invalid_token");
1893     handle->edesc = GNUNET_strdup("The Access Token expired");
1894     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1895     GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
1896     GNUNET_free(ticket);
1897     GNUNET_free(authorization);
1898     GNUNET_free(client_ticket);
1899     return;
1900   }
1901
1902   handle->idp = GNUNET_RECLAIM_connect (cfg);
1903   handle->oidc->response = json_object();
1904   json_object_set_new( handle->oidc->response, "sub", json_string( handle->ego_entry->keystring));
1905   handle->idp_op = GNUNET_RECLAIM_ticket_consume (
1906                                                   handle->idp,
1907                                                   GNUNET_IDENTITY_ego_get_private_key (handle->ego_entry->ego),
1908                                                   ticket,
1909                                                   consume_ticket,
1910                                                   handle);
1911   GNUNET_free(ticket);
1912   GNUNET_free(authorization);
1913   GNUNET_free(client_ticket);
1914
1915 }
1916
1917
1918 /**
1919  * Handle rest request
1920  *
1921  * @param handle the request handle
1922  */
1923 static void
1924 init_cont (struct RequestHandle *handle)
1925 {
1926   struct GNUNET_REST_RequestHandlerError err;
1927   static const struct GNUNET_REST_RequestHandler handlers[] = {
1928     {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_AUTHORIZE, &authorize_endpoint},
1929     {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_AUTHORIZE, &authorize_endpoint}, //url-encoded
1930     {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_LOGIN, &login_cont},
1931     {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_TOKEN, &token_endpoint },
1932     {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_USERINFO, &userinfo_endpoint },
1933     {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_USERINFO, &userinfo_endpoint },
1934     {MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_OIDC,
1935       &options_cont},
1936     GNUNET_REST_HANDLER_END
1937   };
1938
1939   if (GNUNET_NO == GNUNET_REST_handle_request (handle->rest_handle,
1940                                                handlers,
1941                                                &err,
1942                                                handle))
1943   {
1944     handle->response_code = err.error_code;
1945     GNUNET_SCHEDULER_add_now (&do_error, handle);
1946   }
1947 }
1948
1949 /**
1950  * If listing is enabled, prints information about the egos.
1951  *
1952  * This function is initially called for all egos and then again
1953  * whenever a ego's identifier changes or if it is deleted.  At the
1954  * end of the initial pass over all egos, the function is once called
1955  * with 'NULL' for 'ego'. That does NOT mean that the callback won't
1956  * be invoked in the future or that there was an error.
1957  *
1958  * When used with 'GNUNET_IDENTITY_create' or 'GNUNET_IDENTITY_get',
1959  * this function is only called ONCE, and 'NULL' being passed in
1960  * 'ego' does indicate an error (i.e. name is taken or no default
1961  * value is known).  If 'ego' is non-NULL and if '*ctx'
1962  * is set in those callbacks, the value WILL be passed to a subsequent
1963  * call to the identity callback of 'GNUNET_IDENTITY_connect' (if
1964  * that one was not NULL).
1965  *
1966  * When an identity is renamed, this function is called with the
1967  * (known) ego but the NEW identifier.
1968  *
1969  * When an identity is deleted, this function is called with the
1970  * (known) ego and "NULL" for the 'identifier'.  In this case,
1971  * the 'ego' is henceforth invalid (and the 'ctx' should also be
1972  * cleaned up).
1973  *
1974  * @param cls closure
1975  * @param ego ego handle
1976  * @param ctx context for application to store data for this ego
1977  *                 (during the lifetime of this process, initially NULL)
1978  * @param identifier identifier assigned by the user for this ego,
1979  *                   NULL if the user just deleted the ego and it
1980  *                   must thus no longer be used
1981  */
1982 static void
1983 list_ego (void *cls,
1984           struct GNUNET_IDENTITY_Ego *ego,
1985           void **ctx,
1986           const char *identifier)
1987 {
1988   struct RequestHandle *handle = cls;
1989   struct EgoEntry *ego_entry;
1990   struct GNUNET_CRYPTO_EcdsaPublicKey pk;
1991
1992   if ((NULL == ego) && (ID_REST_STATE_INIT == handle->state))
1993   {
1994     handle->state = ID_REST_STATE_POST_INIT;
1995     init_cont (handle);
1996     return;
1997   }
1998   if (ID_REST_STATE_INIT == handle->state) {
1999     ego_entry = GNUNET_new (struct EgoEntry);
2000     GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
2001     ego_entry->keystring =
2002       GNUNET_CRYPTO_ecdsa_public_key_to_string (&pk);
2003     ego_entry->ego = ego;
2004     ego_entry->identifier = GNUNET_strdup (identifier);
2005     GNUNET_CONTAINER_DLL_insert_tail(handle->ego_head,handle->ego_tail, ego_entry);
2006     return;
2007   }
2008   /* Ego renamed or added */
2009   if (identifier != NULL) {
2010     for (ego_entry = handle->ego_head; NULL != ego_entry; ego_entry = ego_entry->next) {
2011       if (ego_entry->ego == ego) {
2012         /* Rename */
2013         GNUNET_free (ego_entry->identifier);
2014         ego_entry->identifier = GNUNET_strdup (identifier);
2015         break;
2016       }
2017     }
2018     if (NULL == ego_entry) {
2019       /* Add */
2020       ego_entry = GNUNET_new (struct EgoEntry);
2021       GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
2022       ego_entry->keystring =
2023         GNUNET_CRYPTO_ecdsa_public_key_to_string (&pk);
2024       ego_entry->ego = ego;
2025       ego_entry->identifier = GNUNET_strdup (identifier);
2026       GNUNET_CONTAINER_DLL_insert_tail(handle->ego_head,handle->ego_tail, ego_entry);
2027     }
2028   } else {
2029     /* Delete */
2030     for (ego_entry = handle->ego_head; NULL != ego_entry; ego_entry = ego_entry->next) {
2031       if (ego_entry->ego == ego)
2032         break;
2033     }
2034     if (NULL != ego_entry)
2035       GNUNET_CONTAINER_DLL_remove(handle->ego_head,handle->ego_tail, ego_entry);
2036   }
2037
2038 }
2039
2040 static void
2041 rest_identity_process_request(struct GNUNET_REST_RequestHandle *rest_handle,
2042                               GNUNET_REST_ResultProcessor proc,
2043                               void *proc_cls)
2044 {
2045   struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
2046   handle->oidc = GNUNET_new (struct OIDC_Variables);
2047   if ( NULL == OIDC_identity_login_time )
2048     OIDC_identity_login_time = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
2049   if ( NULL == OIDC_identity_grants )
2050     OIDC_identity_grants = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
2051   if ( NULL == OIDC_ticket_once )
2052     OIDC_ticket_once = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
2053   if ( NULL == OIDC_interpret_access_token )
2054     OIDC_interpret_access_token = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
2055   handle->response_code = 0;
2056   handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
2057   handle->proc_cls = proc_cls;
2058   handle->proc = proc;
2059   handle->state = ID_REST_STATE_INIT;
2060   handle->rest_handle = rest_handle;
2061
2062   handle->url = GNUNET_strdup (rest_handle->url);
2063   if (handle->url[strlen (handle->url)-1] == '/')
2064     handle->url[strlen (handle->url)-1] = '\0';
2065   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2066               "Connecting...\n");
2067   handle->identity_handle = GNUNET_IDENTITY_connect (cfg,
2068                                                      &list_ego,
2069                                                      handle);
2070   handle->gns_handle = GNUNET_GNS_connect (cfg);
2071   handle->namestore_handle = GNUNET_NAMESTORE_connect (cfg);
2072   handle->timeout_task =
2073     GNUNET_SCHEDULER_add_delayed (handle->timeout,
2074                                   &do_timeout,
2075                                   handle);
2076   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2077               "Connected\n");
2078 }
2079
2080 /**
2081  * Entry point for the plugin.
2082  *
2083  * @param cls Config info
2084  * @return NULL on error, otherwise the plugin context
2085  */
2086 void *
2087 libgnunet_plugin_rest_openid_connect_init (void *cls)
2088 {
2089   static struct Plugin plugin;
2090   struct GNUNET_REST_Plugin *api;
2091
2092   cfg = cls;
2093   if (NULL != plugin.cfg)
2094     return NULL;                /* can only initialize once! */
2095   memset (&plugin, 0, sizeof (struct Plugin));
2096   plugin.cfg = cfg;
2097   api = GNUNET_new (struct GNUNET_REST_Plugin);
2098   api->cls = &plugin;
2099   api->name = GNUNET_REST_API_NS_OIDC;
2100   api->process_request = &rest_identity_process_request;
2101   GNUNET_asprintf (&allow_methods,
2102                    "%s, %s, %s, %s, %s",
2103                    MHD_HTTP_METHOD_GET,
2104                    MHD_HTTP_METHOD_POST,
2105                    MHD_HTTP_METHOD_PUT,
2106                    MHD_HTTP_METHOD_DELETE,
2107                    MHD_HTTP_METHOD_OPTIONS);
2108
2109   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2110               _("Identity Provider REST API initialized\n"));
2111   return api;
2112 }
2113
2114
2115 /**
2116  * Exit point from the plugin.
2117  *
2118  * @param cls the plugin context (as returned by "init")
2119  * @return always NULL
2120  */
2121 void *
2122 libgnunet_plugin_rest_openid_connect_done (void *cls)
2123 {
2124   struct GNUNET_REST_Plugin *api = cls;
2125   struct Plugin *plugin = api->cls;
2126   plugin->cfg = NULL;
2127
2128   struct GNUNET_CONTAINER_MultiHashMapIterator *hashmap_it;
2129   void *value = NULL;
2130   hashmap_it = GNUNET_CONTAINER_multihashmap_iterator_create (
2131                                                               OIDC_identity_login_time);
2132   while (GNUNET_YES ==
2133          GNUNET_CONTAINER_multihashmap_iterator_next (hashmap_it, NULL, value))
2134   {
2135     if (NULL != value)
2136       GNUNET_free(value);
2137   }
2138   GNUNET_CONTAINER_multihashmap_destroy(OIDC_identity_login_time);
2139   hashmap_it = GNUNET_CONTAINER_multihashmap_iterator_create (OIDC_identity_grants);
2140   while (GNUNET_YES ==
2141          GNUNET_CONTAINER_multihashmap_iterator_next (hashmap_it, NULL, value))
2142   {
2143     if (NULL != value)
2144       GNUNET_free(value);
2145   }
2146   GNUNET_CONTAINER_multihashmap_destroy(OIDC_identity_grants);
2147   hashmap_it = GNUNET_CONTAINER_multihashmap_iterator_create (OIDC_ticket_once);
2148   while (GNUNET_YES ==
2149          GNUNET_CONTAINER_multihashmap_iterator_next (hashmap_it, NULL, value))
2150   {
2151     if (NULL != value)
2152       GNUNET_free(value);
2153   }
2154   GNUNET_CONTAINER_multihashmap_destroy(OIDC_ticket_once);
2155   hashmap_it = GNUNET_CONTAINER_multihashmap_iterator_create (OIDC_interpret_access_token);
2156   while (GNUNET_YES ==
2157          GNUNET_CONTAINER_multihashmap_iterator_next (hashmap_it, NULL, value))
2158   {
2159     if (NULL != value)
2160       GNUNET_free(value);
2161   }
2162   GNUNET_CONTAINER_multihashmap_destroy(OIDC_interpret_access_token);
2163   GNUNET_CONTAINER_multihashmap_iterator_destroy(hashmap_it);
2164   GNUNET_free_non_null (allow_methods);
2165   GNUNET_free (api);
2166   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2167               "Identity Provider REST plugin is finished\n");
2168   return NULL;
2169 }
2170
2171 /* end of plugin_rest_identity_provider.c */